mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-27 21:06:46 +02:00
Compare commits
899 Commits
Author | SHA1 | Date | |
---|---|---|---|
a5246bcbc6 | |||
b37e28c2d0 | |||
973e60ef11 | |||
ae94bc593b | |||
81eb58532e | |||
8464ea9a7a | |||
b77bff35a1 | |||
eb964b64c2 | |||
2ff9d29491 | |||
2640919318 | |||
2fcbc4fa1c | |||
4330bf5867 | |||
3f1b7cdd26 | |||
1e985c1456 | |||
61740bda85 | |||
0d8e6d713e | |||
39e4ff8bd6 | |||
0434ac186a | |||
8d9d51640c | |||
c4c0c0d1f4 | |||
3f2826bd66 | |||
7b7ea0e386 | |||
d35a38735b | |||
f0c4906fdc | |||
56ba61e061 | |||
898d89e95a | |||
1fd1e27bdf | |||
8e84ac58d4 | |||
9d65997871 | |||
da2889f3ab | |||
312b38d253 | |||
0554bf0240 | |||
53a7530e8a | |||
41be04a142 | |||
31f36318a2 | |||
f882fdf608 | |||
7e1e410307 | |||
c135c812c2 | |||
6c239193ab | |||
de7db08352 | |||
ba5ded9bc2 | |||
4581dee4fc | |||
d75d6e8b60 | |||
142ce642f0 | |||
7dead5ec96 | |||
ddcf224db2 | |||
daf794c4a2 | |||
dac0124f0f | |||
15b7a30925 | |||
77ab71f26f | |||
68e1ba312d | |||
752fc6d619 | |||
16bcb27ca7 | |||
71d7d355a6 | |||
4188b392cc | |||
811fbfbb1e | |||
32eb7d3040 | |||
42763905d7 | |||
f64300d1d6 | |||
1a723f0e8e | |||
a86884c40e | |||
3f02e119f4 | |||
1bbd50aac7 | |||
e7a0c32a68 | |||
7560bc9547 | |||
2b43340c90 | |||
ff1620c923 | |||
1303053a27 | |||
91f194c821 | |||
478762f76c | |||
4d96d6a753 | |||
3cdcfbf4e5 | |||
4c302da503 | |||
26b5fe8f9d | |||
22d60232fb | |||
e428879beb | |||
3015138a5a | |||
9c06ad608b | |||
015dbd9a96 | |||
a7466c3142 | |||
f012007cc4 | |||
dcf3baf60f | |||
4bda799a4b | |||
bdeae33d48 | |||
47591d5298 | |||
b5ace27a3e | |||
b334622d36 | |||
4bfb344caa | |||
16d96d6faf | |||
0ab2bad226 | |||
68db926f9f | |||
b270c1e8b7 | |||
e7142e49fd | |||
610906d07f | |||
6f72a191ba | |||
dd5fa236ae | |||
51e1ab94b3 | |||
ddc8c53abf | |||
46513017e3 | |||
98cb7f58c0 | |||
206bf407fd | |||
0a8ac4de3b | |||
87d54acad0 | |||
45ff6d8bac | |||
437dd8baeb | |||
b080236ee5 | |||
116bff8cef | |||
ddeca171a0 | |||
127f9462e0 | |||
4b5a9abbd4 | |||
bb2257973e | |||
12b9cd2386 | |||
2cbe8fa880 | |||
2e39bee05a | |||
05eda76643 | |||
f7b73af9a7 | |||
73aaa44d12 | |||
e4abd8b36b | |||
9e3d4f3889 | |||
b7d67c1d86 | |||
c4a11f8dc7 | |||
d18cc8cbc2 | |||
ce6d4861fb | |||
8f0e6026f0 | |||
258736995d | |||
56e03a3f8b | |||
b6c746b5f5 | |||
03673cbced | |||
0fea930c24 | |||
94d22a8853 | |||
63cc23401a | |||
f90a774470 | |||
e7c5fbc873 | |||
d62902ffb3 | |||
bebff3be0e | |||
2be4847b53 | |||
4557614443 | |||
5b30ecf2e4 | |||
2413ad2b59 | |||
52855b805a | |||
20b5e46850 | |||
f2b796940e | |||
a37bf300f9 | |||
cb524dcbc3 | |||
4144b868ce | |||
af11f16bef | |||
1d27a496ef | |||
32bd9dc1a7 | |||
3cd2bc7e3c | |||
43ca840658 | |||
685837357b | |||
812a13b0fb | |||
641fa471d2 | |||
8d5755f2d8 | |||
af510b6666 | |||
46f646afcf | |||
68ce17a57f | |||
a69fad15c1 | |||
7621fe9a62 | |||
f6d9bc3a74 | |||
29aea0b42c | |||
815c29ee31 | |||
3917f664bf | |||
a223329815 | |||
3fec11d5f9 | |||
221edb0012 | |||
2710ab6034 | |||
bc3ffac0ba | |||
29f55c5277 | |||
a290f0f7f8 | |||
5cd196a769 | |||
b14d983a8c | |||
51f1daeec8 | |||
b799c6a8d7 | |||
b9f9546e2b | |||
846bc4613e | |||
a5a1f6015d | |||
fe6a7074ea | |||
db7c9fe426 | |||
b1d8ab102f | |||
0bd5b4ddee | |||
c387df35f8 | |||
351a50c214 | |||
93175314b5 | |||
b905a1a3c5 | |||
c4386b195f | |||
ed1e617380 | |||
1382adb1f6 | |||
ecf1c317b7 | |||
cf0b99f1e5 | |||
3605a296a9 | |||
9375d220f9 | |||
8f94b0a218 | |||
85a0becbf9 | |||
fec36ad9e6 | |||
ecdf6f01d2 | |||
e1bfa30769 | |||
ebcbc7497b | |||
e844ece28b | |||
8508451ba6 | |||
5cc83a7cb5 | |||
febc725763 | |||
f2c116feb3 | |||
52dd60dc86 | |||
80c0500299 | |||
ab490d28b4 | |||
a9f7f85617 | |||
1bfc1ba371 | |||
2ede39369a | |||
922c184195 | |||
56a9571509 | |||
0136f22076 | |||
c4ace288af | |||
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
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/.github/ @TokTok/admins
|
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
github: [JFreegman]
|
17
.github/settings.yml
vendored
Normal file
17
.github/settings.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
_extends: .github
|
||||||
|
|
||||||
|
repository:
|
||||||
|
name: toxic
|
||||||
|
description: An ncurses-based Tox client
|
||||||
|
topics: tox, console, chat
|
||||||
|
|
||||||
|
branches:
|
||||||
|
- name: "master"
|
||||||
|
protection:
|
||||||
|
required_status_checks:
|
||||||
|
contexts:
|
||||||
|
- Codacy/PR Quality Review
|
||||||
|
- CodeFactor
|
||||||
|
- Travis CI - Pull Request
|
||||||
|
- code-review/reviewable
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,3 +16,4 @@ stamp-h1
|
|||||||
build/toxic
|
build/toxic
|
||||||
build/*.o
|
build/*.o
|
||||||
build/*.d
|
build/*.d
|
||||||
|
apidoc/python/build
|
||||||
|
4
.restyled.yaml
Normal file
4
.restyled.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
restylers:
|
||||||
|
- astyle:
|
||||||
|
arguments: ["--options=astylerc"]
|
109
.travis.yml
109
.travis.yml
@ -1,53 +1,60 @@
|
|||||||
language: c
|
---
|
||||||
compiler:
|
language: python
|
||||||
- gcc
|
python: nightly
|
||||||
- clang
|
dist: xenial
|
||||||
|
os: linux
|
||||||
|
|
||||||
before_script:
|
jobs:
|
||||||
# Installing yasm (needed for compiling vpx) and openal
|
include:
|
||||||
- sudo apt-get -yq install yasm libopenal-dev
|
- env: JOB=linux
|
||||||
# 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:
|
addons:
|
||||||
channels:
|
apt:
|
||||||
- "chat.freenode.net#tox-dev"
|
packages:
|
||||||
on_success: always
|
- libalut-dev
|
||||||
on_failure: always
|
- libconfig-dev
|
||||||
|
- libnotify-dev
|
||||||
|
- libopenal-dev
|
||||||
|
- libopus-dev
|
||||||
|
- libqrencode-dev
|
||||||
|
- libvpx-dev
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/cache
|
||||||
|
|
||||||
|
install:
|
||||||
|
# Where to find libraries.
|
||||||
|
- export LD_LIBRARY_PATH=$HOME/cache/usr/lib
|
||||||
|
- export PKG_CONFIG_PATH=$HOME/cache/usr/lib/pkgconfig
|
||||||
|
# c-sodium
|
||||||
|
- git clone --depth=1 --branch=stable https://github.com/jedisct1/libsodium ../libsodium
|
||||||
|
- test -f $HOME/cache/usr/lib/libsodium.so || (cd ../libsodium && ./configure --prefix=$HOME/cache/usr && make install -j$(nproc))
|
||||||
|
# c-toxcore
|
||||||
|
- git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
|
||||||
|
- test -f $HOME/cache/usr/lib/libtoxcore.so || (cd ../c-toxcore && cmake -B_build -H. -DCMAKE_INSTALL_PREFIX:PATH=$HOME/cache/usr && make -C_build install -j$(nproc))
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make ENABLE_PYTHON=1 -j$(nproc)
|
||||||
|
|
||||||
|
- env: JOB=macos
|
||||||
|
os: macos
|
||||||
|
language: c
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/cache
|
||||||
|
|
||||||
|
install:
|
||||||
|
- brew install
|
||||||
|
freealut
|
||||||
|
libconfig
|
||||||
|
libqrencode
|
||||||
|
libsodium
|
||||||
|
openal-soft
|
||||||
|
- export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.21.0/lib"
|
||||||
|
- git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
|
||||||
|
- test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc))
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make ENABLE_PYTHON=1 DISABLE_DESKTOP_NOTIFY=1 DISABLE_X11=1 DISABLE_AV=1 DISABLE_SOUND_NOTIFY=1 -j$(nproc)
|
||||||
|
41
BUILD.bazel
Normal file
41
BUILD.bazel
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
load("@rules_cc//cc:defs.bzl", "cc_binary")
|
||||||
|
load("//tools/project:build_defs.bzl", "project")
|
||||||
|
|
||||||
|
project()
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "toxic",
|
||||||
|
srcs = glob(
|
||||||
|
[
|
||||||
|
"src/*.c",
|
||||||
|
"src/*.h",
|
||||||
|
],
|
||||||
|
exclude = ["src/video*"],
|
||||||
|
) + select({
|
||||||
|
"//tools/config:linux": glob(["src/video*"]),
|
||||||
|
"//tools/config:osx": [],
|
||||||
|
}),
|
||||||
|
copts = [
|
||||||
|
"-std=gnu99",
|
||||||
|
"-DAUDIO",
|
||||||
|
"-DPACKAGE_DATADIR='\"data\"'",
|
||||||
|
"-DPYTHON",
|
||||||
|
"-DQRCODE",
|
||||||
|
] + select({
|
||||||
|
"//tools/config:linux": ["-DVIDEO"],
|
||||||
|
"//tools/config:osx": [],
|
||||||
|
}),
|
||||||
|
deps = [
|
||||||
|
"//c-toxcore",
|
||||||
|
"@curl",
|
||||||
|
"@libconfig",
|
||||||
|
"@libqrencode",
|
||||||
|
"@libvpx",
|
||||||
|
"@ncurses",
|
||||||
|
"@openal",
|
||||||
|
"@python3//:python",
|
||||||
|
] + select({
|
||||||
|
"//tools/config:linux": ["@x11"],
|
||||||
|
"//tools/config:osx": [],
|
||||||
|
}),
|
||||||
|
)
|
503
CHANGELOG.md
Normal file
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)*
|
70
INSTALL.md
Normal file
70
INSTALL.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Installation
|
||||||
|
* [Dependencies](#dependencies)
|
||||||
|
* [OS X Notes](#os-x-notes)
|
||||||
|
* [Compiling](#compiling)
|
||||||
|
* [Documentation](#documentation)
|
||||||
|
* [Notes](#notes)
|
||||||
|
* [Compilation variables](#compilation-variables)
|
||||||
|
* [Environment variables](#environment-variables)
|
||||||
|
|
||||||
|
## 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/) | QRCODE | libqrencode-dev |
|
||||||
|
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
|
||||||
|
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
|
||||||
|
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
|
||||||
|
| [Python 3](http://www.python.org/) | PYTHON | python3-dev |
|
||||||
|
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
|
||||||
|
|
||||||
|
<sup>1</sup>: see [Documentation](#documentation)
|
||||||
|
|
||||||
|
#### OS X Notes
|
||||||
|
Using [Homebrew](http://brew.sh):
|
||||||
|
```
|
||||||
|
brew install curl qrencode openal-soft freealut libconfig libpng
|
||||||
|
brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
|
||||||
|
brew install libnotify
|
||||||
|
export PKG_CONFIG_PATH=/usr/local/opt/openal-soft/lib/pkgconfig
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
You can omit `libnotify` if you intend to build without desktop notifications enabled.
|
||||||
|
|
||||||
|
## Compiling
|
||||||
|
```
|
||||||
|
make
|
||||||
|
sudo make 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 committed together.<br />
|
||||||
|
**Note for everyone**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
#### Compilation variables
|
||||||
|
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables
|
||||||
|
* Default compile options can be overridden by using special variables:
|
||||||
|
* `DISABLE_X11=1` → Disable X11 support (needed for focus tracking)
|
||||||
|
* `DISABLE_AV=1` → Disable audio call support
|
||||||
|
* `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support
|
||||||
|
* `DISABLE_QRCODE` → Disable QR exporting support
|
||||||
|
* `DISABLE_QRPNG` → Disable support for exporting QR as PNG
|
||||||
|
* `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
|
||||||
|
* `ENABLE_PYTHON=1` → Build toxic with Python scripting support
|
||||||
|
* `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations
|
||||||
|
* `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled
|
||||||
|
|
||||||
|
* `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
|
||||||
|
|
||||||
|
#### Environment variables
|
||||||
|
* You can use the `CFLAGS` and `LDFLAGS` environment variables to add specific flags to the Makefile
|
||||||
|
* The `PREFIX` environment variable specifies a base install directory for binaries and data files. This is interchangeable with the `DESTDIR` variable, and is generally used by systems that have the `PREFIX` environment variable set by default.<br />
|
||||||
|
**Note**: `sudo` does not preserve user environment variables by default on some systems. See the `sudoers` manual for more information.
|
97
Makefile
Normal file
97
Makefile
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
BASE_DIR = $(shell pwd -P)
|
||||||
|
CFG_DIR = $(BASE_DIR)/cfg
|
||||||
|
|
||||||
|
-include $(CFG_DIR)/global_vars.mk
|
||||||
|
|
||||||
|
LIBS = toxcore ncursesw libconfig libcurl
|
||||||
|
|
||||||
|
CFLAGS ?= -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wno-missing-braces
|
||||||
|
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
||||||
|
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
||||||
|
CFLAGS += ${USER_CFLAGS}
|
||||||
|
LDFLAGS ?=
|
||||||
|
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 conference_commands.o conference.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 if debug build is enabled
|
||||||
|
RELEASE := $(shell if [ -z "$(ENABLE_RELEASE)" ] || [ "$(ENABLE_RELEASE)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||||
|
ifneq ($(RELEASE), enabled)
|
||||||
|
CFLAGS += -O0 -g -DDEBUG
|
||||||
|
LDFLAGS += -O0
|
||||||
|
else
|
||||||
|
CFLAGS += -O2 -flto
|
||||||
|
LDFLAGS += -O2 -flto
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if LLVM Address Sanitizer is enabled
|
||||||
|
ASAN := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||||
|
ifneq ($(ASAN), disabled)
|
||||||
|
CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check on wich system we are running
|
||||||
|
UNAME_S = $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S), Linux)
|
||||||
|
LDFLAGS += -ldl -lrt
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), OpenBSD)
|
||||||
|
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||||
|
LDFLAGS += -lncursesw
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), NetBSD)
|
||||||
|
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||||
|
LDFLAGS += -lncursesw
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), Darwin)
|
||||||
|
-include $(CFG_DIR)/systems/Darwin.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# 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
|
45
README.md
45
README.md
@ -1,37 +1,20 @@
|
|||||||
# 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 messaging and video chat client.
|
||||||
|
|
||||||
|
[](https://i.imgur.com/TwYA8L0.png)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
[See the install instructions](/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 +25,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.10.1'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.10.1'
|
||||||
|
|
||||||
|
# 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
|
26
astylerc
Normal file
26
astylerc
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Bracket Style Options
|
||||||
|
--style=kr
|
||||||
|
|
||||||
|
# Tab Options
|
||||||
|
--indent=spaces=4
|
||||||
|
|
||||||
|
# Indentation Options
|
||||||
|
--indent-switches
|
||||||
|
|
||||||
|
# Padding Options
|
||||||
|
--pad-header
|
||||||
|
--break-blocks
|
||||||
|
--pad-oper
|
||||||
|
--unpad-paren
|
||||||
|
--align-pointer=name
|
||||||
|
--align-reference=name
|
||||||
|
|
||||||
|
# Formatting Options
|
||||||
|
--add-brackets
|
||||||
|
--convert-tabs
|
||||||
|
--max-code-length=120
|
||||||
|
|
||||||
|
# Other Options
|
||||||
|
--preserve-date
|
||||||
|
--formatted
|
||||||
|
--lineend=linux
|
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 freebsd systems
|
|
||||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
|
||||||
LDFLAGS += -lncursesw
|
|
@ -1,3 +0,0 @@
|
|||||||
# Specials options for linux systems
|
|
||||||
CFLAGS +=
|
|
||||||
LDFLAGS += -ldl -lresolv
|
|
22
cfg/checks/audio.mk
Normal file
22
cfg/checks/audio.mk
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Variables for audio call support
|
||||||
|
AUDIO_LIBS = 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)
|
||||||
|
LDFLAGS += -lm
|
||||||
|
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
|
66
cfg/checks/check_features.mk
Normal file
66
cfg/checks/check_features.mk
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
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_VI)" ] || [ "$(DISABLE_VI)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
ifneq ($(X11), disabled)
|
||||||
|
ifneq ($(AUDIO), disabled)
|
||||||
|
ifneq ($(VIDEO), disabled)
|
||||||
|
-include $(CHECKS_DIR)/video.mk
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we want build sound notifications support
|
||||||
|
SND_NOTIFY := $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
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 export support
|
||||||
|
QR_CODE := $(shell if [ -z "$(DISABLE_QRCODE)" ] || [ "$(DISABLE_QRCODE)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
ifneq ($(QR_CODE), disabled)
|
||||||
|
-include $(CHECKS_DIR)/qr.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we want build QR exported as PNG support
|
||||||
|
QR_PNG := $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
ifneq ($(QR_PNG), disabled)
|
||||||
|
-include $(CHECKS_DIR)/qr_png.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we want build Python scripting support
|
||||||
|
PYTHON := $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||||
|
ifneq ($(PYTHON), disabled)
|
||||||
|
-include $(CHECKS_DIR)/python.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we can build Toxic
|
||||||
|
CHECK_LIBS := $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
|
||||||
|
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.mk
Normal file
15
cfg/checks/qr.mk
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Variables for QR export support
|
||||||
|
QR_LIBS = libqrencode
|
||||||
|
QR_CFLAGS = -DQRCODE
|
||||||
|
|
||||||
|
# Check if we can build QR export support
|
||||||
|
CHECK_QR_LIBS = $(shell pkg-config --exists $(QR_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_QR_LIBS), error)
|
||||||
|
LIBS += $(QR_LIBS)
|
||||||
|
CFLAGS += $(QR_CFLAGS)
|
||||||
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_QR_LIBS = $(shell for lib in $(QR_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without QR export support)
|
||||||
|
$(warning WARNING -- You need these libraries for QR export support)
|
||||||
|
$(warning WARNING -- $(MISSING_QR_LIBS))
|
||||||
|
endif
|
15
cfg/checks/qr_png.mk
Normal file
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 = openal vpx x11
|
||||||
|
VIDEO_CFLAGS = -DVIDEO
|
||||||
|
ifneq (, $(findstring video_device.o, $(OBJ)))
|
||||||
|
VIDEO_OBJ = video_call.o
|
||||||
|
else
|
||||||
|
VIDEO_OBJ = video_call.o video_device.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we can build video support
|
||||||
|
CHECK_VIDEO_LIBS = $(shell $(PKG_CONFIG) --exists $(VIDEO_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_VIDEO_LIBS), error)
|
||||||
|
LIBS += $(VIDEO_LIBS)
|
||||||
|
CFLAGS += $(VIDEO_CFLAGS)
|
||||||
|
OBJ += $(VIDEO_OBJ)
|
||||||
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without video support)
|
||||||
|
$(warning WARNING -- You will need these libraries for video support)
|
||||||
|
$(warning WARNING -- $(MISSING_VIDEO_LIBS))
|
||||||
|
endif
|
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 = x11focus.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.10.1
|
||||||
|
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||||
|
ifneq (, $(findstring error, $(REV)))
|
||||||
|
VERSION = $(TOXIC_VERSION)
|
||||||
|
else
|
||||||
|
VERSION = $(TOXIC_VERSION)_r$(REV)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Project directories
|
||||||
|
BUILD_DIR = $(BASE_DIR)/build
|
||||||
|
DOC_DIR = $(BASE_DIR)/doc
|
||||||
|
SRC_DIR = $(BASE_DIR)/src
|
||||||
|
SND_DIR = $(BASE_DIR)/sounds
|
||||||
|
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 -ltoxcore -lcurl -lconfig -lqrencode -lpng -lopenal -g
|
||||||
|
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
|
||||||
|
|
||||||
|
OSX_LIBRARIES = -lobjc -lresolv
|
||||||
|
OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \
|
||||||
|
-framework QuartzCore -framework CoreMedia
|
||||||
|
OSX_VIDEO = osx_video.m
|
||||||
|
|
||||||
|
LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
|
||||||
|
OBJ += osx_video.o
|
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
|
37
cfg/targets/help.mk
Normal file
37
cfg/targets/help.mk
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# 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_QRCODE: Set to \"1\" to force building without QR export support"
|
||||||
|
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
|
||||||
|
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
|
||||||
|
@echo " ENABLE_RELEASE: Set to \"1\" to build without debug symbols and with full compiler optimizations"
|
||||||
|
@echo " ENABLE_ASAN: Set to \"1\" to build with LLVM address sanitizer enabled.
|
||||||
|
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||||
|
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||||
|
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||||
|
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
|
||||||
|
@echo " MANDIR: Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
|
||||||
|
@echo
|
||||||
|
@echo "-- Environment Variables --"
|
||||||
|
@echo " CFLAGS: Add custom flags to default CFLAGS"
|
||||||
|
@echo " LDFLAGS: Add custom flags to default LDFLAGS"
|
||||||
|
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||||
|
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||||
|
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||||
|
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
|
||||||
|
@echo " MANDIR: Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
|
||||||
|
|
||||||
|
.PHONY: help
|
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##*.}` ;\
|
||||||
|
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 -n -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##*.}` ;\
|
||||||
|
file=$$section/$$f ;\
|
||||||
|
rm -f $$file $$file.gz ;\
|
||||||
|
done
|
||||||
|
|
||||||
|
.PHONY: uninstall
|
222
doc/toxic.1
222
doc/toxic.1
@ -1,63 +1,169 @@
|
|||||||
.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.79.1 <http://docbook.sf.net/>
|
||||||
.B toxic [\-f
|
.\" Date: 2020-05-04
|
||||||
.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" "2020\-05\-04" "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.
|
\-l, \-\-logging
|
||||||
.IP ~/.config/tox/data
|
.RS 4
|
||||||
Savestate which contains your personal info (nickname, Tox ID,...) and
|
Enable toxcore logging to stderr
|
||||||
your contacts list.
|
.RE
|
||||||
.IP ~/.config/tox/toxic.conf
|
.PP
|
||||||
Configuration file. See
|
\-n, \-\-nodes nodes\-file
|
||||||
.BR toxic.conf (5)
|
.RS 4
|
||||||
for more details.
|
Use specified
|
||||||
.IP __DATADIR__/toxic.conf.example
|
\fInodes\-file\fR
|
||||||
Configuration example.
|
for DHT bootstrap nodes instead of
|
||||||
.SH BUGS
|
\fI~/\&.config/tox/DHTnodes\&.json\fR
|
||||||
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.
|
\-o, \-\-noconnect
|
||||||
.SH AUTHORS
|
.RS 4
|
||||||
JFreegman <JFreegman@gmail.com>
|
Do not connect to the DHT network
|
||||||
.SH SEE ALSO
|
.RE
|
||||||
.BR toxic.conf (5)
|
.PP
|
||||||
.SH LINKS
|
\-p, \-\-SOCKS5\-proxy
|
||||||
Project page on github: https://github.com/Tox/toxic
|
.RS 4
|
||||||
.br
|
Use a SOCKS5 proxy: Requires [IP] [port]
|
||||||
IRC channel on Freenode: chat.freenode.net#tox
|
.RE
|
||||||
|
.PP
|
||||||
|
\-P, \-\-HTTP\-proxy
|
||||||
|
.RS 4
|
||||||
|
Use a HTTP proxy: Requires [IP] [port]
|
||||||
|
.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
|
||||||
|
108
doc/toxic.1.asc
Normal file
108
doc/toxic.1.asc
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
-l, --logging::
|
||||||
|
Enable toxcore logging to stderr
|
||||||
|
|
||||||
|
-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
|
491
doc/toxic.conf.5
491
doc/toxic.conf.5
@ -1,111 +1,420 @@
|
|||||||
.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: 2020-11-18
|
||||||
.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" "2020\-11\-18" "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
|
\fBcolor_bar_bg\fR
|
||||||
.RS
|
.RS 4
|
||||||
Audio input device.
|
set background color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
.br
|
|
||||||
Values: <INTEGER> (number correspond to "/lsdev in")
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B audio_out_dev
|
\fBcolor_bar_fg\fR
|
||||||
.RS
|
.RS 4
|
||||||
Audio output device.
|
set foreground (text) color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
.br
|
|
||||||
Values: <INTEGER> (number correspond to "/lsdev out")
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.B download_path
|
\fBcolor_bar_accent\fR
|
||||||
.RS
|
.RS 4
|
||||||
Default path for downloads.
|
set foreground accent color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
.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;
|
\fBcolor_bar_notify\fR
|
||||||
.br
|
.RS 4
|
||||||
timestamps:1;
|
set foreground notify (and typing) color in chat status bar\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
.br
|
.RE
|
||||||
autolog:0;
|
.PP
|
||||||
.br
|
\fBautolog\fR
|
||||||
alerts:1;
|
.RS 4
|
||||||
.br
|
Enable or disable autologging\&. true or false
|
||||||
history_size:700;
|
.RE
|
||||||
.br
|
.PP
|
||||||
colour_theme:0;
|
\fBshow_typing_other\fR
|
||||||
.br
|
.RS 4
|
||||||
audio_in_dev:0;
|
Show when others are typing in a 1\-on\-1 chat\&. true or false
|
||||||
.br
|
.RE
|
||||||
audio_out_dev:0;
|
.PP
|
||||||
.br
|
\fBshow_typing_self\fR
|
||||||
download_path:/home/USERNAME/Downloads/;
|
.RS 4
|
||||||
.SH FILES
|
Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
|
||||||
.IP ~/.config/tox/toxic.conf
|
.RE
|
||||||
Main configuration file.
|
.PP
|
||||||
.IP __DATADIR__/toxic.conf.example
|
\fBshow_welcome_msg\fR
|
||||||
Configuration example.
|
.RS 4
|
||||||
.SH AUTHORS
|
Show welcome message on startup\&. true or false
|
||||||
JFreegman <JFreegman@gmail.com>
|
.RE
|
||||||
.SH SEE ALSO
|
.PP
|
||||||
.BR toxic (1)
|
\fBshow_connection_msg\fR
|
||||||
.SH LINKS
|
.RS 4
|
||||||
Project page on github: https://github.com/Tox/toxic
|
Enable friend connection change notifications\&. true or false
|
||||||
.br
|
.RE
|
||||||
IRC channel on Freenode: chat.freenode.net#tox
|
.PP
|
||||||
|
\fBnodelist_update_freq\fR
|
||||||
|
.RS 4
|
||||||
|
How often in days to update the DHT nodes list\&. (integer; 0 to disable)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBautosave_freq\fR
|
||||||
|
.RS 4
|
||||||
|
How often in seconds to auto\-save the Tox data file\&. (integer; 0 to disable)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBhistory_size\fR
|
||||||
|
.RS 4
|
||||||
|
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBnotification_timeout\fR
|
||||||
|
.RS 4
|
||||||
|
Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBline_join\fR
|
||||||
|
.RS 4
|
||||||
|
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBline_quit\fR
|
||||||
|
.RS 4
|
||||||
|
Indicator for when someone disconnects or leaves a group\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBline_alert\fR
|
||||||
|
.RS 4
|
||||||
|
Indicator for alert messages\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBline_normal\fR
|
||||||
|
.RS 4
|
||||||
|
Indicator for normal messages\&.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\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_threshold\fR
|
||||||
|
.RS 4
|
||||||
|
Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBconference_audio_channels\fR
|
||||||
|
.RS 4
|
||||||
|
Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBchat_audio_channels\fR
|
||||||
|
.RS 4
|
||||||
|
Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBpush_to_talk\fR
|
||||||
|
.RS 4
|
||||||
|
Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false
|
||||||
|
.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
|
||||||
|
\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>
|
||||||
|
275
doc/toxic.conf.5.asc
Normal file
275
doc/toxic.conf.5.asc
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
*color_bar_bg*;;
|
||||||
|
set background color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
|
|
||||||
|
*color_bar_fg*;;
|
||||||
|
set foreground (text) color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
|
|
||||||
|
*color_bar_accent*;;
|
||||||
|
set foreground accent color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
|
|
||||||
|
*color_bar_notify*;;
|
||||||
|
set foreground notify (and typing) color in chat status bar. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
|
|
||||||
|
*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. (integer; 0 to disable)
|
||||||
|
|
||||||
|
*autosave_freq*;;
|
||||||
|
How often in seconds to auto-save the Tox data file. (integer; 0 to disable)
|
||||||
|
|
||||||
|
*history_size*;;
|
||||||
|
Maximum lines for chat window history. Integer value. (for example: 700)
|
||||||
|
|
||||||
|
*notification_timeout*;;
|
||||||
|
Time in milliseconds to display a notification. Integer value. (for example: 3000)
|
||||||
|
|
||||||
|
*line_join*;;
|
||||||
|
Indicator for when someone connects or joins a group.
|
||||||
|
Three characters max for line_ settings.
|
||||||
|
|
||||||
|
*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_threshold*;;
|
||||||
|
Voice Activity Detection threshold. Float value. Recommended values are
|
||||||
|
1.0-40.0
|
||||||
|
|
||||||
|
*conference_audio_channels*;;
|
||||||
|
Number of channels for conference audio broadcast. Integer value. 1 (mono) or 2 (stereo)
|
||||||
|
|
||||||
|
*chat_audio_channels*;;
|
||||||
|
Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo)
|
||||||
|
|
||||||
|
*push_to_talk*;;
|
||||||
|
Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false
|
||||||
|
|
||||||
|
*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.
|
||||||
|
|
||||||
|
*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,148 @@
|
|||||||
# 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
|
// set background color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
download_path:/home/USERNAME/Downloads/;
|
color_bar_bg="blue";
|
||||||
|
|
||||||
|
// set foreground (text) color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
|
color_bar_fg="white";
|
||||||
|
|
||||||
|
// set foreground accent color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
|
color_bar_accent="cyan";
|
||||||
|
|
||||||
|
// set foreground notify (and typing) color in chat status bar (black, white, red, green, blue, cyan, yellow, magenta)
|
||||||
|
color_bar_notify="yellow";
|
||||||
|
|
||||||
|
// true to enable autologging, false to disable
|
||||||
|
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";
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// How often in seconds to auto-save the Tox data file. (0 to disable periodic auto-saves)
|
||||||
|
autosave_freq=600;
|
||||||
|
|
||||||
|
// maximum lines for chat window history
|
||||||
|
history_size=700;
|
||||||
|
|
||||||
|
// time in milliseconds to display a notification
|
||||||
|
notification_timeout=6000;
|
||||||
|
|
||||||
|
// 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 threshold; float (recommended values are 1.0-40.0)
|
||||||
|
VAD_threshold=5.0;
|
||||||
|
|
||||||
|
// Number of channels to use for conference audio broadcasts; 1 for mono, 2 for stereo.
|
||||||
|
conference_audio_channels=1;
|
||||||
|
|
||||||
|
// Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo.
|
||||||
|
chat_audio_channels=2;
|
||||||
|
|
||||||
|
// toggle conference push-to-talk
|
||||||
|
push_to_talk=false;
|
||||||
|
};
|
||||||
|
|
||||||
|
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";
|
||||||
|
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;
|
297
script/build-minimal-static-toxic.sh
Executable file
297
script/build-minimal-static-toxic.sh
Executable file
@ -0,0 +1,297 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# MIT License
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 Maxim Biro <nurupo.contributions@gmail.com>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
# Script for building a minimal statically compiled Toxic. While it doesn't
|
||||||
|
# support X11 integration, video/audio calls, desktop & sound notifications, QR
|
||||||
|
# codes and Python scripting, it is rather portable.
|
||||||
|
#
|
||||||
|
# Run as:
|
||||||
|
#
|
||||||
|
# sudo docker run -it --rm \
|
||||||
|
# -v /tmp/artifact:/artifact \
|
||||||
|
# -v /home/jfreegman/git/toxic:/toxic \
|
||||||
|
# amd64/alpine:latest \
|
||||||
|
# /bin/sh /toxic/script/build-minimal-static-toxic.sh
|
||||||
|
#
|
||||||
|
# that would use Toxic code from /home/jfreegman/git/toxic and place the build
|
||||||
|
# artifact at /tmp/artifact.
|
||||||
|
# You can change between amd64/alpine:latest and i386/alpine:latest, for 64-bit
|
||||||
|
# and 32-bit builds.
|
||||||
|
#
|
||||||
|
# To debug, run:
|
||||||
|
#
|
||||||
|
# sudo docker run -it --rm \
|
||||||
|
# -v /tmp/artifact:/artifact \
|
||||||
|
# -v /home/jfreegman/git/toxic:/toxic \
|
||||||
|
# amd64/alpine:latest \
|
||||||
|
# /bin/sh
|
||||||
|
# # sh /toxic/script/build-minimal-static-toxic.sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ARTIFACT_DIR="/artifact"
|
||||||
|
TOXIC_SRC_DIR="/toxic"
|
||||||
|
|
||||||
|
# Make sure we run in the expected environment
|
||||||
|
if ! grep -q 'docker' /proc/1/cgroup
|
||||||
|
then
|
||||||
|
echo "Error: This script should be run inside a disposable Docker container as it might modify system files in ways that would break a real system."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /etc/os-release ] || ! grep -qi 'Alpine Linux' /etc/os-release
|
||||||
|
then
|
||||||
|
echo "Error: This script expects to be run on Alpine Linux."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$ARTIFACT_DIR" ] || [ ! -d "$TOXIC_SRC_DIR" ]
|
||||||
|
then
|
||||||
|
echo "Error: At least one of $ARTIFACT_DIR or $TOXIC_SRC_DIR directories inside the container is missing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(id -u)" != "0" ]
|
||||||
|
then
|
||||||
|
echo "Error: This script expects to be run as root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Use all cores for building
|
||||||
|
MAKEFLAGS=j$(nproc)
|
||||||
|
export MAKEFLAGS
|
||||||
|
|
||||||
|
check_sha256()
|
||||||
|
{
|
||||||
|
if ! ( echo "$1 $2" | sha256sum -cs - )
|
||||||
|
then
|
||||||
|
echo "Error: sha256 of $2 doesn't match the known one."
|
||||||
|
echo "Expected: $1 $2"
|
||||||
|
echo "Got: $(sha256sum "$2")"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "sha256 matches the expected one: $1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
apk update
|
||||||
|
apk upgrade
|
||||||
|
apk add \
|
||||||
|
brotli-dev \
|
||||||
|
brotli-static \
|
||||||
|
build-base \
|
||||||
|
cmake \
|
||||||
|
git \
|
||||||
|
libconfig-dev \
|
||||||
|
libconfig-static \
|
||||||
|
libsodium-dev \
|
||||||
|
libsodium-static \
|
||||||
|
linux-headers \
|
||||||
|
ncurses-dev \
|
||||||
|
ncurses-static \
|
||||||
|
ncurses-terminfo \
|
||||||
|
ncurses-terminfo-base \
|
||||||
|
nghttp2-dev \
|
||||||
|
nghttp2-static \
|
||||||
|
openssl-dev \
|
||||||
|
openssl-libs-static \
|
||||||
|
pkgconf \
|
||||||
|
wget \
|
||||||
|
xz \
|
||||||
|
zlib-dev \
|
||||||
|
zlib-static
|
||||||
|
|
||||||
|
BUILD_DIR="/tmp/build"
|
||||||
|
mkdir -p "$BUILD_DIR"
|
||||||
|
|
||||||
|
|
||||||
|
# Build Toxcore
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
|
||||||
|
TOXCORE_VERSION="0.2.12"
|
||||||
|
TOXCORE_HASH="30ae3263c9b68d3bef06f799ba9d7a67e3fad447030625f0ffa4bb22684228b0"
|
||||||
|
TOXCORE_FILENAME="c-toxcore-$TOXCORE_VERSION.tar.gz"
|
||||||
|
|
||||||
|
wget --timeout=10 -O "$TOXCORE_FILENAME" "https://github.com/TokTok/c-toxcore/archive/v$TOXCORE_VERSION.tar.gz"
|
||||||
|
check_sha256 "$TOXCORE_HASH" "$TOXCORE_FILENAME"
|
||||||
|
tar -o -xf "$TOXCORE_FILENAME"
|
||||||
|
rm "$TOXCORE_FILENAME"
|
||||||
|
cd c-toxcore*
|
||||||
|
|
||||||
|
cmake -B_build -H. \
|
||||||
|
-DENABLE_STATIC=ON \
|
||||||
|
-DENABLE_SHARED=OFF \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DBUILD_TOXAV=OFF \
|
||||||
|
-DBOOTSTRAP_DAEMON=OFF \
|
||||||
|
-DDHT_BOOTSTRAP=OFF \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="$BUILD_DIR/prefix-toxcore"
|
||||||
|
cmake --build _build --target install
|
||||||
|
|
||||||
|
|
||||||
|
# Build cURL
|
||||||
|
# While Alpine does provide a static cURL build, it's not built with
|
||||||
|
# --with-ca-fallback, which is needed for better cross-distro portability.
|
||||||
|
# Basically, some distros put their ca-certificates in different places, and
|
||||||
|
# with --with-ca-fallback we or the user can provide the cert bundle file
|
||||||
|
# location with SSL_CERT_FILE env variable.
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
|
||||||
|
CURL_VERSION="7.74.0"
|
||||||
|
CURL_HASH="e56b3921eeb7a2951959c02db0912b5fcd5fdba5aca071da819e1accf338bbd7"
|
||||||
|
CURL_FILENAME="curl-$CURL_VERSION.tar.gz"
|
||||||
|
|
||||||
|
wget --timeout=10 -O "$CURL_FILENAME" "https://curl.haxx.se/download/$CURL_FILENAME"
|
||||||
|
check_sha256 "$CURL_HASH" "$CURL_FILENAME"
|
||||||
|
tar -xf curl*.tar.gz
|
||||||
|
rm curl*.tar.gz
|
||||||
|
cd curl*
|
||||||
|
|
||||||
|
./configure \
|
||||||
|
--prefix="$BUILD_DIR/prefix-curl" \
|
||||||
|
--disable-shared \
|
||||||
|
--enable-static \
|
||||||
|
--without-ca-bundle \
|
||||||
|
--without-ca-path \
|
||||||
|
--with-ca-fallback \
|
||||||
|
--with-nghttp2 \
|
||||||
|
--with-brotli
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
sed -i 's|-lbrotlidec |-lbrotlidec-static -lbrotlicommon-static |g' $BUILD_DIR/prefix-curl/lib/pkgconfig/libcurl.pc
|
||||||
|
|
||||||
|
# Build Toxic
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
cp -a "$TOXIC_SRC_DIR" toxic
|
||||||
|
cd toxic
|
||||||
|
|
||||||
|
if [ -z "$(git describe --tags --exact-match HEAD)" ]
|
||||||
|
then
|
||||||
|
set +x
|
||||||
|
echo "Didn't find a git tag on the HEAD commit. You seem to be building an in-development release of Toxic rather than a release version." | fold -sw 80
|
||||||
|
printf "Do you wish to proceed? (y/N): "
|
||||||
|
read -r answer
|
||||||
|
if echo "$answer" | grep -v -iq "^y" ; then
|
||||||
|
echo "Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
sed -i 's|pkg-config|pkg-config --static|' cfg/global_vars.mk
|
||||||
|
sed -i 's|<limits.h|<linux/limits.h|' src/*
|
||||||
|
|
||||||
|
CFLAGS="-static" PKG_CONFIG_PATH="$BUILD_DIR/prefix-toxcore/lib64/pkgconfig:$BUILD_DIR/prefix-toxcore/lib/pkgconfig:$BUILD_DIR/prefix-curl/lib/pkgconfig" PREFIX="$BUILD_DIR/prefix-toxic" make \
|
||||||
|
DISABLE_X11=1 \
|
||||||
|
DISABLE_AV=1 \
|
||||||
|
DISABLE_SOUND_NOTIFY=1 \
|
||||||
|
DISABLE_QRCODE=1 \
|
||||||
|
DISABLE_QRPNG=1 \
|
||||||
|
DISABLE_DESKTOP_NOTIFY=1 \
|
||||||
|
ENABLE_PYTHON=0 \
|
||||||
|
ENABLE_RELEASE=1 \
|
||||||
|
ENABLE_ASAN=0 \
|
||||||
|
install
|
||||||
|
|
||||||
|
|
||||||
|
# Prepare the build artifact
|
||||||
|
PREPARE_ARTIFACT_DIR="$BUILD_DIR/artifact"
|
||||||
|
cp -a "$BUILD_DIR/prefix-toxic/bin" "$PREPARE_ARTIFACT_DIR"
|
||||||
|
strip "$PREPARE_ARTIFACT_DIR"/*
|
||||||
|
|
||||||
|
cp -a "$BUILD_DIR/toxic/misc"/* "$PREPARE_ARTIFACT_DIR"
|
||||||
|
mv "$PREPARE_ARTIFACT_DIR/toxic.conf.example" "$PREPARE_ARTIFACT_DIR/toxic.conf"
|
||||||
|
|
||||||
|
cp -aL /usr/share/terminfo "$PREPARE_ARTIFACT_DIR"
|
||||||
|
|
||||||
|
echo "A minimal statically compiled Toxic.
|
||||||
|
Doesn't support X11 integration, video/audio calls, desktop & sound
|
||||||
|
notifications, QR codes and Python scripting.
|
||||||
|
However, it is rather portable.
|
||||||
|
|
||||||
|
Toxic $(git -C "$BUILD_DIR/toxic" describe --tags --exact-match HEAD) ($(git -C "$BUILD_DIR/toxic" rev-parse HEAD))
|
||||||
|
|
||||||
|
Build date time: $(TZ=UTC date +"%Y-%m-%dT%H:%M:%S%z")
|
||||||
|
|
||||||
|
OS:
|
||||||
|
$(cat /etc/os-release)
|
||||||
|
|
||||||
|
List of self-built software statically compiled into Toxic:
|
||||||
|
libcurl $CURL_VERSION
|
||||||
|
libtoxcore $TOXCORE_VERSION
|
||||||
|
|
||||||
|
List of OS-packaged software statically compiled into Toxic:
|
||||||
|
$(apk list -I | grep 'static' | sort -i)
|
||||||
|
|
||||||
|
List of all packages installed during the build:
|
||||||
|
$(apk list -I | sort -i)" > "$PREPARE_ARTIFACT_DIR/build_info"
|
||||||
|
|
||||||
|
echo '#!/usr/bin/env sh
|
||||||
|
|
||||||
|
DEBIAN_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||||
|
RHEL_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
|
||||||
|
OPENSUSE_CERT_FILE=/etc/ssl/ca-bundle.pem
|
||||||
|
|
||||||
|
if [ ! -f "$SSL_CERT_FILE" ] ; then
|
||||||
|
if [ -f "$DEBIAN_SSL_CERT_FILE" ] ; then
|
||||||
|
SSL_CERT_FILE="$DEBIAN_SSL_CERT_FILE"
|
||||||
|
elif [ -f "$RHEL_SSL_CERT_FILE" ] ; then
|
||||||
|
SSL_CERT_FILE="$RHEL_SSL_CERT_FILE"
|
||||||
|
elif [ -f "$OPENSUSE_CERT_FILE" ] ; then
|
||||||
|
SSL_CERT_FILE="$OPENSUSE_CERT_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$SSL_CERT_FILE" ] ; then
|
||||||
|
echo "Warning: Couldn'\''t find the SSL CA certificate store file." | fold -sw 80
|
||||||
|
echo
|
||||||
|
echo "Toxic uses HTTPS to download a list of DHT bootstrap nodes in order to connect to the Tox DHT. This functionality is optional, you should be able to use Toxic without it. If you choose to use Toxic without it, you might need to manually enter DHT bootstrap node information using the '\''/connect'\'' command in order to come online." | fold -sw 80
|
||||||
|
echo
|
||||||
|
echo "To fix this issue, install SSL CAs as provided by your Linux distribution, e.g. '\''ca-certificates'\'' package on Debian/Ubuntu. If it'\''s already installed and you still see this message, run this script with SSL_CERT_FILE variable set to point to the SSL CA certificate store file location. The file is usually named '\''ca-certificates.crt'\'' or '\''ca-bundle.pem'\''." | fold -sw 80
|
||||||
|
echo
|
||||||
|
printf "Do you wish to run Toxic without SSL CA certificate store file found? (y/N): "
|
||||||
|
read -r answer
|
||||||
|
if echo "$answer" | grep -v -iq "^y" ; then
|
||||||
|
echo "Exiting."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$(dirname -- $0)"
|
||||||
|
|
||||||
|
SSL_CERT_FILE="$SSL_CERT_FILE" TERMINFO=./terminfo ./toxic -c toxic.conf' > "$PREPARE_ARTIFACT_DIR/run_toxic.sh"
|
||||||
|
chmod a+x "$PREPARE_ARTIFACT_DIR/run_toxic.sh"
|
||||||
|
|
||||||
|
|
||||||
|
# Tar it
|
||||||
|
cd "$PREPARE_ARTIFACT_DIR"
|
||||||
|
cd ..
|
||||||
|
ARCH="$(tr '_' '-' < /etc/apk/arch)"
|
||||||
|
ARTIFACT_NAME="toxic-minimal-static-musl_linux_$ARCH"
|
||||||
|
mv "$PREPARE_ARTIFACT_DIR" "$PREPARE_ARTIFACT_DIR/../$ARTIFACT_NAME"
|
||||||
|
tar -cJf "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_NAME"
|
||||||
|
mv "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_DIR"
|
||||||
|
chmod 777 -R "$ARTIFACT_DIR"
|
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.
|
217
src/api.c
Normal file
217
src/api.c
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/* api.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "execute.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "message_queue.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
#include "python_api.h"
|
||||||
|
|
||||||
|
Tox *user_tox;
|
||||||
|
static WINDOW *cur_window;
|
||||||
|
static ToxWindow *self_window;
|
||||||
|
|
||||||
|
extern FriendsList Friends;
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
|
void api_display(const char *const msg)
|
||||||
|
{
|
||||||
|
if (msg == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self_window = get_active_window();
|
||||||
|
line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
FriendsList api_get_friendslist(void)
|
||||||
|
{
|
||||||
|
return Friends;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *api_get_nick(void)
|
||||||
|
{
|
||||||
|
size_t len = tox_self_get_name_size(user_tox);
|
||||||
|
uint8_t *name = malloc(len + 1);
|
||||||
|
|
||||||
|
if (name == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_self_get_name(user_tox, name);
|
||||||
|
name[len] = '\0';
|
||||||
|
return (char *) name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tox_User_Status api_get_status(void)
|
||||||
|
{
|
||||||
|
return tox_self_get_status(user_tox);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *api_get_status_message(void)
|
||||||
|
{
|
||||||
|
size_t len = tox_self_get_status_message_size(user_tox);
|
||||||
|
uint8_t *status = malloc(len + 1);
|
||||||
|
|
||||||
|
if (status == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_self_get_status_message(user_tox, status);
|
||||||
|
status[len] = '\0';
|
||||||
|
return (char *) status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void api_send(const char *msg)
|
||||||
|
{
|
||||||
|
if (msg == NULL || self_window->chatwin->cqueue == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *name = api_get_nick();
|
||||||
|
|
||||||
|
if (name == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self_window = get_active_window();
|
||||||
|
|
||||||
|
strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line));
|
||||||
|
add_line_to_hist(self_window->chatwin);
|
||||||
|
int id = line_info_add(self_window, true, name, NULL, OUT_MSG, 0, 0, "%s", msg);
|
||||||
|
cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id);
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void api_execute(const char *input, int mode)
|
||||||
|
{
|
||||||
|
self_window = get_active_window();
|
||||||
|
execute(cur_window, self_window, user_tox, input, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
return do_python_command(num_args, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_registered_handlers(void)
|
||||||
|
{
|
||||||
|
return python_num_registered_handlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
int help_max_width(void)
|
||||||
|
{
|
||||||
|
return python_help_max_width();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_handler_help(WINDOW *win)
|
||||||
|
{
|
||||||
|
python_draw_handler_help(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
const char *error_str;
|
||||||
|
|
||||||
|
cur_window = window;
|
||||||
|
self_window = self;
|
||||||
|
|
||||||
|
if (argc != 1) {
|
||||||
|
if (argc < 1) {
|
||||||
|
error_str = "Path must be specified.";
|
||||||
|
} else {
|
||||||
|
error_str = "Only one argument allowed.";
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_python(fp, argv[1]);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_autoruns(WINDOW *window, ToxWindow *self)
|
||||||
|
{
|
||||||
|
char abspath_buf[PATH_MAX + 256];
|
||||||
|
char err_buf[PATH_MAX + 128];
|
||||||
|
|
||||||
|
if (user_settings->autorun_path[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIR *d = opendir(user_settings->autorun_path);
|
||||||
|
|
||||||
|
if (d == NULL) {
|
||||||
|
snprintf(err_buf, sizeof(err_buf), "Autorun path does not exist: %s", user_settings->autorun_path);
|
||||||
|
api_display(err_buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *dir = NULL;
|
||||||
|
|
||||||
|
cur_window = window;
|
||||||
|
|
||||||
|
self_window = self;
|
||||||
|
|
||||||
|
while ((dir = readdir(d)) != NULL) {
|
||||||
|
size_t path_len = strlen(dir->d_name);
|
||||||
|
|
||||||
|
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
|
||||||
|
snprintf(abspath_buf, sizeof(abspath_buf), "%s%s", user_settings->autorun_path, dir->d_name);
|
||||||
|
FILE *fp = fopen(abspath_buf, "r");
|
||||||
|
|
||||||
|
if (fp == NULL) {
|
||||||
|
snprintf(err_buf, sizeof(err_buf), "Invalid path: %s", abspath_buf);
|
||||||
|
api_display(err_buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_python(fp, abspath_buf);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
#endif /* PYTHON */
|
43
src/api.h
Normal file
43
src/api.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* api.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef API_H
|
||||||
|
#define API_H
|
||||||
|
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
void api_display(const char *const msg);
|
||||||
|
FriendsList api_get_friendslist(void);
|
||||||
|
char *api_get_nick(void);
|
||||||
|
Tox_User_Status api_get_status(void);
|
||||||
|
char *api_get_status_message(void);
|
||||||
|
void api_send(const char *msg);
|
||||||
|
void api_execute(const char *input, int mode);
|
||||||
|
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]);
|
||||||
|
int num_registered_handlers(void);
|
||||||
|
int help_max_width(void);
|
||||||
|
void draw_handler_help(WINDOW *win);
|
||||||
|
void invoke_autoruns(WINDOW *w, ToxWindow *self);
|
||||||
|
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
|
||||||
|
#endif /* API_H */
|
1260
src/audio_call.c
1260
src/audio_call.c
File diff suppressed because it is too large
Load Diff
@ -20,28 +20,90 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#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
|
typedef enum AudioError {
|
||||||
|
|
||||||
typedef enum _AudioError {
|
|
||||||
ae_None = 0,
|
ae_None = 0,
|
||||||
ae_StartingCaptureDevice = 1 << 0,
|
ae_StartingCaptureDevice = 1 << 0,
|
||||||
ae_StartingOutputDevice = 1 << 1,
|
ae_StartingOutputDevice = 1 << 1,
|
||||||
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 */
|
||||||
|
|
||||||
|
/* Status transitions:
|
||||||
|
* None -> Pending (call invitation made or received);
|
||||||
|
* Pending -> None (invitation rejected or failed);
|
||||||
|
* Pending -> Active (call starts);
|
||||||
|
* Active -> None (call ends).
|
||||||
|
*/
|
||||||
|
typedef enum CallStatus {
|
||||||
|
cs_None = 0,
|
||||||
|
cs_Pending,
|
||||||
|
cs_Active
|
||||||
|
} CallStatus;
|
||||||
|
|
||||||
|
typedef struct Call {
|
||||||
|
CallStatus status;
|
||||||
|
uint32_t state; /* ToxAV call state, valid when `status == cs_Active` */
|
||||||
|
uint32_t in_idx, out_idx; /* Audio device index, or -1 if not open */
|
||||||
|
uint32_t audio_bit_rate; /* Bit rate for sending audio */
|
||||||
|
|
||||||
|
uint32_t vin_idx, vout_idx; /* Video device index, or -1 if not open */
|
||||||
|
uint32_t video_width, video_height;
|
||||||
|
uint32_t video_bit_rate; /* Bit rate for sending video; 0 for no video */
|
||||||
|
} Call;
|
||||||
|
|
||||||
|
struct CallControl {
|
||||||
|
AudioError audio_errors;
|
||||||
|
#ifdef VIDEO
|
||||||
|
VideoError video_errors;
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
ToxAV *av;
|
||||||
|
ToxWindow *prompt;
|
||||||
|
|
||||||
|
Call *calls;
|
||||||
|
uint32_t max_calls;
|
||||||
|
|
||||||
|
bool audio_enabled;
|
||||||
|
bool video_enabled;
|
||||||
|
|
||||||
|
int32_t audio_frame_duration;
|
||||||
|
uint32_t audio_sample_rate;
|
||||||
|
uint8_t audio_channels;
|
||||||
|
uint32_t default_audio_bit_rate;
|
||||||
|
|
||||||
|
int32_t video_frame_duration;
|
||||||
|
uint32_t default_video_width, default_video_height;
|
||||||
|
uint32_t default_video_bit_rate;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct CallControl CallControl;
|
||||||
|
|
||||||
|
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
||||||
|
ToxAV *init_audio(ToxWindow *self, Tox *tox);
|
||||||
|
void terminate_audio(void);
|
||||||
|
|
||||||
|
bool init_call(Call *call);
|
||||||
|
|
||||||
|
void place_call(ToxWindow *self);
|
||||||
void stop_current_call(ToxWindow *self);
|
void stop_current_call(ToxWindow *self);
|
||||||
|
|
||||||
#endif /* _audio_h */
|
void init_friend_AV(uint32_t index);
|
||||||
|
void del_friend_AV(uint32_t index);
|
||||||
|
|
||||||
|
#endif /* AUDIO_CALL_H */
|
||||||
|
790
src/audio_device.c
Normal file
790
src/audio_device.c
Normal file
@ -0,0 +1,790 @@
|
|||||||
|
/* audio_device.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "audio_device.h"
|
||||||
|
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
extern struct Winthread Winthread;
|
||||||
|
|
||||||
|
typedef struct FrameInfo {
|
||||||
|
uint32_t samples_per_frame;
|
||||||
|
uint32_t sample_rate;
|
||||||
|
bool stereo;
|
||||||
|
} FrameInfo;
|
||||||
|
|
||||||
|
/* A virtual input/output device, abstracting the currently selected openal
|
||||||
|
* device (which may change during the lifetime of the virtual device).
|
||||||
|
* We refer to a virtual device as a "device", and refer to an underlying
|
||||||
|
* openal device as an "al_device".
|
||||||
|
* Multiple virtual devices may be open at once; the callback of each virtual
|
||||||
|
* input device has data captured from the input al_device passed to it, and
|
||||||
|
* each virtual output device acts as a source for the output al_device.
|
||||||
|
*/
|
||||||
|
typedef struct Device {
|
||||||
|
bool active;
|
||||||
|
bool muted;
|
||||||
|
|
||||||
|
FrameInfo frame_info;
|
||||||
|
|
||||||
|
// used only by input devices:
|
||||||
|
DataHandleCallback cb;
|
||||||
|
void *cb_data;
|
||||||
|
float VAD_threshold;
|
||||||
|
uint32_t VAD_samples_remaining;
|
||||||
|
|
||||||
|
// used only by output devices:
|
||||||
|
uint32_t source;
|
||||||
|
uint32_t buffers[OPENAL_BUFS];
|
||||||
|
bool source_open;
|
||||||
|
} Device;
|
||||||
|
|
||||||
|
typedef struct AudioState {
|
||||||
|
ALCdevice *al_device[2];
|
||||||
|
|
||||||
|
Device devices[2][MAX_DEVICES];
|
||||||
|
uint32_t num_devices[2];
|
||||||
|
|
||||||
|
FrameInfo capture_frame_info;
|
||||||
|
float input_volume;
|
||||||
|
|
||||||
|
// mutexes to prevent changes to input resp. output devices and al_devices
|
||||||
|
// during poll_input iterations resp. calls to write_out;
|
||||||
|
// mutex[input] also used to lock input_volume which poll_input writes to.
|
||||||
|
pthread_mutex_t mutex[2];
|
||||||
|
|
||||||
|
// TODO: unused
|
||||||
|
const char *default_al_device_name[2]; /* Default devices */
|
||||||
|
|
||||||
|
const char *al_device_names[2][MAX_OPENAL_DEVICES]; /* Available devices */
|
||||||
|
uint32_t num_al_devices[2];
|
||||||
|
char *current_al_device_name[2];
|
||||||
|
} AudioState;
|
||||||
|
|
||||||
|
static AudioState *audio_state;
|
||||||
|
|
||||||
|
static void lock(DeviceType type)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&audio_state->mutex[type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlock(DeviceType type)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&audio_state->mutex[type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool thread_running = true,
|
||||||
|
thread_paused = true; /* Thread control */
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
static void *poll_input(void *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint32_t sound_mode(bool stereo)
|
||||||
|
{
|
||||||
|
return stereo ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sample_size(bool stereo)
|
||||||
|
{
|
||||||
|
return stereo ? 4 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError init_devices(void)
|
||||||
|
{
|
||||||
|
audio_state = calloc(1, sizeof(AudioState));
|
||||||
|
|
||||||
|
if (audio_state == NULL) {
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_al_device_names();
|
||||||
|
|
||||||
|
for (DeviceType type = input; type <= output; ++type) {
|
||||||
|
audio_state->al_device[type] = NULL;
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&audio_state->mutex[type], NULL) != 0) {
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
// Start poll thread
|
||||||
|
pthread_t thread_id;
|
||||||
|
|
||||||
|
if (pthread_create(&thread_id, NULL, poll_input, NULL) != 0
|
||||||
|
|| pthread_detach(thread_id) != 0) {
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError terminate_devices(void)
|
||||||
|
{
|
||||||
|
lock(input);
|
||||||
|
thread_running = false;
|
||||||
|
unlock(input);
|
||||||
|
|
||||||
|
sleep_thread(20000L);
|
||||||
|
|
||||||
|
for (DeviceType type = input; type <= output; ++type) {
|
||||||
|
if (pthread_mutex_destroy(&audio_state->mutex[type]) != 0) {
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audio_state->current_al_device_name[type] != NULL) {
|
||||||
|
free(audio_state->current_al_device_name[type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(audio_state);
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_al_device_names(void)
|
||||||
|
{
|
||||||
|
const char *stringed_device_list;
|
||||||
|
|
||||||
|
for (DeviceType type = input; type <= output; ++type) {
|
||||||
|
audio_state->num_al_devices[type] = 0;
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
|
||||||
|
} else {
|
||||||
|
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 != NULL) {
|
||||||
|
audio_state->default_al_device_name[type] = alcGetString(NULL,
|
||||||
|
type == input ? ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER : ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
|
for (; *stringed_device_list != '\0'
|
||||||
|
&& audio_state->num_al_devices[type] < MAX_OPENAL_DEVICES; ++audio_state->num_al_devices[type]) {
|
||||||
|
audio_state->al_device_names[type][audio_state->num_al_devices[type]] = stringed_device_list;
|
||||||
|
stringed_device_list += strlen(stringed_device_list) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) {
|
||||||
|
return de_InvalidSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[type][device_idx];
|
||||||
|
|
||||||
|
if (!device->active) {
|
||||||
|
return de_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(type);
|
||||||
|
|
||||||
|
device->muted = !device->muted;
|
||||||
|
|
||||||
|
unlock(type);
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool device_is_muted(DeviceType type, uint32_t device_idx)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[type][device_idx];
|
||||||
|
|
||||||
|
if (!device->active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device->muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError device_set_VAD_threshold(uint32_t device_idx, float value)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) {
|
||||||
|
return de_InvalidSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[input][device_idx];
|
||||||
|
|
||||||
|
if (!device->active) {
|
||||||
|
return de_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 0.0f) {
|
||||||
|
value = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(input);
|
||||||
|
|
||||||
|
device->VAD_threshold = value;
|
||||||
|
|
||||||
|
unlock(input);
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
float device_get_VAD_threshold(uint32_t device_idx)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[input][device_idx];
|
||||||
|
|
||||||
|
if (!device->active) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device->VAD_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError set_source_position(uint32_t device_idx, float x, float y, float z)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) {
|
||||||
|
return de_InvalidSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[output][device_idx];
|
||||||
|
|
||||||
|
if (!device->active) {
|
||||||
|
return de_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(output);
|
||||||
|
|
||||||
|
alSource3f(device->source, AL_POSITION, x, y, z);
|
||||||
|
|
||||||
|
unlock(output);
|
||||||
|
|
||||||
|
if (!audio_state->al_device[output] || alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
|
return de_AlError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DeviceError close_al_device(DeviceType type)
|
||||||
|
{
|
||||||
|
if (audio_state->al_device[type] == NULL) {
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
if (!alcCaptureCloseDevice(audio_state->al_device[type])) {
|
||||||
|
return de_AlError;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_paused = true;
|
||||||
|
} else {
|
||||||
|
ALCcontext *context = alcGetCurrentContext();
|
||||||
|
alcMakeContextCurrent(NULL);
|
||||||
|
alcDestroyContext(context);
|
||||||
|
|
||||||
|
if (!alcCloseDevice(audio_state->al_device[type])) {
|
||||||
|
return de_AlError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_state->al_device[type] = NULL;
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DeviceError open_al_device(DeviceType type, FrameInfo frame_info)
|
||||||
|
{
|
||||||
|
audio_state->al_device[type] = type == input
|
||||||
|
? alcCaptureOpenDevice(audio_state->current_al_device_name[type],
|
||||||
|
frame_info.sample_rate, sound_mode(frame_info.stereo), frame_info.samples_per_frame * 2)
|
||||||
|
: alcOpenDevice(audio_state->current_al_device_name[type]);
|
||||||
|
|
||||||
|
if (audio_state->al_device[type] == NULL) {
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
alcCaptureStart(audio_state->al_device[type]);
|
||||||
|
thread_paused = false;
|
||||||
|
|
||||||
|
audio_state->capture_frame_info = frame_info;
|
||||||
|
} else {
|
||||||
|
alcMakeContextCurrent(alcCreateContext(audio_state->al_device[type], NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alcGetError(audio_state->al_device[type]) != AL_NO_ERROR) {
|
||||||
|
close_al_device(type);
|
||||||
|
return de_AlError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_source(Device *device)
|
||||||
|
{
|
||||||
|
if (device->source_open) {
|
||||||
|
alDeleteSources(1, &device->source);
|
||||||
|
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
||||||
|
|
||||||
|
device->source_open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DeviceError open_source(Device *device)
|
||||||
|
{
|
||||||
|
alGenBuffers(OPENAL_BUFS, device->buffers);
|
||||||
|
|
||||||
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
alGenSources((uint32_t)1, &device->source);
|
||||||
|
|
||||||
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
|
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->source_open = true;
|
||||||
|
|
||||||
|
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
||||||
|
|
||||||
|
const uint32_t frame_size = device->frame_info.samples_per_frame * sample_size(device->frame_info.stereo);
|
||||||
|
size_t zeros_size = frame_size * sizeof(uint16_t);
|
||||||
|
uint16_t *zeros = calloc(1, zeros_size);
|
||||||
|
|
||||||
|
if (zeros == NULL) {
|
||||||
|
close_source(device);
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < OPENAL_BUFS; ++i) {
|
||||||
|
alBufferData(device->buffers[i], sound_mode(device->frame_info.stereo), zeros,
|
||||||
|
zeros_size, device->frame_info.sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(zeros);
|
||||||
|
|
||||||
|
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
|
||||||
|
alSourcePlay(device->source);
|
||||||
|
|
||||||
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
|
close_source(device);
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError set_al_device(DeviceType type, int32_t selection)
|
||||||
|
{
|
||||||
|
if (audio_state->num_al_devices[type] <= selection || selection < 0) {
|
||||||
|
return de_InvalidSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *name = audio_state->al_device_names[type][selection];
|
||||||
|
|
||||||
|
char **cur_name = &audio_state->current_al_device_name[type];
|
||||||
|
|
||||||
|
if (*cur_name != NULL) {
|
||||||
|
free(*cur_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
*cur_name = malloc(strlen(name) + 1);
|
||||||
|
|
||||||
|
if (*cur_name == NULL) {
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(*cur_name, name);
|
||||||
|
|
||||||
|
if (audio_state->num_devices[type] > 0) {
|
||||||
|
// close any existing al_device and try to open new one, reopening existing sources
|
||||||
|
lock(type);
|
||||||
|
|
||||||
|
if (type == output) {
|
||||||
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||||
|
Device *device = &audio_state->devices[type][i];
|
||||||
|
|
||||||
|
if (device->active) {
|
||||||
|
close_source(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close_al_device(type);
|
||||||
|
|
||||||
|
DeviceError err = open_al_device(type, audio_state->capture_frame_info);
|
||||||
|
|
||||||
|
if (err != de_None) {
|
||||||
|
unlock(type);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == output) {
|
||||||
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||||
|
Device *device = &audio_state->devices[type][i];
|
||||||
|
|
||||||
|
if (device->active) {
|
||||||
|
open_source(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DeviceError open_device(DeviceType type, uint32_t *device_idx,
|
||||||
|
DataHandleCallback cb, void *cb_data, bool enable_VAD,
|
||||||
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
|
||||||
|
{
|
||||||
|
if (channels != 1 && channels != 2) {
|
||||||
|
return de_UnsupportedMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t samples_per_frame = (sample_rate * frame_duration / 1000);
|
||||||
|
FrameInfo frame_info = {samples_per_frame, sample_rate, channels == 2};
|
||||||
|
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DEVICES && audio_state->devices[type][i].active; ++i);
|
||||||
|
|
||||||
|
if (i == MAX_DEVICES) {
|
||||||
|
return de_AllDevicesBusy;
|
||||||
|
}
|
||||||
|
|
||||||
|
*device_idx = i;
|
||||||
|
|
||||||
|
lock(type);
|
||||||
|
|
||||||
|
if (audio_state->al_device[type] == NULL) {
|
||||||
|
DeviceError err = open_al_device(type, frame_info);
|
||||||
|
|
||||||
|
if (err != de_None) {
|
||||||
|
unlock(type);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
} else if (type == input) {
|
||||||
|
// Use previously set frame info on existing capture device
|
||||||
|
frame_info = audio_state->capture_frame_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[type][i];
|
||||||
|
device->active = true;
|
||||||
|
++audio_state->num_devices[type];
|
||||||
|
|
||||||
|
device->muted = false;
|
||||||
|
device->frame_info = frame_info;
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
device->cb = cb;
|
||||||
|
device->cb_data = cb_data;
|
||||||
|
#ifdef AUDIO
|
||||||
|
device->VAD_threshold = enable_VAD ? user_settings->VAD_threshold : 0.0f;
|
||||||
|
#else
|
||||||
|
device->VAD_threshold = 0.0f;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
if (open_source(device) != de_None) {
|
||||||
|
device->active = false;
|
||||||
|
--audio_state->num_devices[type];
|
||||||
|
unlock(type);
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock(type);
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError open_input_device(uint32_t *device_idx,
|
||||||
|
DataHandleCallback cb, void *cb_data, bool enable_VAD,
|
||||||
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
|
||||||
|
{
|
||||||
|
return open_device(input, device_idx,
|
||||||
|
cb, cb_data, enable_VAD,
|
||||||
|
sample_rate, frame_duration, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError open_output_device(uint32_t *device_idx,
|
||||||
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
|
||||||
|
{
|
||||||
|
return open_device(output, device_idx,
|
||||||
|
0, 0, 0,
|
||||||
|
sample_rate, frame_duration, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError close_device(DeviceType type, uint32_t device_idx)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) {
|
||||||
|
return de_InvalidSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(type);
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[type][device_idx];
|
||||||
|
|
||||||
|
if (!device->active) {
|
||||||
|
return de_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == output) {
|
||||||
|
close_source(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
device->active = false;
|
||||||
|
--audio_state->num_devices[type];
|
||||||
|
|
||||||
|
DeviceError err = de_None;
|
||||||
|
|
||||||
|
if (audio_state->num_devices[type] == 0) {
|
||||||
|
err = close_al_device(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock(type);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(output);
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[output][device_idx];
|
||||||
|
|
||||||
|
if (!device->active || device->muted) {
|
||||||
|
unlock(output);
|
||||||
|
return de_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALuint bufid;
|
||||||
|
ALint processed, queued;
|
||||||
|
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
||||||
|
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
||||||
|
|
||||||
|
if (audio_state->al_device[output] == NULL || alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
|
unlock(output);
|
||||||
|
return de_AlError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processed) {
|
||||||
|
ALuint *bufids = malloc(processed * sizeof(ALuint));
|
||||||
|
|
||||||
|
if (bufids == NULL) {
|
||||||
|
unlock(output);
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourceUnqueueBuffers(device->source, processed, bufids);
|
||||||
|
alDeleteBuffers(processed - 1, bufids + 1);
|
||||||
|
bufid = bufids[0];
|
||||||
|
free(bufids);
|
||||||
|
} else if (queued < 16) {
|
||||||
|
alGenBuffers(1, &bufid);
|
||||||
|
} else {
|
||||||
|
unlock(output);
|
||||||
|
return de_Busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const bool stereo = channels == 2;
|
||||||
|
alBufferData(bufid, sound_mode(stereo), data,
|
||||||
|
sample_count * sample_size(stereo),
|
||||||
|
sample_rate);
|
||||||
|
alSourceQueueBuffers(device->source, 1, &bufid);
|
||||||
|
|
||||||
|
ALint state;
|
||||||
|
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
|
||||||
|
|
||||||
|
if (state != AL_PLAYING) {
|
||||||
|
alSourcePlay(device->source);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock(output);
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
/* Adapted from qtox,
|
||||||
|
* Copyright © 2014-2019 by The qTox Project Contributors
|
||||||
|
*
|
||||||
|
* return normalized volume of buffer in range 0.0-100.0
|
||||||
|
*/
|
||||||
|
float volume(int16_t *frame, uint32_t samples)
|
||||||
|
{
|
||||||
|
float sum_of_squares = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < samples; i++) {
|
||||||
|
const float sample = (float)(frame[i]) / INT16_MAX;
|
||||||
|
sum_of_squares += powf(sample, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float root_mean_square = sqrtf(sum_of_squares / samples);
|
||||||
|
const float root_two = 1.414213562;
|
||||||
|
|
||||||
|
// normalizedVolume == 1.0 corresponds to a sine wave of maximal amplitude
|
||||||
|
const float normalized_volume = root_mean_square * root_two;
|
||||||
|
|
||||||
|
return 100.0f * fminf(1.0f, normalized_volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time in ms for which we continue to capture audio after VAD is triggered:
|
||||||
|
#define VAD_TIME 250
|
||||||
|
|
||||||
|
#define FRAME_BUF_SIZE 16000
|
||||||
|
|
||||||
|
static void *poll_input(void *arg)
|
||||||
|
{
|
||||||
|
UNUSED_VAR(arg);
|
||||||
|
|
||||||
|
int16_t *frame_buf = malloc(FRAME_BUF_SIZE * sizeof(int16_t));
|
||||||
|
|
||||||
|
if (frame_buf == NULL) {
|
||||||
|
exit_toxic_err("failed in thread_poll", FATALERR_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
lock(input);
|
||||||
|
|
||||||
|
if (!thread_running) {
|
||||||
|
free(frame_buf);
|
||||||
|
unlock(input);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread_paused) {
|
||||||
|
unlock(input);
|
||||||
|
sleep_thread(10000L);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audio_state->al_device[input] != NULL) {
|
||||||
|
int32_t available_samples;
|
||||||
|
alcGetIntegerv(audio_state->al_device[input], ALC_CAPTURE_SAMPLES, sizeof(int32_t), &available_samples);
|
||||||
|
|
||||||
|
const uint32_t f_size = audio_state->capture_frame_info.samples_per_frame;
|
||||||
|
|
||||||
|
if (available_samples >= f_size && f_size <= FRAME_BUF_SIZE) {
|
||||||
|
alcCaptureSamples(audio_state->al_device[input], frame_buf, f_size);
|
||||||
|
|
||||||
|
unlock(input);
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
lock(input);
|
||||||
|
|
||||||
|
float frame_volume = volume(frame_buf, f_size);
|
||||||
|
|
||||||
|
audio_state->input_volume = frame_volume;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||||
|
Device *device = &audio_state->devices[input][i];
|
||||||
|
|
||||||
|
if (device->VAD_threshold != 0.0f) {
|
||||||
|
if (frame_volume >= device->VAD_threshold) {
|
||||||
|
device->VAD_samples_remaining = VAD_TIME * (audio_state->capture_frame_info.sample_rate / 1000);
|
||||||
|
} else if (device->VAD_samples_remaining < f_size) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
device->VAD_samples_remaining -= f_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->active && !device->muted && device->cb) {
|
||||||
|
device->cb(frame_buf, f_size, device->cb_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock(input);
|
||||||
|
sleep_thread(5000L);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float get_input_volume(void)
|
||||||
|
{
|
||||||
|
float ret = 0.0f;
|
||||||
|
|
||||||
|
if (audio_state->al_device[input] != NULL) {
|
||||||
|
lock(input);
|
||||||
|
ret = audio_state->input_volume;
|
||||||
|
unlock(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_al_devices(ToxWindow *self, DeviceType type)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < audio_state->num_al_devices[type]; ++i) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG,
|
||||||
|
audio_state->current_al_device_name[type]
|
||||||
|
&& strcmp(audio_state->current_al_device_name[type], audio_state->al_device_names[type][i]) == 0 ? 1 : 0,
|
||||||
|
0, "%d: %s", i, audio_state->al_device_names[type][i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError selection_valid(DeviceType type, int32_t selection)
|
||||||
|
{
|
||||||
|
return (audio_state->num_al_devices[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/* device.h
|
/* audio_device.h
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
@ -22,19 +22,18 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* You can have multiple sources (Input devices) but only one output device.
|
* You can have multiple sources (Input devices) but only one output device.
|
||||||
* Pass buffers to output device via write();
|
* Pass buffers to output device via write();
|
||||||
* Read from running input device(s) via select()/callback combo.
|
* 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_OPENAL_DEVICES 32
|
||||||
#define MAX_DEVICES 32
|
#define MAX_DEVICES 32
|
||||||
#include <inttypes.h>
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
#define _True 1
|
#include "windows.h"
|
||||||
#define _False 0
|
|
||||||
|
|
||||||
typedef enum DeviceType {
|
typedef enum DeviceType {
|
||||||
input,
|
input,
|
||||||
@ -50,35 +49,49 @@ 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);
|
DeviceError init_devices(void);
|
||||||
DeviceError terminate_devices();
|
|
||||||
|
|
||||||
/* Callback handles ready data from INPUT device */
|
void get_al_device_names(void);
|
||||||
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD);
|
DeviceError terminate_devices(void);
|
||||||
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);
|
||||||
|
|
||||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
bool device_is_muted(DeviceType type, uint32_t device_idx);
|
||||||
|
|
||||||
|
DeviceError device_set_VAD_threshold(uint32_t device_idx, float value);
|
||||||
|
|
||||||
|
float device_get_VAD_threshold(uint32_t device_idx);
|
||||||
|
|
||||||
|
DeviceError set_source_position(uint32_t device_idx, float x, float y, float z);
|
||||||
|
|
||||||
|
DeviceError set_al_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);
|
|
||||||
/* Start device */
|
/* Start device */
|
||||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx);
|
DeviceError open_input_device(uint32_t *device_idx,
|
||||||
|
DataHandleCallback cb, void *cb_data, bool enable_VAD,
|
||||||
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||||
|
DeviceError open_output_device(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 output 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);
|
/* return current input volume as float in range 0.0-100.0 */
|
||||||
|
float get_input_volume(void);
|
||||||
|
|
||||||
|
void print_al_devices(ToxWindow *self, DeviceType type);
|
||||||
|
|
||||||
DeviceError selection_valid(DeviceType type, int32_t selection);
|
DeviceError selection_valid(DeviceType type, int32_t selection);
|
||||||
#endif /* _device_h */
|
#endif /* AUDIO_DEVICE_H */
|
393
src/autocomplete.c
Normal file
393
src/autocomplete.c
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
/* 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 <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/dir.h>
|
||||||
|
#else
|
||||||
|
#include <dirent.h>
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
|
#include "configdir.h"
|
||||||
|
#include "execute.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matches)
|
||||||
|
{
|
||||||
|
if (m) {
|
||||||
|
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n_matches; ++i) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", list[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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, const char **matches, size_t n_items,
|
||||||
|
size_t max_size)
|
||||||
|
{
|
||||||
|
UNUSED_VAR(self);
|
||||||
|
|
||||||
|
if (n_items == 1) {
|
||||||
|
return snprintf(match, match_sz, "%s", matches[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < max_size; ++i) {
|
||||||
|
char ch1 = matches[0][i];
|
||||||
|
|
||||||
|
for (size_t j = 0; j < n_items; ++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 `n_items` strings. Each string in the list must be <= MAX_STR_SIZE.
|
||||||
|
*
|
||||||
|
* dir_search should be true if the line being completed is a file path.
|
||||||
|
*
|
||||||
|
* Returns the difference between the old len and new len of line on success.
|
||||||
|
* Returns -1 on error.
|
||||||
|
*
|
||||||
|
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
|
||||||
|
*/
|
||||||
|
static int complete_line_helper(ToxWindow *self, const char **list, const size_t n_items, bool dir_search)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->len >= MAX_STR_SIZE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* isolate substring from space behind pos to pos */
|
||||||
|
char tmp[MAX_STR_SIZE];
|
||||||
|
memcpy(tmp, ubuf, ctx->pos);
|
||||||
|
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_helper", 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] && !(dir_search && n_items == 1)) {
|
||||||
|
free(sub);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int s_len = strlen(sub);
|
||||||
|
size_t n_matches = 0;
|
||||||
|
|
||||||
|
char **matches = (char **) malloc_ptr_array(n_items, MAX_STR_SIZE);
|
||||||
|
|
||||||
|
if (matches == NULL) {
|
||||||
|
free(sub);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put all list matches in matches array */
|
||||||
|
for (size_t i = 0; i < n_items; ++i) {
|
||||||
|
if (strncasecmp(list[i], sub, s_len) == 0) {
|
||||||
|
snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sub);
|
||||||
|
|
||||||
|
if (!n_matches) {
|
||||||
|
free_ptr_array((void **) matches);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dir_search && n_matches > 1) {
|
||||||
|
print_ac_matches(self, NULL, matches, n_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
char match[MAX_STR_SIZE];
|
||||||
|
size_t match_len = get_str_match(self, match, sizeof(match), (const char **) matches, n_matches, MAX_STR_SIZE);
|
||||||
|
|
||||||
|
free_ptr_array((void **) 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);
|
||||||
|
|
||||||
|
/* If path points to a file with no extension don't append a forward slash */
|
||||||
|
if (dir_search && *endchrs == '/') {
|
||||||
|
const char *path_start = strchr(ubuf + 1, '/');
|
||||||
|
|
||||||
|
if (!path_start) {
|
||||||
|
path_start = strchr(ubuf + 1, ' ');
|
||||||
|
|
||||||
|
if (!path_start) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(path_start) < 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
++path_start;
|
||||||
|
|
||||||
|
if (file_type(path_start) == FILE_TYPE_REGULAR) {
|
||||||
|
endchrs = "";
|
||||||
|
diff -= n_endchrs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(&ubuf[strt + match_len], endchrs);
|
||||||
|
strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
|
||||||
|
|
||||||
|
/* convert to widechar and copy back to original buf */
|
||||||
|
wchar_t newbuf[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wcscpy(ctx->line, newbuf);
|
||||||
|
|
||||||
|
ctx->len += diff;
|
||||||
|
ctx->pos += diff;
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int complete_line(ToxWindow *self, const char **list, size_t n_items)
|
||||||
|
{
|
||||||
|
return complete_line_helper(self, list, n_items, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int complete_path(ToxWindow *self, const char **list, const size_t n_items)
|
||||||
|
{
|
||||||
|
return complete_line_helper(self, list, n_items, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transforms a tab complete starting with the shorthand "~" into the full home directory. */
|
||||||
|
static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
char homedir[MAX_STR_SIZE] = {0};
|
||||||
|
get_home_dir(homedir, sizeof(homedir));
|
||||||
|
|
||||||
|
char newline[MAX_STR_SIZE + 1];
|
||||||
|
snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1);
|
||||||
|
snprintf(path, pathsize, "%s", &newline[cmdlen - 1]);
|
||||||
|
|
||||||
|
wchar_t wline[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return true if the first `p_len` chars in `s` are equal to `p` and `s` is a valid directory name.
|
||||||
|
*/
|
||||||
|
static bool is_partial_match(const char *s, const char *p, size_t p_len)
|
||||||
|
{
|
||||||
|
if (s == NULL || p == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strncmp(s, p, p_len) == 0 && strcmp(".", s) != 0 && strcmp("..", s) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||||
|
* If there is only one match the line is auto-completed.
|
||||||
|
*
|
||||||
|
* Returns the diff between old len and new len of ctx->line on success.
|
||||||
|
* Returns -1 if no matches or more than one match.
|
||||||
|
*/
|
||||||
|
#define MAX_DIRS 75
|
||||||
|
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||||
|
{
|
||||||
|
char b_path[MAX_STR_SIZE + 1];
|
||||||
|
char b_name[MAX_STR_SIZE + 1];
|
||||||
|
char b_cmd[MAX_STR_SIZE];
|
||||||
|
const wchar_t *tmpline = &line[wcslen(cmd) + 1]; /* start after "/command " */
|
||||||
|
|
||||||
|
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path) - 1) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b_path[0] == '~') {
|
||||||
|
complete_home_dir(self, b_path, sizeof(b_path) - 1, 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 */
|
||||||
|
memmove(b_path + 1, b_path, sizeof(b_path) - 1);
|
||||||
|
b_path[0] = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
|
||||||
|
b_path[si + 1] = '\0';
|
||||||
|
size_t b_name_len = strlen(b_name);
|
||||||
|
DIR *dp = opendir(b_path);
|
||||||
|
|
||||||
|
if (dp == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **dirnames = (char **) malloc_ptr_array(MAX_DIRS, NAME_MAX + 1);
|
||||||
|
|
||||||
|
if (dirnames == NULL) {
|
||||||
|
closedir(dp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *entry;
|
||||||
|
|
||||||
|
int dircount = 0;
|
||||||
|
|
||||||
|
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
|
||||||
|
if (is_partial_match(entry->d_name, b_name, b_name_len)) {
|
||||||
|
snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name);
|
||||||
|
++dircount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dp);
|
||||||
|
|
||||||
|
if (dircount == 0) {
|
||||||
|
free_ptr_array((void **) dirnames);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dircount > 1) {
|
||||||
|
qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper);
|
||||||
|
print_ac_matches(self, m, dirnames, dircount);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = complete_path(self, (const char **) dirnames, dircount);
|
||||||
|
|
||||||
|
free_ptr_array((void **) dirnames);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
52
src/autocomplete.h
Normal file
52
src/autocomplete.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
#include "windows.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 `n_items` strings.
|
||||||
|
*
|
||||||
|
* dir_search should be true if the line being completed is a file path.
|
||||||
|
*
|
||||||
|
* Returns the difference between the old len and new len of line on success.
|
||||||
|
* Returns -1 on error.
|
||||||
|
*
|
||||||
|
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
|
||||||
|
*/
|
||||||
|
int complete_line(ToxWindow *self, const char **list, size_t n_items);
|
||||||
|
|
||||||
|
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||||
|
* If there is only one match the line is auto-completed.
|
||||||
|
*
|
||||||
|
* Returns the diff between old len and new len of ctx->line on success.
|
||||||
|
* Returns -1 if no matches or more than one match.
|
||||||
|
*/
|
||||||
|
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
|
||||||
|
|
||||||
|
#endif /* AUTOCOMPLETE_H */
|
262
src/avatars.c
Normal file
262
src/avatars.c
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/* 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "avatars.h"
|
||||||
|
#include "file_transfers.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "misc_tools.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;
|
||||||
|
|
||||||
|
/* Compares the first size bytes of fp to signature.
|
||||||
|
*
|
||||||
|
* Returns 0 if they are the same
|
||||||
|
* Returns 1 if they differ
|
||||||
|
* Returns -1 on error.
|
||||||
|
*
|
||||||
|
* On success this function will seek back to the beginning of fp.
|
||||||
|
*/
|
||||||
|
static int check_file_signature(const unsigned char *signature, size_t size, FILE *fp)
|
||||||
|
{
|
||||||
|
char *buf = malloc(size);
|
||||||
|
|
||||||
|
if (buf == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(buf, size, 1, fp) != 1) {
|
||||||
|
free(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = memcmp(signature, buf, size);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
if (fseek(fp, 0L, SEEK_SET) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret == 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avatar_clear(void)
|
||||||
|
{
|
||||||
|
Avatar = (struct Avatar) {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends avatar to friendnumber.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_send(Tox *m, uint32_t friendnumber)
|
||||||
|
{
|
||||||
|
Tox_Err_File_Send err;
|
||||||
|
uint32_t filenumber = tox_file_send(m, friendnumber, 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 %u (error %d)\n", friendnumber, err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileTransfer *ft = new_file_transfer(NULL, friendnumber, filenumber, 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)
|
||||||
|
{
|
||||||
|
for (size_t 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||||
|
|
||||||
|
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
|
||||||
|
fclose(fp);
|
||||||
|
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_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status)
|
||||||
|
{
|
||||||
|
if (connection_status == TOX_CONNECTION_NONE) {
|
||||||
|
kill_avatar_file_transfers_friend(m, friendnumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = malloc(length);
|
||||||
|
|
||||||
|
if (send_data == NULL) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t send_length = fread(send_data, 1, length, ft->file);
|
||||||
|
|
||||||
|
if (send_length != length) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
free(send_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tox_Err_File_Send_Chunk err;
|
||||||
|
tox_file_send_chunk(m, ft->friendnumber, ft->filenumber, position, send_data, send_length, &err);
|
||||||
|
|
||||||
|
free(send_data);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
55
src/avatars.h
Normal file
55
src/avatars.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
#include "file_transfers.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);
|
||||||
|
void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status);
|
||||||
|
|
||||||
|
#endif /* AVATARS_H */
|
635
src/bootstrap.c
Normal file
635
src/bootstrap.c
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
/* 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 <arpa/inet.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "configdir.h"
|
||||||
|
#include "curl_util.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "prompt.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "windows.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;
|
||||||
|
|
||||||
|
/* Return true if address appears to be a valid ipv4 address. */
|
||||||
|
static 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).
|
||||||
|
*/
|
||||||
|
static bool is_ip6_address(const char *address)
|
||||||
|
{
|
||||||
|
size_t num_colons = 0;
|
||||||
|
char ch = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; (ch = address[i]); ++i) {
|
||||||
|
if (ch == '.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == ':') {
|
||||||
|
++num_colons;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_colons > 1 && num_colons < 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine if a node is offline by comparing the age of the nodeslist
|
||||||
|
* to the last time the node was successfully pinged.
|
||||||
|
*/
|
||||||
|
static bool node_is_offline(unsigned long long int last_ping)
|
||||||
|
{
|
||||||
|
return last_ping + NODE_OFFLINE_TIMOUT <= last_ping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if nodeslist pointed to by fp needs to be updated.
|
||||||
|
* This will be the case if the file is empty, has an invalid format,
|
||||||
|
* or if the file is older than the given timeout.
|
||||||
|
*/
|
||||||
|
static bool nodeslist_needs_update(const char *nodes_path)
|
||||||
|
{
|
||||||
|
if (user_settings->nodeslist_update_freq <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fopen(nodes_path, "r+");
|
||||||
|
|
||||||
|
if (fp == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* last_scan value should be at beginning of file */
|
||||||
|
char line[LAST_SCAN_JSON_KEY_LEN + 32];
|
||||||
|
|
||||||
|
if (fgets(line, sizeof(line), fp) == NULL) {
|
||||||
|
fclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
const char *last_scan_val = strstr(line, LAST_SCAN_JSON_KEY);
|
||||||
|
|
||||||
|
if (last_scan_val == NULL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long int last_scan = strtoll(last_scan_val + LAST_SCAN_JSON_KEY_LEN, NULL, 10);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&thread_data.lock);
|
||||||
|
Nodes.last_updated = last_scan;
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
bool is_timeout = timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
if (is_timeout) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fetches the JSON encoded DHT nodeslist from NODES_LIST_URL.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data)
|
||||||
|
{
|
||||||
|
CURL *c_handle = curl_easy_init();
|
||||||
|
|
||||||
|
if (c_handle == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = -1;
|
||||||
|
|
||||||
|
struct curl_slist *headers = NULL;
|
||||||
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||||
|
headers = curl_slist_append(headers, "charsets: utf-8");
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_URL, NODES_LIST_URL);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L);
|
||||||
|
|
||||||
|
int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
|
||||||
|
|
||||||
|
if (proxy_ret != 0) {
|
||||||
|
fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_perform(c_handle);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
/* If system doesn't support any of the specified ciphers suites, fall back to default */
|
||||||
|
if (ret == CURLE_SSL_CIPHER) {
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
|
||||||
|
ret = curl_easy_perform(c_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
on_exit:
|
||||||
|
curl_slist_free_all(headers);
|
||||||
|
curl_easy_cleanup(c_handle);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempts to update the DHT nodeslist.
|
||||||
|
*
|
||||||
|
* Return 1 if list was updated successfully.
|
||||||
|
* Return 0 if list does not need to be updated.
|
||||||
|
* Return -1 if file cannot be opened.
|
||||||
|
* Return -2 if http lookup failed.
|
||||||
|
* Return -3 if http reponse was empty.
|
||||||
|
* Return -4 if data could not be written to disk.
|
||||||
|
* Return -5 if memory allocation fails.
|
||||||
|
*/
|
||||||
|
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 = calloc(1, sizeof(struct Recv_Curl_Data));
|
||||||
|
|
||||||
|
if (recv_data == NULL) {
|
||||||
|
fclose(fp);
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curl_fetch_nodes_JSON(recv_data) == -1) {
|
||||||
|
free(recv_data);
|
||||||
|
fclose(fp);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recv_data->length == 0) {
|
||||||
|
free(recv_data);
|
||||||
|
fclose(fp);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(recv_data->data, recv_data->length, 1, fp) != 1) {
|
||||||
|
free(recv_data);
|
||||||
|
fclose(fp);
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(recv_data);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_nodeslist_path(char *buf, size_t buf_size)
|
||||||
|
{
|
||||||
|
char *config_dir = NULL;
|
||||||
|
|
||||||
|
if (arg_opts.nodes_path[0]) {
|
||||||
|
snprintf(buf, buf_size, "%s", arg_opts.nodes_path);
|
||||||
|
} else if ((config_dir = get_user_config_dir()) != NULL) {
|
||||||
|
snprintf(buf, buf_size, "%s%s%s", config_dir, CONFIGDIR, DEFAULT_NODES_FILENAME);
|
||||||
|
free(config_dir);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, buf_size, "%s", DEFAULT_NODES_FILENAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if json encoded string s contains a valid IP address and puts address in ip_buf.
|
||||||
|
*
|
||||||
|
* ip_type should be set to 1 for ipv4 address, or 0 for ipv6 addresses.
|
||||||
|
* ip_buf must have room for at least IP_MAX_SIZE + 1 bytes.
|
||||||
|
*/
|
||||||
|
static bool extract_val_ip(const char *s, char *ip_buf, unsigned short int ip_type)
|
||||||
|
{
|
||||||
|
int ip_len = char_find(0, s, '"');
|
||||||
|
|
||||||
|
if (ip_len < IP_MIN_SIZE || ip_len > IP_MAX_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ip_buf, s, ip_len);
|
||||||
|
ip_buf[ip_len] = 0;
|
||||||
|
|
||||||
|
return (ip_type == 1) ? is_ip4_address(ip_buf) : is_ip6_address(ip_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extracts the port from json encoded string s.
|
||||||
|
*
|
||||||
|
* Return port number on success.
|
||||||
|
* Return 0 on failure.
|
||||||
|
*/
|
||||||
|
static uint16_t extract_val_port(const char *s)
|
||||||
|
{
|
||||||
|
long int port = strtol(s, NULL, 10);
|
||||||
|
return (port > 0 && port <= MAX_PORT_RANGE) ? port : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extracts the last pinged value from json encoded string s.
|
||||||
|
*
|
||||||
|
* Return timestamp on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
static long long int extract_val_last_pinged(const char *s)
|
||||||
|
{
|
||||||
|
long long int last_pinged = strtoll(s, NULL, 10);
|
||||||
|
return (last_pinged <= 0) ? -1 : last_pinged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extracts DHT public key from json encoded string s and puts key in key_buf.
|
||||||
|
* key_buf must have room for at least TOX_PUBLIC_KEY_SIZE * 2 + 1 bytes.
|
||||||
|
*
|
||||||
|
* Return number of bytes copied to key_buf on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
static int extract_val_pk(const char *s, char *key_buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
int key_len = char_find(0, s, '"');
|
||||||
|
|
||||||
|
if (key_len != TOX_PUBLIC_KEY_SIZE * 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(key_buf, s, key_len);
|
||||||
|
key_buf[key_len] = 0;
|
||||||
|
|
||||||
|
return key_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extracts values from json formatted string, validats them, and puts them in node.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if line is empty.
|
||||||
|
* Return -2 if line does not appear to be a valid nodes list entry.
|
||||||
|
* Return -3 if node appears to be offline.
|
||||||
|
* Return -4 if entry does not contain either a valid ipv4 or ipv6 address.
|
||||||
|
* Return -5 if port value is invalid.
|
||||||
|
* Return -6 if public key is invalid.
|
||||||
|
*/
|
||||||
|
static int extract_node(const char *line, struct Node *node)
|
||||||
|
{
|
||||||
|
if (!line) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ip4_start = strstr(line, IPV4_JSON_KEY);
|
||||||
|
const char *ip6_start = strstr(line, IPV6_JSON_KEY);
|
||||||
|
const char *port_start = strstr(line, PORT_JSON_KEY);
|
||||||
|
const char *key_start = strstr(line, PK_JSON_KEY);
|
||||||
|
const char *last_pinged_str = strstr(line, LAST_PING_JSON_KEY);
|
||||||
|
|
||||||
|
if (!ip4_start || !ip6_start || !port_start || !key_start || !last_pinged_str) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN);
|
||||||
|
|
||||||
|
if (last_pinged <= 0 || node_is_offline(last_pinged)) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
char ip4_string[IP_MAX_SIZE + 1];
|
||||||
|
bool have_ip4 = extract_val_ip(ip4_start + IPV4_JSON_KEY_LEN, ip4_string, 1);
|
||||||
|
|
||||||
|
char ip6_string[IP_MAX_SIZE + 1];
|
||||||
|
bool have_ip6 = extract_val_ip(ip6_start + IPV6_JSON_KEY_LEN, ip6_string, 0);
|
||||||
|
|
||||||
|
if (!have_ip6 && !have_ip4) {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t port = extract_val_port(port_start + PORT_JSON_KEY_LEN);
|
||||||
|
|
||||||
|
if (port == 0) {
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||||
|
int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string);
|
||||||
|
|
||||||
|
if (key_len == -1) {
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) {
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_ip4) {
|
||||||
|
snprintf(node->ip4, sizeof(node->ip4), "%s", ip4_string);
|
||||||
|
node->have_ip4 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_ip6) {
|
||||||
|
snprintf(node->ip6, sizeof(node->ip6), "%s", ip6_string);
|
||||||
|
node->have_ip6 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->port = port;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loads the DHT nodeslist to memory from json encoded nodes file. */
|
||||||
|
void *load_nodeslist_thread(void *data)
|
||||||
|
{
|
||||||
|
UNUSED_VAR(data);
|
||||||
|
|
||||||
|
char nodes_path[PATH_MAX];
|
||||||
|
get_nodeslist_path(nodes_path, sizeof(nodes_path));
|
||||||
|
|
||||||
|
FILE *fp = NULL;
|
||||||
|
|
||||||
|
if (!file_exists(nodes_path)) {
|
||||||
|
if ((fp = fopen(nodes_path, "w+")) == NULL) {
|
||||||
|
fprintf(stderr, "nodeslist load error: failed to create file '%s'\n", nodes_path);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
} else if ((fp = fopen(nodes_path, "r+")) == NULL) {
|
||||||
|
fprintf(stderr, "nodeslist load error: failed to open file '%s'\n", nodes_path);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int update_err = update_DHT_nodeslist(nodes_path);
|
||||||
|
|
||||||
|
if (update_err < 0) {
|
||||||
|
fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[MAX_NODELIST_SIZE + 1];
|
||||||
|
|
||||||
|
if (fgets(line, sizeof(line), fp) == NULL) {
|
||||||
|
fclose(fp);
|
||||||
|
fprintf(stderr, "nodeslist load error: file empty.\n");
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t idx = 0;
|
||||||
|
const char *line_start = line;
|
||||||
|
|
||||||
|
while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY))) {
|
||||||
|
pthread_mutex_lock(&thread_data.lock);
|
||||||
|
idx = Nodes.count;
|
||||||
|
|
||||||
|
if (idx >= MAX_NODES) {
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extract_node(line_start, &Nodes.list[idx]) == 0) {
|
||||||
|
++Nodes.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If nodeslist does not contain any valid entries we set the last_scan value
|
||||||
|
* to 0 so that it will fetch a new list the next time this function is called.
|
||||||
|
*/
|
||||||
|
if (Nodes.count == 0) {
|
||||||
|
const char *s = "{\"last_scan\":0}";
|
||||||
|
rewind(fp);
|
||||||
|
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
|
||||||
|
fclose(fp);
|
||||||
|
fprintf(stderr, "nodeslist load error: List did not contain any valid entries.\n");
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
on_exit:
|
||||||
|
thread_data.active = false;
|
||||||
|
pthread_attr_destroy(&thread_data.attr);
|
||||||
|
pthread_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Creates a new thread that will load the DHT nodeslist to memory
|
||||||
|
* from json encoded nodes file obtained at NODES_LIST_URL. Only one
|
||||||
|
* thread may run at a time.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if a thread is already active.
|
||||||
|
* Return -2 if mutex fails to init.
|
||||||
|
* Return -3 if pthread attribute fails to init.
|
||||||
|
* Return -4 if pthread fails to set detached state.
|
||||||
|
* Return -5 if thread creation fails.
|
||||||
|
*/
|
||||||
|
int load_DHT_nodeslist(void)
|
||||||
|
{
|
||||||
|
if (thread_data.active) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&thread_data.lock, NULL) != 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_attr_init(&thread_data.attr) != 0) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_data.active = true;
|
||||||
|
|
||||||
|
if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) {
|
||||||
|
thread_data.active = false;
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */
|
||||||
|
static void DHT_bootstrap(Tox *m)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&thread_data.lock);
|
||||||
|
size_t num_nodes = Nodes.count;
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
|
||||||
|
if (num_nodes == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&thread_data.lock);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) {
|
||||||
|
struct Node *node = &Nodes.list[rand() % Nodes.count];
|
||||||
|
const char *addr = node->have_ip4 ? node->ip4 : node->ip6;
|
||||||
|
|
||||||
|
if (!addr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tox_Err_Bootstrap err;
|
||||||
|
tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
fprintf(stderr, "Failed to bootstrap %s:%d\n", addr, node->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_add_tcp_relay(m, addr, node->port, (uint8_t *) node->key, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&thread_data.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manages connection to the Tox DHT network. */
|
||||||
|
void do_tox_connection(Tox *m)
|
||||||
|
{
|
||||||
|
static time_t last_bootstrap_time = 0;
|
||||||
|
bool connected = prompt_selfConnectionStatus() != TOX_CONNECTION_NONE;
|
||||||
|
|
||||||
|
if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) {
|
||||||
|
DHT_bootstrap(m);
|
||||||
|
last_bootstrap_time = get_unix_time();
|
||||||
|
}
|
||||||
|
}
|
42
src/bootstrap.h
Normal file
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 */
|
1602
src/chat.c
1602
src/chat.c
File diff suppressed because it is too large
Load Diff
15
src/chat.h
15
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 "toxic.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
void kill_chat_window(ToxWindow *self);
|
/* set CTRL to -1 if we don't want to send a control signal.
|
||||||
ToxWindow new_chat(Tox *m, int32_t friendnum);
|
set msg to NULL if we don't want to display a message */
|
||||||
|
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
|
||||||
|
void kill_chat_window(ToxWindow *self, Tox *m);
|
||||||
|
ToxWindow *new_chat(Tox *m, int32_t friendnum);
|
||||||
|
|
||||||
#endif /* end of include guard: CHAT_H_6489PZ13 */
|
#endif /* end of include guard: CHAT_H */
|
||||||
|
@ -23,208 +23,322 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "chat.h"
|
||||||
|
#include "conference.h"
|
||||||
|
#include "execute.h"
|
||||||
|
#include "file_transfers.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "misc_tools.h"
|
|
||||||
#include "friendlist.h"
|
|
||||||
#include "execute.h"
|
|
||||||
#include "line_info.h"
|
|
||||||
#include "groupchat.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;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 2) {
|
||||||
errmsg = "Invalid syntax";
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
|
||||||
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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ft) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
line_info_add(self, false, 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_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
|
if (argc < 1) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long int conferencenum = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
|
if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tox_Err_Conference_Invite err;
|
||||||
|
|
||||||
|
if (!tox_conference_invite(m, self->num, conferencenum, &err)) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
UNUSED_VAR(argc);
|
||||||
|
UNUSED_VAR(argv);
|
||||||
|
|
||||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
errmsg = " * Warning: Too many windows are open.";
|
line_info_add(self, false, 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 *conferencekey = Friends.list[self->num].conference_invite.key;
|
||||||
|
uint16_t length = Friends.list[self->num].conference_invite.length;
|
||||||
|
uint8_t type = Friends.list[self->num].conference_invite.type;
|
||||||
|
|
||||||
if (!friends[self->num].groupchat_pending) {
|
if (!Friends.list[self->num].conference_invite.pending) {
|
||||||
errmsg = "No pending group chat invite.";
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference 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);
|
uint32_t conferencenum;
|
||||||
|
|
||||||
if (groupnum == -1) {
|
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
||||||
errmsg = "Group chat instance failed to initialize.";
|
Tox_Err_Conference_Join err;
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||||
|
#ifdef AUDIO
|
||||||
|
conferencenum = toxav_join_av_groupchat(m, self->num, (const uint8_t *) conferencekey, length,
|
||||||
|
audio_conference_callback, NULL);
|
||||||
|
|
||||||
|
if (conferencenum == (uint32_t) -1) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
|
||||||
errmsg = "Group chat window failed to initialize.";
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
tox_conference_delete(m, conferencenum, NULL);
|
||||||
tox_del_groupchat(m, groupnum);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
|
||||||
|
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||||
|
if (!init_conference_audio_input(m, conferencenum)) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
if (argc != 1) {
|
if (argc < 1) {
|
||||||
errmsg = "Invalid syntax.";
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
|
||||||
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, false, 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, false, 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, false, 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 download 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->filenumber, TOX_FILE_CONTROL_RESUME, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_FILE_CONTROL_OK) {
|
||||||
|
goto on_recv_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] as: '%s'", idx, ft->file_path);
|
||||||
|
|
||||||
|
/* prep progress bar line */
|
||||||
|
char progline[MAX_STR_SIZE];
|
||||||
|
init_progress_bar(progline);
|
||||||
|
line_info_add(self, false, 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, false, 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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case TOX_ERR_FILE_CONTROL_SENDQ:
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
line_info_add(self, false, 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;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
if (max_file_senders_index >= (MAX_FILES - 1)) {
|
const char *errmsg = NULL;
|
||||||
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, false, 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];
|
char path[MAX_STR_SIZE];
|
||||||
|
snprintf(path, sizeof(path), "%s", argv[1]);
|
||||||
|
int path_len = strlen(path);
|
||||||
|
|
||||||
if (path[0] != '\"') {
|
if (path_len >= MAX_STR_SIZE) {
|
||||||
errmsg = "File path must be enclosed in quotes.";
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
++path;
|
|
||||||
int path_len = strlen(path) - 1;
|
|
||||||
path[path_len] = '\0';
|
|
||||||
|
|
||||||
if (path_len > MAX_STR_SIZE) {
|
|
||||||
errmsg = "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, false, 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, false, 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)
|
|
||||||
++max_file_senders_index;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
|
||||||
|
|
||||||
|
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, false, 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, false, 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 "toxic.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_conference_invite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_conference_join(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,13 @@ 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 */
|
void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
#endif /* #define _chat_commands_h */
|
#ifdef VIDEO
|
||||||
|
void cmd_vcall(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_res(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
#endif /* CHAT_COMMANDS_H */
|
||||||
|
1418
src/conference.c
Normal file
1418
src/conference.c
Normal file
File diff suppressed because it is too large
Load Diff
118
src/conference.h
Normal file
118
src/conference.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/* conference.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 CONFERENCE_H
|
||||||
|
#define CONFERENCE_H
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
#define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH
|
||||||
|
#define SIDEBAR_WIDTH 16
|
||||||
|
|
||||||
|
typedef struct ConferencePeer {
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
uint8_t pubkey[TOX_PUBLIC_KEY_SIZE];
|
||||||
|
uint32_t peernum; /* index in chat->peer_list */
|
||||||
|
|
||||||
|
char name[TOX_MAX_NAME_LENGTH];
|
||||||
|
size_t name_length;
|
||||||
|
|
||||||
|
bool sending_audio;
|
||||||
|
uint32_t audio_out_idx;
|
||||||
|
time_t last_audio_time;
|
||||||
|
} ConferencePeer;
|
||||||
|
|
||||||
|
typedef struct AudioInputCallbackData {
|
||||||
|
Tox *tox;
|
||||||
|
uint32_t conferencenum;
|
||||||
|
} AudioInputCallbackData;
|
||||||
|
|
||||||
|
#define PUBKEY_STRING_SIZE (2 * TOX_PUBLIC_KEY_SIZE + 1)
|
||||||
|
typedef struct NameListEntry {
|
||||||
|
char name[TOX_MAX_NAME_LENGTH];
|
||||||
|
char pubkey_str[PUBKEY_STRING_SIZE];
|
||||||
|
uint32_t peernum;
|
||||||
|
} NameListEntry;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int chatwin;
|
||||||
|
bool active;
|
||||||
|
uint8_t type;
|
||||||
|
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||||
|
time_t start_time;
|
||||||
|
|
||||||
|
char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
|
||||||
|
size_t title_length;
|
||||||
|
|
||||||
|
ConferencePeer *peer_list;
|
||||||
|
uint32_t max_idx;
|
||||||
|
|
||||||
|
NameListEntry *name_list;
|
||||||
|
uint32_t num_peers;
|
||||||
|
|
||||||
|
bool push_to_talk_enabled;
|
||||||
|
time_t ptt_last_pushed;
|
||||||
|
|
||||||
|
bool audio_enabled;
|
||||||
|
time_t last_sent_audio;
|
||||||
|
uint32_t audio_in_idx;
|
||||||
|
AudioInputCallbackData audio_input_callback_data;
|
||||||
|
} ConferenceChat;
|
||||||
|
|
||||||
|
/* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */
|
||||||
|
void free_conference(ToxWindow *self, uint32_t conferencenum);
|
||||||
|
|
||||||
|
int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length);
|
||||||
|
|
||||||
|
/* destroys and re-creates conference window with or without the peerlist */
|
||||||
|
void redraw_conference_win(ToxWindow *self);
|
||||||
|
|
||||||
|
void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length);
|
||||||
|
void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title);
|
||||||
|
int conference_enable_logging(ToxWindow *self, Tox *m, uint32_t conferencenum, struct chatlog *log);
|
||||||
|
|
||||||
|
/* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum
|
||||||
|
* of `maxpeers`.
|
||||||
|
* Maches each peer whose name or pubkey begins with `prefix`.
|
||||||
|
* If `prefix` is exactly the pubkey of a peer, matches only that peer.
|
||||||
|
* return number of entries placed in `entries`.
|
||||||
|
*/
|
||||||
|
uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries,
|
||||||
|
uint32_t maxpeers);
|
||||||
|
|
||||||
|
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum);
|
||||||
|
bool enable_conference_audio(Tox *tox, uint32_t conferencenum);
|
||||||
|
bool disable_conference_audio(Tox *tox, uint32_t conferencenum);
|
||||||
|
bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled);
|
||||||
|
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum,
|
||||||
|
const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
|
||||||
|
sample_rate, void *userdata);
|
||||||
|
|
||||||
|
bool conference_mute_self(uint32_t conferencenum);
|
||||||
|
bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum);
|
||||||
|
bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold);
|
||||||
|
float conference_get_VAD_threshold(uint32_t conferencenum);
|
||||||
|
|
||||||
|
#endif /* CONFERENCE_H */
|
210
src/conference_commands.c
Normal file
210
src/conference_commands.c
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/* conference_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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "conference.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
static void print_err(ToxWindow *self, const char *error_str)
|
||||||
|
{
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
|
Tox_Err_Conference_Title err;
|
||||||
|
char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
|
||||||
|
|
||||||
|
if (argc < 1) {
|
||||||
|
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) {
|
||||||
|
print_err(self, "Title is not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
|
||||||
|
print_err(self, "Title is not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
title[tlen] = '\0';
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(argv[1]);
|
||||||
|
|
||||||
|
if (len >= sizeof(title)) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(title, sizeof(title), "%s", argv[1]);
|
||||||
|
|
||||||
|
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conference_rename_log_path(m, self->num, title); // must be called first
|
||||||
|
|
||||||
|
conference_set_title(self, self->num, title, len);
|
||||||
|
|
||||||
|
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||||
|
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, true, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title);
|
||||||
|
|
||||||
|
char tmp_event[MAX_STR_SIZE + 20];
|
||||||
|
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||||
|
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
|
bool enable;
|
||||||
|
|
||||||
|
if (argc == 1 && !strcasecmp(argv[1], "on")) {
|
||||||
|
enable = true;
|
||||||
|
} else if (argc == 1 && !strcasecmp(argv[1], "off")) {
|
||||||
|
enable = false;
|
||||||
|
} else {
|
||||||
|
print_err(self, "Please specify: on | off");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) {
|
||||||
|
print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk."
|
||||||
|
: "Disabled conference audio");
|
||||||
|
} else {
|
||||||
|
print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
|
if (argc < 1) {
|
||||||
|
if (conference_mute_self(self->num)) {
|
||||||
|
print_err(self, "Toggled self audio mute status");
|
||||||
|
} else {
|
||||||
|
print_err(self, "No audio input to mute");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NameListEntry *entries[16];
|
||||||
|
uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16);
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
print_err(self, "No such peer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 1) {
|
||||||
|
print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):");
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < n; ++i) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conference_mute_peer(m, self->num, entries[0]->peernum)) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name);
|
||||||
|
} else {
|
||||||
|
print_err(self, "Peer is not on the call");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
UNUSED_VAR(m);
|
||||||
|
|
||||||
|
if (argc == 0) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f",
|
||||||
|
(double) conference_get_VAD_threshold(self->num));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
print_err(self, "Only one argument allowed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
float value = strtof(argv[1], &end);
|
||||||
|
|
||||||
|
if (*end) {
|
||||||
|
print_err(self, "Invalid input");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conference_set_VAD_threshold(self->num, value)) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value);
|
||||||
|
} else {
|
||||||
|
print_err(self, "Failed to set conference audio input sensitivity.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
UNUSED_VAR(m);
|
||||||
|
|
||||||
|
bool enable;
|
||||||
|
|
||||||
|
if (argc == 1 && !strcasecmp(argv[1], "on")) {
|
||||||
|
enable = true;
|
||||||
|
} else if (argc == 1 && !strcasecmp(argv[1], "off")) {
|
||||||
|
enable = false;
|
||||||
|
} else {
|
||||||
|
print_err(self, "Please specify: on | off");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!toggle_conference_push_to_talk(self->num, enable)) {
|
||||||
|
print_err(self, "Failed to toggle push to talk.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled");
|
||||||
|
}
|
||||||
|
#endif /* AUDIO */
|
@ -1,4 +1,4 @@
|
|||||||
/* file_senders.h
|
/* conference_commands.h
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
@ -20,32 +20,16 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _filesenders_h
|
#ifndef CONFERENCE_COMMANDS_H
|
||||||
#define _filesenders_h
|
#define CONFERENCE_COMMANDS_H
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
|
||||||
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
|
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
#define MAX_FILES 255
|
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
#define TIMEOUT_FILESENDER 120
|
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
|
||||||
typedef struct {
|
#endif /* CONFERENCE_COMMANDS_H */
|
||||||
FILE *file;
|
|
||||||
ToxWindow *toxwin;
|
|
||||||
int32_t friendnum;
|
|
||||||
bool active;
|
|
||||||
int filenum;
|
|
||||||
char nextpiece[FILE_PIECE_SIZE];
|
|
||||||
uint16_t piecelen;
|
|
||||||
char pathname[MAX_STR_SIZE];
|
|
||||||
uint64_t timestamp;
|
|
||||||
uint64_t last_progress;
|
|
||||||
uint64_t size;
|
|
||||||
uint32_t line_id;
|
|
||||||
} FileSender;
|
|
||||||
|
|
||||||
void close_all_file_senders(Tox *m);
|
|
||||||
void do_file_senders(Tox *m);
|
|
||||||
|
|
||||||
#endif /* #define _filesenders_h */
|
|
111
src/configdir.c
111
src/configdir.c
@ -20,54 +20,59 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "configdir.h"
|
#include "configdir.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "toxic.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;
|
||||||
@ -80,9 +85,9 @@ char *get_user_config_dir(void)
|
|||||||
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);
|
||||||
|
|
||||||
@ -98,34 +103,52 @@ 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 /* CONFIGDIR_H */
|
||||||
|
94
src/curl_util.c
Normal file
94
src/curl_util.c
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/* curl_util.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "curl_util.h"
|
||||||
|
|
||||||
|
/* Sets proxy info for given CURL handler.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or if no proxy is set by the client.
|
||||||
|
* Returns -1 if proxy info is invalid.
|
||||||
|
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
|
||||||
|
*/
|
||||||
|
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
|
||||||
|
{
|
||||||
|
if (proxy_type == TOX_PROXY_TYPE_NONE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proxy_address == NULL || port == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback function for CURL to write received data.
|
||||||
|
*
|
||||||
|
* This function will append data from an http request to the data buffer
|
||||||
|
* until the request is complete or the buffer is full. Buffer will be null terminated.
|
||||||
|
*
|
||||||
|
* Returns number of bytes received from http request on success (don't change this).
|
||||||
|
* Returns 0 if data exceeds buffer size.
|
||||||
|
*/
|
||||||
|
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer)
|
||||||
|
{
|
||||||
|
struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer;
|
||||||
|
|
||||||
|
size_t length = size * nmemb;
|
||||||
|
size_t total_size = length + recv_data->length;
|
||||||
|
|
||||||
|
if (total_size > MAX_RECV_CURL_DATA_SIZE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(recv_data->data + recv_data->length, data, length);
|
||||||
|
recv_data->data[total_size] = '\0';
|
||||||
|
recv_data->length += length;
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
57
src/curl_util.h
Normal file
57
src/curl_util.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* List based on Mozilla's recommended configurations for modern browsers */
|
||||||
|
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
|
||||||
|
|
||||||
|
/* Max size of an http response that we can store in Recv_Data */
|
||||||
|
#define MAX_RECV_CURL_DATA_SIZE 32767
|
||||||
|
|
||||||
|
/* Holds data received from curl lookup */
|
||||||
|
struct Recv_Curl_Data {
|
||||||
|
char data[MAX_RECV_CURL_DATA_SIZE + 1]; /* Data received from curl write data callback */
|
||||||
|
size_t length; /* Total number of bytes written to data buffer (doesn't include null) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Sets proxy info for given CURL handler.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or if no proxy is set by the client.
|
||||||
|
* Returns -1 if proxy info is invalid.
|
||||||
|
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
|
||||||
|
*/
|
||||||
|
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type);
|
||||||
|
|
||||||
|
/* Callback function for CURL to write received data.
|
||||||
|
*
|
||||||
|
* This function will append data from an http request to the data buffer
|
||||||
|
* until the request is complete or the buffer is full. Buffer will be null terminated.
|
||||||
|
*
|
||||||
|
* Returns size of bytes written to the data buffer.
|
||||||
|
*/
|
||||||
|
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer);
|
||||||
|
|
||||||
|
#endif /* CURL_UTIL_H */
|
432
src/device.c
432
src/device.c
@ -1,432 +0,0 @@
|
|||||||
/* device.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "audio_call.h"
|
|
||||||
#include "line_info.h"
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <OpenAL/al.h>
|
|
||||||
#include <OpenAL/alc.h>
|
|
||||||
#else
|
|
||||||
#include <AL/al.h>
|
|
||||||
#include <AL/alc.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <tox/toxav.h>
|
|
||||||
|
|
||||||
#define openal_bufs 5
|
|
||||||
#define sample_rate 48000
|
|
||||||
#define inline__ inline __attribute__((always_inline))
|
|
||||||
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
|
|
||||||
|
|
||||||
typedef struct _Device {
|
|
||||||
ALCdevice *dhndl; /* Handle of device selected/opened */
|
|
||||||
ALCcontext *ctx; /* Device context */
|
|
||||||
DataHandleCallback cb; /* Use this to handle data from input device usually */
|
|
||||||
void* cb_data; /* Data to be passed to callback */
|
|
||||||
int32_t call_idx; /* ToxAv call index */
|
|
||||||
|
|
||||||
uint32_t source, buffers[openal_bufs]; /* Playback source/buffers */
|
|
||||||
size_t ref_count;
|
|
||||||
int32_t selection;
|
|
||||||
_Bool enable_VAD;
|
|
||||||
_Bool muted;
|
|
||||||
float VAD_treshold; /* 40 is usually recommended value */
|
|
||||||
pthread_mutex_t mutex[1];
|
|
||||||
} Device;
|
|
||||||
|
|
||||||
const char *ddevice_names[2]; /* Default device */
|
|
||||||
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
|
|
||||||
static int size[2]; /* Size of above containers */
|
|
||||||
Device *running[2][MAX_DEVICES]; /* Running devices */
|
|
||||||
uint32_t primary_device[2]; /* Primary device */
|
|
||||||
|
|
||||||
static ToxAv* av = NULL;
|
|
||||||
|
|
||||||
/* q_mutex */
|
|
||||||
#define lock pthread_mutex_lock(&mutex)
|
|
||||||
#define unlock pthread_mutex_unlock(&mutex)
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
|
|
||||||
|
|
||||||
_Bool thread_running = _True,
|
|
||||||
thread_paused = _True; /* Thread control */
|
|
||||||
|
|
||||||
void* thread_poll(void*);
|
|
||||||
/* Meet devices */
|
|
||||||
DeviceError init_devices(ToxAv* av_)
|
|
||||||
{
|
|
||||||
const char *stringed_device_list;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
size[input] = 0;
|
|
||||||
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
|
|
||||||
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
|
||||||
|
|
||||||
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
|
|
||||||
devices_names[input][size[input]] = stringed_device_list;
|
|
||||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
size[output] = 0;
|
|
||||||
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
|
|
||||||
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
|
||||||
|
|
||||||
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
|
|
||||||
devices_names[output][size[output]] = stringed_device_list;
|
|
||||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start poll thread
|
|
||||||
|
|
||||||
pthread_mutex_init(&mutex, NULL);
|
|
||||||
|
|
||||||
pthread_t thread_id;
|
|
||||||
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
|
|
||||||
return de_InternalError;
|
|
||||||
|
|
||||||
av = av_;
|
|
||||||
|
|
||||||
return (DeviceError) ae_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError terminate_devices()
|
|
||||||
{
|
|
||||||
/* Cleanup if needed */
|
|
||||||
thread_running = false;
|
|
||||||
usleep(20000);
|
|
||||||
|
|
||||||
pthread_mutex_destroy(&mutex);
|
|
||||||
|
|
||||||
return (DeviceError) ae_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
|
||||||
{
|
|
||||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
|
||||||
lock;
|
|
||||||
|
|
||||||
Device* device = running[type][device_idx];
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
unlock;
|
|
||||||
return de_DeviceNotActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
device->muted = !device->muted;
|
|
||||||
|
|
||||||
unlock;
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
|
||||||
{
|
|
||||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
|
||||||
lock;
|
|
||||||
|
|
||||||
Device* device = running[input][device_idx];
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
unlock;
|
|
||||||
return de_DeviceNotActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
device->VAD_treshold = value;
|
|
||||||
|
|
||||||
unlock;
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError set_primary_device(DeviceType type, int32_t selection)
|
|
||||||
{
|
|
||||||
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
|
||||||
primary_device[type] = selection;
|
|
||||||
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx)
|
|
||||||
{
|
|
||||||
return open_device(type, primary_device[type], device_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: generate buffers separately
|
|
||||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx)
|
|
||||||
{
|
|
||||||
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
|
||||||
|
|
||||||
lock;
|
|
||||||
|
|
||||||
uint32_t i;
|
|
||||||
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; i ++);
|
|
||||||
|
|
||||||
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
|
|
||||||
else *device_idx = i;
|
|
||||||
|
|
||||||
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));;
|
|
||||||
device->selection = selection;
|
|
||||||
|
|
||||||
for (i = 0; i < *device_idx; i ++) { /* Check if any previous has the same selection */
|
|
||||||
if ( running[type][i]->selection == selection ) {
|
|
||||||
device->dhndl = running[type][i]->dhndl;
|
|
||||||
if (type == output) {
|
|
||||||
device->ctx = running[type][i]->ctx;
|
|
||||||
memcpy(device->buffers, running[type][i]->buffers, sizeof(running[type][i]->buffers));
|
|
||||||
device->source = running[type][i]->source;
|
|
||||||
}
|
|
||||||
device->ref_count++;
|
|
||||||
pthread_mutex_init(device->mutex, NULL);
|
|
||||||
unlock;
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == input) {
|
|
||||||
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
|
|
||||||
av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16, frame_size * 2);
|
|
||||||
device->VAD_treshold = VAD_THRESHOLD_DEFAULT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
device->dhndl = alcOpenDevice(devices_names[type][selection]);
|
|
||||||
if ( !device->dhndl ) {
|
|
||||||
free(device);
|
|
||||||
running[type][*device_idx] = NULL;
|
|
||||||
unlock;
|
|
||||||
return de_FailedStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
device->ctx = alcCreateContext(device->dhndl, NULL);
|
|
||||||
alcMakeContextCurrent(device->ctx);
|
|
||||||
|
|
||||||
alGenBuffers(openal_bufs, device->buffers);
|
|
||||||
alGenSources((uint32_t)1, &device->source);
|
|
||||||
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
|
||||||
|
|
||||||
uint16_t zeros[frame_size];
|
|
||||||
memset(zeros, 0, frame_size*2);
|
|
||||||
|
|
||||||
for ( i =0; i < openal_bufs; ++i) {
|
|
||||||
alBufferData(device->buffers[i], AL_FORMAT_MONO16, zeros, frame_size*2, sample_rate);
|
|
||||||
}
|
|
||||||
|
|
||||||
alSourceQueueBuffers(device->source, openal_bufs, device->buffers);
|
|
||||||
alSourcePlay(device->source);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alcGetError(device->dhndl) != AL_NO_ERROR) {
|
|
||||||
free(device);
|
|
||||||
running[type][*device_idx] = NULL;
|
|
||||||
unlock;
|
|
||||||
return de_FailedStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == input) {
|
|
||||||
alcCaptureStart(device->dhndl);
|
|
||||||
thread_paused = _False;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_init(device->mutex, NULL);
|
|
||||||
unlock;
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError close_device(DeviceType type, uint32_t device_idx)
|
|
||||||
{
|
|
||||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
|
||||||
|
|
||||||
lock;
|
|
||||||
Device* device = running[type][device_idx];
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
unlock;
|
|
||||||
return de_DeviceNotActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !(device->ref_count--) ) {
|
|
||||||
running[type][device_idx] = NULL;
|
|
||||||
unlock;
|
|
||||||
|
|
||||||
DeviceError rc = de_None;
|
|
||||||
|
|
||||||
if (type == input) {
|
|
||||||
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
|
|
||||||
|
|
||||||
alDeleteSources(1, &device->source);
|
|
||||||
alDeleteBuffers(openal_bufs, device->buffers);
|
|
||||||
|
|
||||||
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
|
|
||||||
alcMakeContextCurrent(NULL);
|
|
||||||
if ( device->ctx ) alcDestroyContext(device->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(device);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock;
|
|
||||||
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD)
|
|
||||||
{
|
|
||||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
|
||||||
return de_InvalidSelection;
|
|
||||||
|
|
||||||
lock;
|
|
||||||
running[input][device_idx]->cb = callback;
|
|
||||||
running[input][device_idx]->cb_data = data;
|
|
||||||
running[input][device_idx]->enable_VAD = enable_VAD;
|
|
||||||
running[input][device_idx]->call_idx = call_idx;
|
|
||||||
unlock;
|
|
||||||
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels)
|
|
||||||
{
|
|
||||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
|
||||||
|
|
||||||
Device* device = running[output][device_idx];
|
|
||||||
|
|
||||||
if (!device || device->muted) return de_DeviceNotActive;
|
|
||||||
|
|
||||||
pthread_mutex_lock(device->mutex);
|
|
||||||
|
|
||||||
|
|
||||||
ALuint bufid;
|
|
||||||
ALint processed, queued;
|
|
||||||
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
|
||||||
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
|
||||||
|
|
||||||
if(processed) {
|
|
||||||
ALuint bufids[processed];
|
|
||||||
alSourceUnqueueBuffers(device->source, processed, bufids);
|
|
||||||
alDeleteBuffers(processed - 1, bufids + 1);
|
|
||||||
bufid = bufids[0];
|
|
||||||
}
|
|
||||||
else if(queued < 16) alGenBuffers(1, &bufid);
|
|
||||||
else {
|
|
||||||
pthread_mutex_unlock(device->mutex);
|
|
||||||
return de_Busy;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
alBufferData(bufid, AL_FORMAT_MONO16, data, lenght * 2 * channels, av_DefaultSettings.audio_sample_rate);
|
|
||||||
alSourceQueueBuffers(device->source, 1, &bufid);
|
|
||||||
|
|
||||||
ALint state;
|
|
||||||
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
|
|
||||||
|
|
||||||
if(state != AL_PLAYING) alSourcePlay(device->source);
|
|
||||||
|
|
||||||
|
|
||||||
pthread_mutex_unlock(device->mutex);
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* thread_poll (void* arg) // TODO: maybe use thread for every input source
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* NOTE: We only need to poll input devices for data.
|
|
||||||
*/
|
|
||||||
(void)arg;
|
|
||||||
uint32_t i;
|
|
||||||
int32_t sample = 0;
|
|
||||||
|
|
||||||
int f_size = frame_size;
|
|
||||||
|
|
||||||
while (thread_running)
|
|
||||||
{
|
|
||||||
if (thread_paused) usleep(10000); /* Wait for unpause. */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 0; i < size[input]; i ++)
|
|
||||||
{
|
|
||||||
lock;
|
|
||||||
if (running[input][i] != NULL)
|
|
||||||
{
|
|
||||||
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
|
|
||||||
|
|
||||||
if (sample < f_size) {
|
|
||||||
unlock;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Device* device = running[input][i];
|
|
||||||
|
|
||||||
int16_t frame[4096];
|
|
||||||
alcCaptureSamples(device->dhndl, frame, f_size);
|
|
||||||
|
|
||||||
if ( device->muted ||
|
|
||||||
(device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold)))
|
|
||||||
{ unlock; continue; } /* Skip if no voice activity */
|
|
||||||
|
|
||||||
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
|
|
||||||
}
|
|
||||||
unlock;
|
|
||||||
}
|
|
||||||
usleep(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_devices(ToxWindow* self, DeviceType type)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for ( ; i < size[type]; i ++) {
|
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
snprintf(msg, sizeof(msg), "%d: %s", i, devices_names[type][i]);
|
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError selection_valid(DeviceType type, int32_t selection)
|
|
||||||
{
|
|
||||||
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* get_device_callback_data(uint32_t device_idx)
|
|
||||||
{
|
|
||||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return running[input][device_idx]->cb_data;
|
|
||||||
}
|
|
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);
|
|
||||||
}
|
|
245
src/execute.c
245
src/execute.c
@ -20,17 +20,20 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "api.h"
|
||||||
#include "windows.h"
|
|
||||||
#include "execute.h"
|
|
||||||
#include "chat_commands.h"
|
#include "chat_commands.h"
|
||||||
|
#include "execute.h"
|
||||||
#include "global_commands.h"
|
#include "global_commands.h"
|
||||||
|
#include "conference_commands.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "notify.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
struct cmd_func {
|
struct cmd_func {
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -40,86 +43,179 @@ 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 },
|
{ "/conference", cmd_conference },
|
||||||
{ "/help", cmd_prompt_help },
|
{ "/help", cmd_prompt_help },
|
||||||
{ "/log", cmd_log },
|
{ "/log", cmd_log },
|
||||||
{ "/myid", cmd_myid },
|
{ "/myid", cmd_myid },
|
||||||
|
#ifdef QRCODE
|
||||||
|
{ "/myqr", cmd_myqr },
|
||||||
|
#endif /* QRCODE */
|
||||||
{ "/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[] = {
|
||||||
{ "/invite", cmd_groupinvite },
|
{ "/cancel", cmd_cancelfile },
|
||||||
{ "/join", cmd_join_group },
|
{ "/invite", cmd_conference_invite },
|
||||||
{ "/savefile", cmd_savefile },
|
{ "/join", cmd_conference_join },
|
||||||
{ "/sendfile", cmd_sendfile },
|
{ "/savefile", cmd_savefile },
|
||||||
|
{ "/sendfile", cmd_sendfile },
|
||||||
#ifdef _SUPPORT_AUDIO
|
#ifdef 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 },
|
{ "/mute", cmd_mute },
|
||||||
{ "/sdev", cmd_ccur_device },
|
{ "/sense", cmd_sense },
|
||||||
{ "/mute", cmd_mute },
|
{ "/bitrate", cmd_bitrate },
|
||||||
{ "/sense", cmd_sense },
|
#endif /* AUDIO */
|
||||||
#endif /* _SUPPORT_AUDIO */
|
#ifdef VIDEO
|
||||||
|
{ "/vcall", cmd_vcall },
|
||||||
|
{ "/video", cmd_video },
|
||||||
|
{ "/res", cmd_res },
|
||||||
|
#endif /* VIDEO */
|
||||||
|
{ NULL, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Parses input command and puts args into arg array.
|
static struct cmd_func conference_commands[] = {
|
||||||
Returns number of arguments on success, -1 on failure. */
|
{ "/title", cmd_conference_set_title },
|
||||||
static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE])
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
{ "/audio", cmd_enable_audio },
|
||||||
|
{ "/mute", cmd_conference_mute },
|
||||||
|
{ "/ptt", cmd_conference_push_to_talk },
|
||||||
|
{ "/sense", cmd_conference_sense },
|
||||||
|
#endif /* AUDIO */
|
||||||
|
{ NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
#define SPECIAL_COMMANDS 7
|
||||||
|
#else
|
||||||
|
#define SPECIAL_COMMANDS 6
|
||||||
|
#endif /* PYTHON */
|
||||||
|
|
||||||
|
/* Special commands are commands that only take one argument even if it contains spaces */
|
||||||
|
static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||||
|
"/avatar",
|
||||||
|
"/nick",
|
||||||
|
"/note",
|
||||||
|
#ifdef PYTHON
|
||||||
|
"/run",
|
||||||
|
#endif /* PYTHON */
|
||||||
|
"/sendfile",
|
||||||
|
"/title",
|
||||||
|
"/mute",
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns true if input command is in the special_commands array. */
|
||||||
|
static bool is_special_command(const char *input)
|
||||||
{
|
{
|
||||||
int num_args = 0;
|
const int s = char_find(0, input, ' ');
|
||||||
bool cmd_end = false; /* flags when we get to the end of cmd */
|
|
||||||
char *end; /* points to the end of the current arg */
|
|
||||||
|
|
||||||
/* characters wrapped in double quotes count as one arg */
|
for (int i = 0; i < SPECIAL_COMMANDS; ++i) {
|
||||||
while (!cmd_end && num_args < MAX_NUM_ARGS) {
|
if (strncmp(input, special_commands[i], s) == 0) {
|
||||||
if (*cmd == '\"') {
|
return true;
|
||||||
end = strchr(cmd + 1, '\"');
|
|
||||||
|
|
||||||
if (end++ == NULL) { /* Increment past the end quote */
|
|
||||||
char *errmsg = "Invalid argument. Did you forget a closing \"?";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_end = *end == '\0';
|
|
||||||
} else {
|
|
||||||
end = strchr(cmd, ' ');
|
|
||||||
cmd_end = end == NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cmd_end)
|
|
||||||
*end++ = '\0'; /* mark end of current argument */
|
|
||||||
|
|
||||||
/* Copy from start of current arg to where we just inserted the null byte */
|
|
||||||
strcpy(args[num_args++], cmd);
|
|
||||||
cmd = end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parses commands in the special_commands array. Unlike parse_command, this function
|
||||||
|
* does not split the input string at spaces.
|
||||||
|
*
|
||||||
|
* Returns the number of arguments.
|
||||||
|
*/
|
||||||
|
static int parse_special_command(const char *input, char (*args)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
int len = strlen(input);
|
||||||
|
int s = char_find(0, input, ' ');
|
||||||
|
|
||||||
|
memcpy(args[0], input, s);
|
||||||
|
args[0][s++] = '\0'; // increment to remove space after "/command "
|
||||||
|
|
||||||
|
if (s >= len) {
|
||||||
|
return 1; // No additional args
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(args[1], input + s, len - s);
|
||||||
|
args[1][len - s] = '\0';
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parses input command and puts args into arg array.
|
||||||
|
*
|
||||||
|
* Returns the number of arguments.
|
||||||
|
*/
|
||||||
|
static int parse_command(const char *input, char (*args)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
if (is_special_command(input)) {
|
||||||
|
return parse_special_command(input, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cmd = strdup(input);
|
||||||
|
|
||||||
|
if (cmd == NULL) {
|
||||||
|
exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_args = 0;
|
||||||
|
|
||||||
|
/* characters wrapped in double quotes count as one arg */
|
||||||
|
while (num_args < MAX_NUM_ARGS) {
|
||||||
|
int i = char_find(0, cmd, ' '); // index of last char in an argument
|
||||||
|
memcpy(args[num_args], cmd, i);
|
||||||
|
args[num_args++][i] = '\0';
|
||||||
|
|
||||||
|
if (cmd[i] == '\0') { // no more args
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char tmp[MAX_STR_SIZE];
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
|
||||||
|
strcpy(cmd, tmp); // tmp will always fit inside cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_cmds,
|
*
|
||||||
struct cmd_func *commands, char (*args)[MAX_STR_SIZE])
|
* Returns 0 on match.
|
||||||
|
* Returns 1 on no match
|
||||||
|
*/
|
||||||
|
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
|
||||||
|
char (*args)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
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,34 +225,51 @@ 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(input, args);
|
||||||
|
|
||||||
if (num_args == -1)
|
if (num_args <= 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to match input command to command functions. If non-global command mode is specified,
|
/* Try to match input command to command functions. If non-global command mode is specified,
|
||||||
try specified mode's commands first, then upon failure try global commands.
|
* try specified mode's commands first, then upon failure try global commands.
|
||||||
|
*
|
||||||
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 CONFERENCE_COMMAND_MODE:
|
||||||
|
if (do_command(w, self, m, num_args, conference_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, false, 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,
|
CONFERENCE_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 /* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
309
src/file_transfers.c
Normal file
309
src/file_transfers.c
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
/* 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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "file_transfers.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "notify.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
extern FriendsList Friends;
|
||||||
|
|
||||||
|
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||||
|
#define NUM_PROG_MARKS 50
|
||||||
|
#define STR_BUF_SIZE 30
|
||||||
|
|
||||||
|
/* 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[STR_BUF_SIZE];
|
||||||
|
snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
|
||||||
|
|
||||||
|
char bps_str[STR_BUF_SIZE];
|
||||||
|
bytes_convert_str(bps_str, sizeof(bps_str), bps);
|
||||||
|
|
||||||
|
char prog_line[NUM_PROG_MARKS + 1];
|
||||||
|
prog_line[0] = 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, "-");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t line_buf_size = strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7;
|
||||||
|
char *full_line = malloc(line_buf_size);
|
||||||
|
|
||||||
|
if (full_line == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(full_line, line_buf_size, "%s [%s] %s/s", pct_str, prog_line, bps_str);
|
||||||
|
|
||||||
|
line_info_set(self, line_id, full_line);
|
||||||
|
|
||||||
|
free(full_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void refresh_progress_helper(ToxWindow *self, 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, uint32_t friendnumber)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||||
|
refresh_progress_helper(self, &Friends.list[friendnumber].file_receiver[i]);
|
||||||
|
refresh_progress_helper(self, &Friends.list[friendnumber].file_sender[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_file_transfer(struct FileTransfer *ft)
|
||||||
|
{
|
||||||
|
*ft = (struct FileTransfer) {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
|
||||||
|
* Returns NULL if filenumber is invalid.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i];
|
||||||
|
|
||||||
|
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) {
|
||||||
|
return ft_send;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) {
|
||||||
|
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 friendnumber, uint32_t index,
|
||||||
|
FILE_TRANSFER_DIRECTION direction)
|
||||||
|
{
|
||||||
|
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
|
||||||
|
&Friends.list[friendnumber].file_sender[i] :
|
||||||
|
&Friends.list[friendnumber].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 friendnumber, uint32_t filenumber, uint8_t type)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
clear_file_transfer(ft);
|
||||||
|
ft->window = window;
|
||||||
|
ft->index = i;
|
||||||
|
ft->friendnumber = friendnumber;
|
||||||
|
ft->filenumber = filenumber;
|
||||||
|
ft->file_type = type;
|
||||||
|
ft->state = FILE_TRANSFER_PENDING;
|
||||||
|
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 friendnumber, uint32_t filenumber,
|
||||||
|
uint8_t type)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
clear_file_transfer(ft);
|
||||||
|
ft->window = window;
|
||||||
|
ft->index = i;
|
||||||
|
ft->friendnumber = friendnumber;
|
||||||
|
ft->filenumber = filenumber;
|
||||||
|
ft->file_type = type;
|
||||||
|
ft->state = FILE_TRANSFER_PENDING;
|
||||||
|
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 friendnumber, uint32_t filenumber,
|
||||||
|
FILE_TRANSFER_DIRECTION direction, uint8_t type)
|
||||||
|
{
|
||||||
|
if (direction == FILE_TRANSFER_RECV) {
|
||||||
|
return new_file_receiver(window, friendnumber, filenumber, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction == FILE_TRANSFER_SEND) {
|
||||||
|
return new_file_sender(window, friendnumber, filenumber, 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->friendnumber, ft->filenumber, (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, false, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_file_transfer(ft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kills active outgoing avatar file transfers for friendnumber */
|
||||||
|
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
|
||||||
|
|
||||||
|
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kills all active file transfers for friendnumber */
|
||||||
|
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||||
|
close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kill_all_file_transfers(Tox *m)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < Friends.max_idx; ++i) {
|
||||||
|
kill_all_file_transfers_friend(m, Friends.list[i].num);
|
||||||
|
}
|
||||||
|
}
|
112
src/file_transfers.h
Normal file
112
src/file_transfers.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/* 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 "notify.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.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;
|
||||||
|
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 filenumber;
|
||||||
|
uint32_t friendnumber;
|
||||||
|
size_t index;
|
||||||
|
uint64_t file_size;
|
||||||
|
uint64_t position;
|
||||||
|
time_t last_line_progress; /* The last time we updated the progress bar */
|
||||||
|
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, uint32_t friendnumber);
|
||||||
|
|
||||||
|
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
|
||||||
|
* Returns NULL if filenumber is invalid.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber);
|
||||||
|
|
||||||
|
|
||||||
|
/* 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 friendnumber, 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 friendnumber, uint32_t filenumber,
|
||||||
|
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 active outgoing avatar file transfers for friendnumber */
|
||||||
|
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber);
|
||||||
|
|
||||||
|
/* Kills all active file transfers for friendnumber */
|
||||||
|
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber);
|
||||||
|
|
||||||
|
void kill_all_file_transfers(Tox *m);
|
||||||
|
|
||||||
|
#endif /* FILE_TRANSFERS_H */
|
1447
src/friendlist.c
1447
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 "file_transfers.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "file_senders.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,70 @@ 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 ConferenceInvite {
|
||||||
|
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;
|
uint16_t 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;
|
Tox_User_Status status;
|
||||||
|
|
||||||
struct LastOnline last_online;
|
struct LastOnline last_online;
|
||||||
struct FileReceiver file_receiver;
|
struct ConferenceInvite conference_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);
|
uint16_t 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 friendlist_onInit(ToxWindow *self, Tox *m);
|
||||||
|
void disable_chatwin(uint32_t f_num);
|
||||||
|
int get_friendnum(uint8_t *name);
|
||||||
|
int load_blocklist(char *data);
|
||||||
|
void kill_friendlist(ToxWindow *self);
|
||||||
|
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
|
||||||
|
Tox_User_Status get_friend_status(uint32_t friendnumber);
|
||||||
|
Tox_Connection get_friend_connection_status(uint32_t friendnumber);
|
||||||
|
|
||||||
/* sorts friendlist_index first by connection status then alphabetically */
|
/* 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 */
|
/*
|
||||||
|
* Returns true if friend associated with `public_key` is in the block list.
|
||||||
|
*
|
||||||
|
* `public_key` must be at least TOX_PUBLIC_KEY_SIZE bytes.
|
||||||
|
*/
|
||||||
|
bool friend_is_blocked(const char *public_key);
|
||||||
|
|
||||||
|
#endif /* end of include guard: FRIENDLIST_H */
|
||||||
|
@ -22,337 +22,570 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "avatars.h"
|
||||||
#include "windows.h"
|
#include "conference.h"
|
||||||
#include "misc_tools.h"
|
|
||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
#include "log.h"
|
|
||||||
#include "line_info.h"
|
|
||||||
#include "dns.h"
|
|
||||||
#include "groupchat.h"
|
|
||||||
#include "prompt.h"
|
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "name_lookup.h"
|
||||||
|
#include "prompt.h"
|
||||||
|
#include "qr_code.h"
|
||||||
|
#include "term_mplex.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
#include "windows.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;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
if (argc != 1) {
|
if (argc < 1) {
|
||||||
msg = "Invalid syntax.";
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||||
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, false, 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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
|
||||||
else {
|
return;
|
||||||
msg = "Friend request accepted.";
|
} else {
|
||||||
on_friendadded(m, friendnum, true);
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
|
||||||
|
on_friend_added(m, friendnum, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
|
FrndRequests.request[req] = (struct friend_request) {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
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, (const uint8_t *) id_bin, (const 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_friend_added(m, f_num, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_FRIEND_ADD_NULL:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
default:
|
||||||
|
errmsg = "Failed to add friend: Unknown error.";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
line_info_add(self, false, 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;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
errmsg = "Invalid syntax.";
|
line_info_add(self, false, 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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id_bin[i] = x;
|
id_bin[i] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (friend_is_blocked(id_bin)) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
|
if (argc != 1 || strlen(argv[1]) < 3) {
|
||||||
|
avatar_unset(m);
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[MAX_STR_SIZE];
|
||||||
|
snprintf(path, sizeof(path), "%s", argv[1]);
|
||||||
|
int len = strlen(path);
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
line_info_add(self, false, 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, false, 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, false, 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])
|
||||||
{
|
{
|
||||||
|
UNUSED_VAR(m);
|
||||||
|
UNUSED_VAR(argc);
|
||||||
|
UNUSED_VAR(argv);
|
||||||
|
|
||||||
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;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
/* check arguments */
|
|
||||||
if (argc != 3) {
|
if (argc != 3) {
|
||||||
errmsg = "Invalid syntax.";
|
line_info_add(self, false, 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, false, 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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_BOOTSTRAP_BAD_PORT:
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_BOOTSTRAP_NULL:
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *errmsg;
|
UNUSED_VAR(window);
|
||||||
|
UNUSED_VAR(m);
|
||||||
|
|
||||||
|
if (argc < 1) {
|
||||||
|
line_info_add(self, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FrndRequests.request[req].active) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrndRequests.request[req] = (struct friend_request) {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
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_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference 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, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
uint32_t conferencenum = 0;
|
||||||
snprintf(msg, sizeof(msg), "Group chat created as %d.", groupnum);
|
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
||||||
|
Tox_Err_Conference_New err;
|
||||||
|
|
||||||
|
conferencenum = tox_conference_new(m, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||||
|
#ifdef AUDIO
|
||||||
|
conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL);
|
||||||
|
|
||||||
|
if (conferencenum == (uint32_t) -1) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
|
||||||
|
tox_conference_delete(m, conferencenum, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
|
||||||
|
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||||
|
if (!init_conference_audio_input(m, conferencenum)) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
|
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, false, 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")) {
|
||||||
|
msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log.";
|
||||||
if (self->is_chat) {
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
friends[self->num].logging_on = true;
|
|
||||||
log_enable(self->name, friends[self->num].pub_key, log);
|
|
||||||
} else if (self->is_prompt) {
|
|
||||||
char myid[TOX_FRIEND_ADDRESS_SIZE];
|
|
||||||
tox_get_address(m, (uint8_t *) myid);
|
|
||||||
log_enable(self->name, myid, log);
|
|
||||||
} else if (self->is_groupchat) {
|
|
||||||
log_enable(self->name, NULL, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = "Logging enabled";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||||
if (self->is_chat)
|
if (self->type == WINDOW_TYPE_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, false, 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, false, 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};
|
UNUSED_VAR(window);
|
||||||
char address[TOX_FRIEND_ADDRESS_SIZE];
|
UNUSED_VAR(argc);
|
||||||
tox_get_address(m, (uint8_t *) address);
|
UNUSED_VAR(argv);
|
||||||
|
|
||||||
size_t i;
|
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||||
|
char bin_id[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||||
|
|
||||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||||
char xx[3];
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
|
||||||
snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff);
|
|
||||||
strcat(id, xx);
|
|
||||||
}
|
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
|
||||||
{
|
|
||||||
char *errmsg;
|
|
||||||
|
|
||||||
/* check arguments */
|
|
||||||
if (argc < 1) {
|
|
||||||
errmsg = "Invalid name.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *nick = argv[1];
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
|
||||||
int len = strlen(nick);
|
}
|
||||||
|
|
||||||
if (nick[0] == '\"') {
|
#ifdef QRCODE
|
||||||
++nick;
|
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
len -= 2;
|
{
|
||||||
nick[len] = '\0';
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
|
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||||
|
char bin_id[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||||
|
|
||||||
|
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||||
|
line_info_add(self, false, 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 = malloc(data_file_len + 1);
|
||||||
|
|
||||||
|
if (dir == NULL) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
|
||||||
|
|
||||||
|
#ifdef QRPNG
|
||||||
|
|
||||||
|
if (argc == 0) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'");
|
||||||
|
free(dir);
|
||||||
|
return;
|
||||||
|
} else if (!strcmp(argv[1], "txt")) {
|
||||||
|
|
||||||
|
#endif /* QRPNG */
|
||||||
|
size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT);
|
||||||
|
char *qr_path = malloc(qr_path_buf_size);
|
||||||
|
|
||||||
|
if (qr_path == NULL) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory");
|
||||||
|
free(dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
|
||||||
|
|
||||||
|
if (ID_to_QRcode_txt(id_string, qr_path) == -1) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||||
|
free(dir);
|
||||||
|
free(qr_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||||
|
|
||||||
|
free(qr_path);
|
||||||
|
|
||||||
|
#ifdef QRPNG
|
||||||
|
} else if (!strcmp(argv[1], "png")) {
|
||||||
|
size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT_PNG);
|
||||||
|
char *qr_path = malloc(qr_path_buf_size);
|
||||||
|
|
||||||
|
if (qr_path == NULL) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory");
|
||||||
|
free(dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG);
|
||||||
|
|
||||||
|
if (ID_to_QRcode_png(id_string, qr_path) == -1) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||||
|
free(dir);
|
||||||
|
free(qr_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||||
|
|
||||||
|
free(qr_path);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]);
|
||||||
|
free(dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* QRPNG */
|
||||||
|
|
||||||
|
free(dir);
|
||||||
|
}
|
||||||
|
#endif /* QRCODE */
|
||||||
|
|
||||||
|
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
|
if (argc < 1) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char nick[MAX_STR_SIZE];
|
||||||
|
snprintf(nick, sizeof(nick), "%s", argv[1]);
|
||||||
|
size_t len = strlen(nick);
|
||||||
|
|
||||||
if (!valid_nick(nick)) {
|
if (!valid_nick(nick)) {
|
||||||
errmsg = "Invalid name.";
|
line_info_add(self, false, 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,83 +593,132 @@ 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;
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
errmsg = "Wrong number of arguments.";
|
line_info_add(self, false, 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];
|
prompt_update_statusmessage(prompt, m, argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
if (msg[0] != '\"') {
|
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
errmsg = "Note must be enclosed in quotes.";
|
{
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
long int nospam = rand();
|
||||||
return;
|
|
||||||
|
if (argc > 0) {
|
||||||
|
nospam = strtol(argv[1], NULL, 16);
|
||||||
|
|
||||||
|
if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++msg;
|
uint32_t old_nospam = tox_self_get_nospam(m);
|
||||||
int len = strlen(msg) - 1;
|
tox_self_set_nospam(m, (uint32_t) nospam);
|
||||||
msg[len] = '\0';
|
|
||||||
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:");
|
||||||
prompt_update_statusmessage(prompt, msg);
|
cmd_myid(window, self, m, 0, NULL);
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
|
||||||
|
"Any services that relied on your old ID will need to be updated manually.");
|
||||||
|
line_info_add(self, false, 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])
|
||||||
{
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
UNUSED_VAR(m);
|
||||||
|
UNUSED_VAR(argc);
|
||||||
|
UNUSED_VAR(argv);
|
||||||
|
|
||||||
help_init_menu(self);
|
help_init_menu(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
UNUSED_VAR(argc);
|
||||||
|
UNUSED_VAR(argv);
|
||||||
|
UNUSED_VAR(self);
|
||||||
|
|
||||||
exit_toxic_success(m);
|
exit_toxic_success(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
UNUSED_VAR(m);
|
||||||
|
UNUSED_VAR(argc);
|
||||||
|
UNUSED_VAR(argv);
|
||||||
|
|
||||||
|
if (FrndRequests.num_requests == 0) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < FrndRequests.max_idx; ++i) {
|
||||||
|
if (!FrndRequests.request[i].active) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
|
||||||
|
|
||||||
|
if (++count < FrndRequests.num_requests) {
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char *msg = NULL;
|
UNUSED_VAR(window);
|
||||||
char *errmsg;
|
|
||||||
|
|
||||||
if (argc >= 2) {
|
const char *errmsg;
|
||||||
msg = argv[2];
|
|
||||||
|
|
||||||
if (msg[0] != '\"') {
|
lock_status();
|
||||||
errmsg = "Note must be enclosed in quotes.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
if (argc < 1) {
|
||||||
return;
|
errmsg = "Require a status. Statuses are: online, busy and away.";
|
||||||
}
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||||
} else if (argc != 1) {
|
goto finish;
|
||||||
errmsg = "Wrong number of arguments.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *status = argv[1];
|
const char *status_str = argv[1];
|
||||||
str_to_lower(status);
|
Tox_User_Status status;
|
||||||
|
|
||||||
TOX_USERSTATUS status_kind;
|
if (!strcasecmp(status_str, "online")) {
|
||||||
|
status = TOX_USER_STATUS_NONE;
|
||||||
if (!strcmp(status, "online"))
|
} else if (!strcasecmp(status_str, "away")) {
|
||||||
status_kind = TOX_USERSTATUS_NONE;
|
status = TOX_USER_STATUS_AWAY;
|
||||||
else if (!strcmp(status, "away"))
|
} else if (!strcasecmp(status_str, "busy")) {
|
||||||
status_kind = TOX_USERSTATUS_AWAY;
|
status = TOX_USER_STATUS_BUSY;
|
||||||
else if (!strcmp(status, "busy"))
|
} else {
|
||||||
status_kind = TOX_USERSTATUS_BUSY;
|
|
||||||
else {
|
|
||||||
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||||
return;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
tox_set_user_status(m, status_kind);
|
tox_self_set_status(m, status);
|
||||||
prompt_update_status(prompt, status_kind);
|
prompt_update_status(prompt, status);
|
||||||
|
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
|
||||||
|
|
||||||
if (msg != NULL) {
|
|
||||||
++msg;
|
finish:
|
||||||
int len = strlen(msg) - 1;
|
unlock_status();
|
||||||
msg[len] = '\0'; /* remove opening and closing quotes */
|
|
||||||
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
|
|
||||||
prompt_update_statusmessage(prompt, msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,30 +20,46 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _global_commands_h
|
#ifndef GLOBAL_COMMANDS_H
|
||||||
#define _global_commands_h
|
#define GLOBAL_COMMANDS_H
|
||||||
|
|
||||||
#include "windows.h"
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.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_groupchat(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_conference(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]);
|
||||||
|
#ifdef QRCODE
|
||||||
|
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* QRCODE */
|
||||||
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_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 /* PYTHON */
|
||||||
|
|
||||||
|
#endif /* GLOBAL_COMMANDS_H */
|
||||||
|
543
src/groupchat.c
543
src/groupchat.c
@ -1,543 +0,0 @@
|
|||||||
/* groupchat.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _GNU_SOURCE
|
|
||||||
#define _GNU_SOURCE /* needed for strcasestr() and wcswidth() */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <wchar.h>
|
|
||||||
|
|
||||||
#include "windows.h"
|
|
||||||
#include "toxic.h"
|
|
||||||
#include "execute.h"
|
|
||||||
#include "misc_tools.h"
|
|
||||||
#include "groupchat.h"
|
|
||||||
#include "prompt.h"
|
|
||||||
#include "toxic_strings.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "line_info.h"
|
|
||||||
#include "settings.h"
|
|
||||||
#include "input.h"
|
|
||||||
#include "help.h"
|
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
|
||||||
|
|
||||||
static GroupChat groupchats[MAX_GROUPCHAT_NUM];
|
|
||||||
static int max_groupchat_index = 0;
|
|
||||||
|
|
||||||
extern struct user_settings *user_settings;
|
|
||||||
|
|
||||||
/* temporary until group chats have unique commands */
|
|
||||||
extern const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
|
|
||||||
|
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
|
||||||
{
|
|
||||||
if (groupnum > MAX_GROUPCHAT_NUM)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i <= max_groupchat_index; ++i) {
|
|
||||||
if (!groupchats[i].active) {
|
|
||||||
groupchats[i].chatwin = add_window(m, new_group_chat(m, groupnum));
|
|
||||||
groupchats[i].active = true;
|
|
||||||
groupchats[i].num_peers = 0;
|
|
||||||
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
|
||||||
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
|
||||||
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
|
|
||||||
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
|
|
||||||
|
|
||||||
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
|
||||||
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
|
|
||||||
|
|
||||||
set_active_window(groupchats[i].chatwin);
|
|
||||||
|
|
||||||
if (i == max_groupchat_index)
|
|
||||||
++max_groupchat_index;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kill_groupchat_window(ToxWindow *self)
|
|
||||||
{
|
|
||||||
ChatContext *ctx = self->chatwin;
|
|
||||||
|
|
||||||
log_disable(ctx->log);
|
|
||||||
line_info_cleanup(ctx->hst);
|
|
||||||
delwin(ctx->linewin);
|
|
||||||
delwin(ctx->history);
|
|
||||||
delwin(ctx->sidebar);
|
|
||||||
free(ctx->log);
|
|
||||||
free(ctx->hst);
|
|
||||||
free(ctx);
|
|
||||||
free(self->help);
|
|
||||||
del_window(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
|
||||||
{
|
|
||||||
tox_del_groupchat(m, groupnum);
|
|
||||||
|
|
||||||
free(groupchats[groupnum].peer_names);
|
|
||||||
free(groupchats[groupnum].oldpeer_names);
|
|
||||||
free(groupchats[groupnum].peer_name_lengths);
|
|
||||||
free(groupchats[groupnum].oldpeer_name_lengths);
|
|
||||||
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = max_groupchat_index; i > 0; --i) {
|
|
||||||
if (groupchats[i - 1].active)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
max_groupchat_index = i;
|
|
||||||
kill_groupchat_window(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
|
||||||
const char *msg, uint16_t len)
|
|
||||||
{
|
|
||||||
if (self->num != groupnum)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
|
||||||
|
|
||||||
char nick[TOX_MAX_NAME_LENGTH];
|
|
||||||
int n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
|
|
||||||
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); /* enforce client max name length */
|
|
||||||
nick[n_len] = '\0';
|
|
||||||
|
|
||||||
/* check if message contains own name and alert appropriately */
|
|
||||||
int alert_type = WINDOW_ALERT_1;
|
|
||||||
bool beep = false;
|
|
||||||
|
|
||||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
|
||||||
uint16_t sn_len = tox_get_self_name(m, (uint8_t *) selfnick);
|
|
||||||
selfnick[sn_len] = '\0';
|
|
||||||
|
|
||||||
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
|
||||||
|
|
||||||
bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH - 1);
|
|
||||||
|
|
||||||
if (nick_match) {
|
|
||||||
alert_type = WINDOW_ALERT_0;
|
|
||||||
beep = true;
|
|
||||||
nick_clr = RED;
|
|
||||||
}
|
|
||||||
|
|
||||||
alert_window(self, alert_type, beep);
|
|
||||||
|
|
||||||
char timefrmt[TIME_STR_SIZE];
|
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
|
||||||
|
|
||||||
line_info_add(self, timefrmt, nick, NULL, msg, IN_MSG, 0, nick_clr);
|
|
||||||
write_to_log(msg, nick, ctx->log, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *action,
|
|
||||||
uint16_t len)
|
|
||||||
{
|
|
||||||
if (self->num != groupnum)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
|
||||||
|
|
||||||
/* check if message contains own name and alert appropriately */
|
|
||||||
int alert_type = WINDOW_ALERT_1;
|
|
||||||
bool beep = false;
|
|
||||||
|
|
||||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
|
||||||
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfnick);
|
|
||||||
selfnick[n_len] = '\0';
|
|
||||||
|
|
||||||
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];
|
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
|
||||||
|
|
||||||
line_info_add(self, timefrmt, nick, NULL, action, ACTION, 0, 0);
|
|
||||||
write_to_log(action, nick, ctx->log, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Puts two copies of peerlist/lengths in chat instance */
|
|
||||||
static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers)
|
|
||||||
{
|
|
||||||
/* Assumes these are initiated in init_groupchat_win */
|
|
||||||
free(groupchats[gnum].peer_names);
|
|
||||||
free(groupchats[gnum].oldpeer_names);
|
|
||||||
free(groupchats[gnum].peer_name_lengths);
|
|
||||||
free(groupchats[gnum].oldpeer_name_lengths);
|
|
||||||
|
|
||||||
int N = TOX_MAX_NAME_LENGTH;
|
|
||||||
|
|
||||||
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
|
|
||||||
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
|
|
||||||
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
|
|
||||||
groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers);
|
|
||||||
|
|
||||||
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|
|
||||||
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
|
|
||||||
exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t unknown_len = (uint16_t) strlen(UNKNOWN_NAME);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < npeers; ++i) {
|
|
||||||
if (string_is_empty((char *) peerlist[i])) {
|
|
||||||
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
|
||||||
groupchats[gnum].peer_name_lengths[i] = unknown_len;
|
|
||||||
} else {
|
|
||||||
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], N);
|
|
||||||
uint16_t n_len = lengths[i];
|
|
||||||
|
|
||||||
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
|
|
||||||
|
|
||||||
groupchats[gnum].peer_names[i * N + n_len] = '\0';
|
|
||||||
groupchats[gnum].peer_name_lengths[i] = n_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
|
|
||||||
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths,
|
|
||||||
sizeof(uint16_t) * npeers);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
|
||||||
uint8_t change)
|
|
||||||
{
|
|
||||||
if (self->num != groupnum)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (groupnum > max_groupchat_index)
|
|
||||||
return;
|
|
||||||
|
|
||||||
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
|
|
||||||
int num_peers = groupchats[groupnum].num_peers;
|
|
||||||
|
|
||||||
if (peernum > num_peers)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* get old peer name before updating name list */
|
|
||||||
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
|
|
||||||
|
|
||||||
if (change != TOX_CHAT_CHANGE_PEER_ADD) {
|
|
||||||
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH],
|
|
||||||
sizeof(oldpeername));
|
|
||||||
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
|
|
||||||
oldpeername[old_n_len] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update name/len lists */
|
|
||||||
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
|
|
||||||
uint16_t tmp_peerlens[num_peers];
|
|
||||||
tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers);
|
|
||||||
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
|
|
||||||
|
|
||||||
/* get current peername then sort namelist */
|
|
||||||
uint8_t peername[TOX_MAX_NAME_LENGTH];
|
|
||||||
|
|
||||||
if (change != TOX_CHAT_CHANGE_PEER_DEL) {
|
|
||||||
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
|
|
||||||
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
|
|
||||||
peername[n_len] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
|
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
|
||||||
|
|
||||||
char *event;
|
|
||||||
char timefrmt[TIME_STR_SIZE];
|
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
|
||||||
|
|
||||||
switch (change) {
|
|
||||||
case TOX_CHAT_CHANGE_PEER_ADD:
|
|
||||||
event = "has joined the room";
|
|
||||||
line_info_add(self, timefrmt, (char *) peername, NULL, event, CONNECTION, 0, GREEN);
|
|
||||||
write_to_log(event, (char *) peername, ctx->log, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_DEL:
|
|
||||||
event = "has left the room";
|
|
||||||
line_info_add(self, timefrmt, (char *) oldpeername, NULL, event, CONNECTION, 0, 0);
|
|
||||||
|
|
||||||
if (groupchats[self->num].side_pos > 0)
|
|
||||||
--groupchats[self->num].side_pos;
|
|
||||||
|
|
||||||
write_to_log(event, (char *) oldpeername, ctx->log, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_NAME:
|
|
||||||
event = " is now known as ";
|
|
||||||
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, event, NAME_CHANGE, 0, 0);
|
|
||||||
|
|
||||||
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
|
|
||||||
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
|
|
||||||
write_to_log(tmp_event, (char *) oldpeername, ctx->log, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
alert_window(self, WINDOW_ALERT_2, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
|
||||||
{
|
|
||||||
if (action == NULL) {
|
|
||||||
wprintw(ctx->history, "Invalid syntax.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) {
|
|
||||||
char *errmsg = " * Failed to send action.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|
||||||
{
|
|
||||||
ChatContext *ctx = self->chatwin;
|
|
||||||
|
|
||||||
int x, y, y2, x2;
|
|
||||||
getyx(self->window, y, x);
|
|
||||||
getmaxyx(self->window, y2, x2);
|
|
||||||
|
|
||||||
if (x2 <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (self->help->active) {
|
|
||||||
help_onKey(self, key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ltr) { /* char is printable */
|
|
||||||
input_new_char(self, key, x, y, x2, y2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line_info_onKey(self, key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (input_handle(self, key, x, y, x2, y2))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (key == '\t') { /* TAB key: auto-completes peer name or command */
|
|
||||||
if (ctx->len > 0) {
|
|
||||||
int diff;
|
|
||||||
|
|
||||||
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
|
|
||||||
diff = complete_line(ctx, groupchats[self->num].peer_names,
|
|
||||||
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
|
|
||||||
else
|
|
||||||
diff = complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
|
|
||||||
|
|
||||||
if (diff != -1) {
|
|
||||||
if (x + diff > x2 - 1) {
|
|
||||||
wmove(self->window, y, x + diff);
|
|
||||||
ctx->start += diff;
|
|
||||||
} else {
|
|
||||||
wmove(self->window, y, x + diff);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
} else if (key == T_KEY_C_RB) { /* Scroll peerlist up and down one position */
|
|
||||||
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
|
||||||
|
|
||||||
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
|
|
||||||
++groupchats[self->num].side_pos;
|
|
||||||
} else if (key == T_KEY_C_LB) {
|
|
||||||
if (groupchats[self->num].side_pos > 0)
|
|
||||||
--groupchats[self->num].side_pos;
|
|
||||||
} else if (key == '\n') {
|
|
||||||
rm_trailing_spaces_buf(ctx);
|
|
||||||
|
|
||||||
char line[MAX_STR_SIZE];
|
|
||||||
|
|
||||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
|
||||||
memset(&line, 0, sizeof(line));
|
|
||||||
|
|
||||||
if (!string_is_empty(line))
|
|
||||||
add_line_to_hist(ctx);
|
|
||||||
|
|
||||||
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 if (!string_is_empty(line)) {
|
|
||||||
if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) {
|
|
||||||
char *errmsg = " * Failed to send message.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
|
||||||
reset_buf(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
|
||||||
{
|
|
||||||
int x2, y2;
|
|
||||||
getmaxyx(self->window, y2, x2);
|
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
|
||||||
|
|
||||||
line_info_print(self);
|
|
||||||
wclear(ctx->linewin);
|
|
||||||
|
|
||||||
curs_set(1);
|
|
||||||
|
|
||||||
if (ctx->len > 0)
|
|
||||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
|
||||||
|
|
||||||
wclear(ctx->sidebar);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
wmove(ctx->sidebar, 0, 1);
|
|
||||||
wattron(ctx->sidebar, A_BOLD);
|
|
||||||
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
|
|
||||||
wattroff(ctx->sidebar, A_BOLD);
|
|
||||||
|
|
||||||
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
|
||||||
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
|
||||||
|
|
||||||
int N = TOX_MAX_NAME_LENGTH;
|
|
||||||
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
|
||||||
wmove(ctx->sidebar, i + 2, 1);
|
|
||||||
int peer = i + groupchats[self->num].side_pos;
|
|
||||||
|
|
||||||
/* truncate nick to fit in side panel without modifying list */
|
|
||||||
char tmpnck[TOX_MAX_NAME_LENGTH];
|
|
||||||
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * N], SIDEBAR_WIDTH - 2);
|
|
||||||
tmpnck[SIDEBAR_WIDTH - 2] = '\0';
|
|
||||||
|
|
||||||
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
|
||||||
}
|
|
||||||
|
|
||||||
int y, x;
|
|
||||||
getyx(self->window, y, x);
|
|
||||||
(void) x;
|
|
||||||
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
|
||||||
wmove(self->window, y + 1, new_x);
|
|
||||||
|
|
||||||
wrefresh(self->window);
|
|
||||||
|
|
||||||
if (self->help->active)
|
|
||||||
help_onDraw(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void groupchat_onInit(ToxWindow *self, Tox *m)
|
|
||||||
{
|
|
||||||
int x2, y2;
|
|
||||||
getmaxyx(self->window, y2, x2);
|
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
|
||||||
|
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
|
||||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
|
||||||
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
|
||||||
|
|
||||||
ctx->hst = calloc(1, sizeof(struct history));
|
|
||||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
|
||||||
|
|
||||||
if (ctx->log == NULL || ctx->hst == NULL)
|
|
||||||
exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY);
|
|
||||||
|
|
||||||
line_info_init(ctx->hst);
|
|
||||||
|
|
||||||
if (user_settings->autolog == AUTOLOG_ON)
|
|
||||||
log_enable(self->name, NULL, ctx->log);
|
|
||||||
|
|
||||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
|
||||||
|
|
||||||
scrollok(ctx->history, 0);
|
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ToxWindow new_group_chat(Tox *m, int groupnum)
|
|
||||||
{
|
|
||||||
ToxWindow ret;
|
|
||||||
memset(&ret, 0, sizeof(ret));
|
|
||||||
|
|
||||||
ret.active = true;
|
|
||||||
ret.is_groupchat = true;
|
|
||||||
|
|
||||||
ret.onKey = &groupchat_onKey;
|
|
||||||
ret.onDraw = &groupchat_onDraw;
|
|
||||||
ret.onInit = &groupchat_onInit;
|
|
||||||
ret.onGroupMessage = &groupchat_onGroupMessage;
|
|
||||||
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
|
||||||
ret.onGroupAction = &groupchat_onGroupAction;
|
|
||||||
|
|
||||||
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
|
|
||||||
|
|
||||||
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
|
||||||
Help *help = calloc(1, sizeof(Help));
|
|
||||||
|
|
||||||
if (chatwin == NULL || help == NULL)
|
|
||||||
exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
|
|
||||||
|
|
||||||
ret.chatwin = chatwin;
|
|
||||||
ret.help = help;
|
|
||||||
|
|
||||||
ret.num = groupnum;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
/* groupchat.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 _groupchat_h
|
|
||||||
#define _groupchat_h
|
|
||||||
|
|
||||||
#include "toxic.h"
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
#define SIDEBAR_WIDTH 16
|
|
||||||
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
|
||||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int chatwin;
|
|
||||||
bool active;
|
|
||||||
int num_peers;
|
|
||||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
|
||||||
uint8_t *peer_names;
|
|
||||||
uint8_t *oldpeer_names;
|
|
||||||
uint16_t *peer_name_lengths;
|
|
||||||
uint16_t *oldpeer_name_lengths;
|
|
||||||
} GroupChat;
|
|
||||||
|
|
||||||
void kill_groupchat_window(ToxWindow *self);
|
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
|
|
||||||
ToxWindow new_group_chat(Tox *m, int groupnum);
|
|
||||||
|
|
||||||
#endif /* #define _groupchat_h */
|
|
292
src/help.c
292
src/help.c
@ -22,24 +22,34 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "windows.h"
|
|
||||||
#include "toxic.h"
|
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
#define HELP_MENU_HEIGHT 7
|
#ifdef PYTHON
|
||||||
|
#include "api.h"
|
||||||
|
#endif /* PYTHON */
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
#define HELP_MENU_HEIGHT 10
|
||||||
|
#else
|
||||||
|
#define HELP_MENU_HEIGHT 9
|
||||||
|
#endif /* PYTHON */
|
||||||
#define HELP_MENU_WIDTH 26
|
#define HELP_MENU_WIDTH 26
|
||||||
|
|
||||||
void help_init_menu(ToxWindow *self)
|
void help_init_menu(ToxWindow *self)
|
||||||
{
|
{
|
||||||
if (self->help->win)
|
if (self->help->win) {
|
||||||
delwin(self->help->win);
|
delwin(self->help->win);
|
||||||
|
}
|
||||||
|
|
||||||
int y2, x2;
|
int y2, x2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH)
|
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
|
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
|
||||||
self->help->active = true;
|
self->help->active = true;
|
||||||
@ -49,17 +59,25 @@ void help_init_menu(ToxWindow *self)
|
|||||||
static void help_exit(ToxWindow *self)
|
static void help_exit(ToxWindow *self)
|
||||||
{
|
{
|
||||||
delwin(self->help->win);
|
delwin(self->help->win);
|
||||||
memset(self->help, 0, sizeof(Help));
|
|
||||||
|
*(self->help) = (struct Help) {
|
||||||
|
0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help_init_window(ToxWindow *self, int height, int width)
|
static void help_init_window(ToxWindow *self, int height, int width)
|
||||||
{
|
{
|
||||||
if (self->help->win)
|
if (self->help->win) {
|
||||||
delwin(self->help->win);
|
delwin(self->help->win);
|
||||||
|
}
|
||||||
|
|
||||||
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,44 +95,63 @@ 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, " c");
|
||||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
wprintw(win, " K");
|
wprintw(win, "o");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "nference 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));
|
||||||
wprintw(win, "it menu\n");
|
wprintw(win, "it menu\n");
|
||||||
|
|
||||||
box(win, ACS_VLINE, ACS_HLINE);
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
wrefresh(win);
|
wnoutrefresh(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help_draw_bottom_menu(WINDOW *win)
|
static void help_draw_bottom_menu(WINDOW *win)
|
||||||
{
|
{
|
||||||
int y2, x2;
|
int y2, x2;
|
||||||
getmaxyx(win, y2, x2);
|
getmaxyx(win, y2, x2);
|
||||||
(void) x2;
|
|
||||||
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
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,32 +168,60 @@ 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, " /conference <type> : Create a conference where type: text | audio\n");
|
||||||
wprintw(win, " /myid : Print your ID\n");
|
wprintw(win, " /myid : Print your Tox ID\n");
|
||||||
|
#ifdef QRCODE
|
||||||
|
#ifdef QRPNG
|
||||||
|
wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n");
|
||||||
|
#else
|
||||||
|
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
|
||||||
|
#endif /* QRPNG */
|
||||||
|
#endif /* QRCODE */
|
||||||
wprintw(win, " /clear : Clear window history\n");
|
wprintw(win, " /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);
|
||||||
|
|
||||||
box(win, ACS_VLINE, ACS_HLINE);
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
wrefresh(win);
|
wnoutrefresh(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help_draw_chat(ToxWindow *self)
|
static void help_draw_chat(ToxWindow *self)
|
||||||
@ -169,30 +234,40 @@ static void help_draw_chat(ToxWindow *self)
|
|||||||
wprintw(win, "Chat Commands:\n");
|
wprintw(win, "Chat Commands:\n");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
|
wprintw(win, " /invite <n> : Invite contact to a conference \n");
|
||||||
wprintw(win, " /join : Join a pending group chat\n");
|
wprintw(win, " /join : Join a pending conference\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 */
|
wprintw(win, " /bitrate <n> : Set the audio encoding bitrate\n");
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Video:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
wprintw(win, " /res <width> <height> : Set video resolution\n");
|
||||||
|
wprintw(win, " /vcall : Video call\n");
|
||||||
|
wprintw(win, " /video : Toggle video in call\n");
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
help_draw_bottom_menu(win);
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
box(win, ACS_VLINE, ACS_HLINE);
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
wrefresh(win);
|
wnoutrefresh(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help_draw_keys(ToxWindow *self)
|
static void help_draw_keys(ToxWindow *self)
|
||||||
@ -205,50 +280,155 @@ static void help_draw_keys(ToxWindow *self)
|
|||||||
wprintw(win, "Key bindings:\n");
|
wprintw(win, "Key bindings:\n");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
|
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
|
||||||
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
|
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
|
||||||
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
|
wprintw(win, " Ctrl+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+up and Ctrl+down : Scroll peer list in conference\n");
|
||||||
|
wprintw(win, " Ctrl+B : Toggle the conference 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);
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
box(win, ACS_VLINE, ACS_HLINE);
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
wrefresh(win);
|
wnoutrefresh(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_draw_conference(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, "Conference commands:\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
wprintw(win, " /title <msg> : Show/set conference title\n");
|
||||||
|
#ifdef AUDIO
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Audio:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
wprintw(win, " /audio <on> or <off> : Enable/disable audio in an audio conference\n");
|
||||||
|
wprintw(win, " /mute : Toggle self audio mute status\n");
|
||||||
|
wprintw(win, " /mute <nick> or <pubkey> : Toggle peer audio mute status\n");
|
||||||
|
wprintw(win, " /ptt <on> or <off> : Toggle audio input Push-To-Talk (F2 to activate)\n");
|
||||||
|
wprintw(win, " /sense <n> : VAD sensitivity threshold\n\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wnoutrefresh(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
static void help_draw_plugin(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, "Plugin commands:\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
draw_handler_help(win);
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wnoutrefresh(win);
|
||||||
|
}
|
||||||
|
#endif /* PYTHON */
|
||||||
|
|
||||||
|
static void help_draw_contacts(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, "Friendlist controls:\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
wprintw(win, " Up and Down arrows : Scroll through list\n");
|
||||||
|
wprintw(win, " Right and Left arrows : Switch between friendlist and blocked list\n");
|
||||||
|
wprintw(win, " Enter : Open a chat window with selected contact\n");
|
||||||
|
wprintw(win, " Delete : Permanently delete a contact\n");
|
||||||
|
wprintw(win, " B : Block or unblock a contact\n");
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wnoutrefresh(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
void help_onKey(ToxWindow *self, wint_t key)
|
void help_onKey(ToxWindow *self, wint_t key)
|
||||||
{
|
{
|
||||||
switch(key) {
|
int height;
|
||||||
case 'x':
|
|
||||||
|
switch (key) {
|
||||||
|
case L'x':
|
||||||
case T_KEY_ESC:
|
case T_KEY_ESC:
|
||||||
help_exit(self);
|
help_exit(self);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c':
|
case L'c':
|
||||||
#ifdef _SUPPORT_AUDIO
|
#ifdef VIDEO
|
||||||
help_init_window(self, 19, 80);
|
help_init_window(self, 25, 80);
|
||||||
|
#elif AUDIO
|
||||||
|
help_init_window(self, 20, 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 L'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 L'o':
|
||||||
|
height = 6;
|
||||||
|
#ifdef AUDIO
|
||||||
|
height += 7;
|
||||||
|
#endif
|
||||||
|
help_init_window(self, height, 80);
|
||||||
|
self->help->type = HELP_CONFERENCE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
|
||||||
|
case L'p':
|
||||||
|
help_init_window(self, 4 + num_registered_handlers(), help_max_width());
|
||||||
|
self->help->type = HELP_PLUGIN;
|
||||||
|
break;
|
||||||
|
#endif /* PYTHON */
|
||||||
|
|
||||||
|
case L'f':
|
||||||
help_init_window(self, 10, 80);
|
help_init_window(self, 10, 80);
|
||||||
|
self->help->type = HELP_CONTACTS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L'k':
|
||||||
|
help_init_window(self, 15, 80);
|
||||||
self->help->type = HELP_KEYS;
|
self->help->type = HELP_KEYS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm':
|
case L'm':
|
||||||
help_init_menu(self);
|
help_init_menu(self);
|
||||||
self->help->type = HELP_MENU;
|
self->help->type = HELP_MENU;
|
||||||
break;
|
break;
|
||||||
@ -257,9 +437,7 @@ void help_onKey(ToxWindow *self, wint_t key)
|
|||||||
|
|
||||||
void help_onDraw(ToxWindow *self)
|
void help_onDraw(ToxWindow *self)
|
||||||
{
|
{
|
||||||
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 +454,19 @@ 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_CONFERENCE:
|
||||||
|
help_draw_conference(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef PYTHON
|
||||||
|
|
||||||
|
case HELP_PLUGIN:
|
||||||
|
help_draw_plugin(self);
|
||||||
|
break;
|
||||||
|
#endif /* PYTHON */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/help.h
12
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"
|
||||||
@ -30,12 +30,16 @@ typedef enum {
|
|||||||
HELP_MENU,
|
HELP_MENU,
|
||||||
HELP_GLOBAL,
|
HELP_GLOBAL,
|
||||||
HELP_CHAT,
|
HELP_CHAT,
|
||||||
HELP_GROUP,
|
HELP_CONFERENCE,
|
||||||
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 /* HELP_H */
|
||||||
|
178
src/input.c
178
src/input.c
@ -26,27 +26,36 @@
|
|||||||
|
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "conference.h"
|
||||||
#include "windows.h"
|
|
||||||
#include "misc_tools.h"
|
|
||||||
#include "toxic_strings.h"
|
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "notify.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
#include "windows.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 mx_x)
|
||||||
{
|
{
|
||||||
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,40 +71,52 @@ 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))
|
} else if (ctx->start) {
|
||||||
ctx->start = MAX(0, ctx->start - cur_len);
|
|
||||||
else if (ctx->start)
|
|
||||||
ctx->start = MAX(0, ctx->start - cur_len);
|
ctx->start = MAX(0, ctx->start - cur_len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* delete a char via delete key from input field and buffer */
|
/* delete a char via delete key from input field and buffer */
|
||||||
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)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (del_word_buf(ctx) == -1) {
|
||||||
|
sound_notify(self, notif_error, 0, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 +124,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,19 +132,19 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* moves cursor/line position to end of line in input field and buffer */
|
/* moves cursor/line position to end of line in input field and buffer */
|
||||||
static void input_mv_end(ToxWindow *self, int y, int mx_x)
|
static void input_mv_end(ToxWindow *self, int mx_x)
|
||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,8 +153,9 @@ static void input_mv_home(ToxWindow *self)
|
|||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
if (ctx->pos <= 0)
|
if (ctx->pos <= 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ctx->pos = 0;
|
ctx->pos = 0;
|
||||||
ctx->start = 0;
|
ctx->start = 0;
|
||||||
@ -144,21 +166,44 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x)
|
|||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
--ctx->pos;
|
--ctx->pos;
|
||||||
|
|
||||||
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
if (ctx->start > 0 && (x >= mx_x - cur_len)) {
|
||||||
|
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
||||||
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))
|
} else if (ctx->start > 0) {
|
||||||
ctx->start = MAX(0, ctx->start - cur_len);
|
|
||||||
else if (ctx->start)
|
|
||||||
ctx->start = MAX(0, ctx->start - cur_len);
|
ctx->start = MAX(0, ctx->start - cur_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* moves the cursor to the beginning of the previous word in input field and buffer */
|
||||||
|
static void input_skip_left(ToxWindow *self, int x, int mx_x)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (ctx->pos <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
--ctx->pos;
|
||||||
|
count += wcwidth(ctx->line[ctx->pos]);
|
||||||
|
} while (ctx->pos > 0 && (ctx->line[ctx->pos - 1] != L' ' || ctx->line[ctx->pos] == L' '));
|
||||||
|
|
||||||
|
if (ctx->start > 0 && (x >= mx_x - count)) {
|
||||||
|
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
||||||
|
ctx->start = MAX(0, ctx->start - 1 + (s_len - count));
|
||||||
|
} else if (ctx->start > 0) {
|
||||||
|
ctx->start = MAX(0, ctx->start - count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* moves cursor/line position right in input field and buffer */
|
/* moves cursor/line position right in input field and buffer */
|
||||||
@ -166,8 +211,9 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
|
|||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
if (ctx->pos >= ctx->len)
|
if (ctx->pos >= ctx->len) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
++ctx->pos;
|
++ctx->pos;
|
||||||
|
|
||||||
@ -179,18 +225,42 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* moves the cursor to the end of the next word in input field and buffer */
|
||||||
|
static void input_skip_right(ToxWindow *self, int x, int mx_x)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (ctx->pos >= ctx->len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
count += wcwidth(ctx->line[ctx->pos]);
|
||||||
|
++ctx->pos;
|
||||||
|
} while (ctx->pos < ctx->len && !(ctx->line[ctx->pos] == L' ' && ctx->line[ctx->pos - 1] != L' '));
|
||||||
|
|
||||||
|
int newpos = x + count;
|
||||||
|
|
||||||
|
if (newpos >= mx_x) {
|
||||||
|
ctx->start += (1 + (newpos - mx_x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* puts a line history item in input field and buffer */
|
/* puts a line history item in input field and buffer */
|
||||||
static void input_history(ToxWindow *self, wint_t key, int mx_x)
|
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.
|
||||||
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 mx_x)
|
||||||
{
|
{
|
||||||
bool match = true;
|
bool match = true;
|
||||||
|
|
||||||
@ -216,6 +286,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);
|
||||||
|
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);
|
||||||
@ -223,7 +297,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
|||||||
|
|
||||||
case KEY_END:
|
case KEY_END:
|
||||||
case T_KEY_C_E:
|
case T_KEY_C_E:
|
||||||
input_mv_end(self, y, mx_x);
|
input_mv_end(self, mx_x);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KEY_LEFT:
|
case KEY_LEFT:
|
||||||
@ -239,10 +313,38 @@ 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;
|
||||||
|
|
||||||
|
case T_KEY_C_LEFT:
|
||||||
|
input_skip_left(self, x, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_KEY_C_RIGHT:
|
||||||
|
input_skip_right(self, x, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
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->type == WINDOW_TYPE_CONFERENCE) {
|
||||||
|
self->show_peerlist ^= 1;
|
||||||
|
redraw_conference_win(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
match = true;
|
||||||
|
} else if (key == user_settings->key_toggle_pastemode) {
|
||||||
|
self->chatwin->pastemode ^= 1;
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
10
src/input.h
10
src/input.h
@ -20,14 +20,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#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 mx_x);
|
||||||
|
|
||||||
/* 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.
|
||||||
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 mx_x);
|
||||||
|
|
||||||
#endif /* #define _input_h */
|
#endif /* INPUT_H */
|
||||||
|
719
src/line_info.c
719
src/line_info.c
@ -20,15 +20,19 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
|
#include "conference.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "message_queue.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "notify.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "line_info.h"
|
|
||||||
#include "groupchat.h"
|
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
extern struct user_settings *user_settings;
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
@ -36,39 +40,42 @@ void line_info_init(struct history *hst)
|
|||||||
{
|
{
|
||||||
hst->line_root = calloc(1, sizeof(struct line_info));
|
hst->line_root = calloc(1, sizeof(struct line_info));
|
||||||
|
|
||||||
if (hst->line_root == NULL)
|
if (hst->line_root == NULL) {
|
||||||
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
|
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
hst->line_start = hst->line_root;
|
hst->line_start = hst->line_root;
|
||||||
hst->line_end = hst->line_start;
|
hst->line_end = hst->line_start;
|
||||||
hst->queue_sz = 0;
|
hst->queue_size = 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;
|
||||||
|
|
||||||
if (line->prev == NULL)
|
if (line == NULL || line->prev == NULL) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int y2, x2;
|
int y2;
|
||||||
|
int x2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
|
int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||||
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
|
int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst;
|
||||||
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
|
|
||||||
|
|
||||||
int curlines = 0;
|
uint16_t curlines = 0;
|
||||||
int nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
curlines += 1 + nxtlines;
|
curlines += line->format_lines;
|
||||||
line = line->prev;
|
line = line->prev;
|
||||||
nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
} while (line->prev && curlines + line->format_lines <= max_y);
|
||||||
} while (line->prev && curlines + nxtlines < max_y);
|
|
||||||
|
|
||||||
hst->line_start = line;
|
hst->line_start = line;
|
||||||
|
|
||||||
|
self->scroll_pause = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void line_info_cleanup(struct history *hst)
|
void line_info_cleanup(struct history *hst)
|
||||||
@ -81,12 +88,13 @@ void line_info_cleanup(struct history *hst)
|
|||||||
tmp1 = tmp2;
|
tmp1 = tmp2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i;
|
for (size_t i = 0; i < hst->queue_size; ++i) {
|
||||||
|
if (hst->queue[i]) {
|
||||||
for (i = 0; i < hst->queue_sz; ++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,57 +113,294 @@ 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_size == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct line_info *ret = hst->queue[0];
|
struct line_info *line = hst->queue[0];
|
||||||
|
|
||||||
int i;
|
for (size_t i = 0; i < hst->queue_size; ++i) {
|
||||||
|
|
||||||
for (i = 0; i < hst->queue_sz; ++i)
|
|
||||||
hst->queue[i] = hst->queue[i + 1];
|
hst->queue[i] = hst->queue[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
--hst->queue_sz;
|
--hst->queue_size;
|
||||||
|
|
||||||
return ret;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* creates new line_info line and puts it in the queue */
|
/* Prints a maximum of `n` chars from `s` to `win`.
|
||||||
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, const char *msg, uint8_t type,
|
*
|
||||||
uint8_t bold, uint8_t colour)
|
* Return 1 if the string contains a newline byte.
|
||||||
|
* Return 0 if string does not contain a newline byte.
|
||||||
|
* Return -1 if printing was aborted.
|
||||||
|
*/
|
||||||
|
static int print_n_chars(WINDOW *win, const char *s, size_t n, int max_y)
|
||||||
{
|
{
|
||||||
|
bool newline = false;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n && (ch = s[i]); ++i) {
|
||||||
|
if (ch == '\n') {
|
||||||
|
newline = true;
|
||||||
|
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
UNUSED_VAR(x);
|
||||||
|
getyx(win, y, x);
|
||||||
|
|
||||||
|
// make sure cursor will wrap correctly after newline to prevent display bugs
|
||||||
|
if (y + 1 >= max_y) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (win) {
|
||||||
|
wprintw(win, "%c", ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the index of the last space character in `s` found before `limit`.
|
||||||
|
* Returns -1 if no space is found.
|
||||||
|
*/
|
||||||
|
static int rspace_index(const char *s, int limit)
|
||||||
|
{
|
||||||
|
for (int i = limit; i >= 0; --i) {
|
||||||
|
if (s[i] == ' ') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the first index in `s` containing a newline byte found before `limit`.
|
||||||
|
* Returns -1 if no newline is found.
|
||||||
|
*/
|
||||||
|
static int newline_index(const char *s, int limit)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
for (int i = 0; i < limit && (ch = s[i]); ++i) {
|
||||||
|
if (ch == '\n') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of newline bytes in `s` */
|
||||||
|
static unsigned int newline_count(const char *s)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
unsigned int count = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; (ch = s[i]); ++i) {
|
||||||
|
if (ch == '\n') {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prints `line` message to window, wrapping at the last word that fits on the current line.
|
||||||
|
* This function updates the `format_lines` field of `line` according to current window dimensions.
|
||||||
|
*
|
||||||
|
* If `win` is null nothing will be printed to the window. This is useful to set the
|
||||||
|
* `format_lines` field on initialization.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if not all characters in line's message were printed to screen.
|
||||||
|
*/
|
||||||
|
static int print_wrap(WINDOW *win, struct line_info *line, int max_x, int max_y)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
UNUSED_VAR(y);
|
||||||
|
|
||||||
|
const char *msg = line->msg;
|
||||||
|
uint16_t length = line->msg_len;
|
||||||
|
uint16_t lines = 0;
|
||||||
|
const int x_start = line->len - line->msg_len - 1; // manually keep track of x position because ncurses sucks
|
||||||
|
int x_limit = max_x - x_start;
|
||||||
|
|
||||||
|
if (x_limit <= 1) {
|
||||||
|
fprintf(stderr, "Warning: x_limit <= 0 in print_wrap(): %d\n", x_limit);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (msg) {
|
||||||
|
getyx(win, y, x);
|
||||||
|
|
||||||
|
// next line would print past window limit so we abort; we don't want to update format_lines
|
||||||
|
if (x > x_start) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length < x_limit) {
|
||||||
|
int p_ret = print_n_chars(win, msg, length, max_y);
|
||||||
|
|
||||||
|
if (p_ret == 1) {
|
||||||
|
lines += newline_count(msg);
|
||||||
|
} else if (p_ret == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
++lines;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newline_idx = newline_index(msg, x_limit - 1);
|
||||||
|
|
||||||
|
if (newline_idx >= 0) {
|
||||||
|
if (print_n_chars(win, msg, newline_idx + 1, max_y) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += (newline_idx + 1);
|
||||||
|
length -= (newline_idx + 1);
|
||||||
|
x_limit = max_x; // if we find a newline we stop adding column padding for rest of message
|
||||||
|
++lines;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int space_idx = rspace_index(msg, x_limit - 1);
|
||||||
|
|
||||||
|
if (space_idx >= 1) {
|
||||||
|
if (print_n_chars(win, msg, space_idx, max_y) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += space_idx + 1;
|
||||||
|
length -= (space_idx + 1);
|
||||||
|
|
||||||
|
if (win) {
|
||||||
|
waddch(win, '\n');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (print_n_chars(win, msg, x_limit, max_y) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += x_limit;
|
||||||
|
length -= x_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add padding to the start of the next line
|
||||||
|
if (win && x_limit < max_x) {
|
||||||
|
for (size_t i = 0; i < x_start; ++i) {
|
||||||
|
waddch(win, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (win && line->noread_flag) {
|
||||||
|
getyx(win, y, x);
|
||||||
|
|
||||||
|
if (x >= max_x - 1 || x == x_start) {
|
||||||
|
++lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
wprintw(win, " x");
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
}
|
||||||
|
|
||||||
|
line->format_lines = lines;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void line_info_init_line(ToxWindow *self, struct line_info *line)
|
||||||
|
{
|
||||||
|
int y2;
|
||||||
|
int x2;
|
||||||
|
UNUSED_VAR(y2);
|
||||||
|
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT;
|
||||||
|
const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2;
|
||||||
|
|
||||||
|
print_wrap(NULL, line, max_x, max_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates new line_info line and puts it in the queue.
|
||||||
|
*
|
||||||
|
* Returns the id of the new line.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type,
|
||||||
|
uint8_t bold, uint8_t colour, const char *msg, ...)
|
||||||
|
{
|
||||||
|
if (!self) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
struct history *hst = self->chatwin->hst;
|
struct history *hst = self->chatwin->hst;
|
||||||
|
|
||||||
|
if (hst->queue_size >= MAX_LINE_INFO_QUEUE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
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];
|
||||||
|
frmt_msg[0] = 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; // two spaces
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IN_MSG:
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
case OUT_MSG:
|
||||||
|
len += strlen(user_settings->line_normal) + 3; // two spaces and a ':' char
|
||||||
|
break;
|
||||||
|
|
||||||
case CONNECTION:
|
case CONNECTION:
|
||||||
len += 3;
|
len += strlen(user_settings->line_join) + 2; // two spaces
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISCONNECTION:
|
||||||
|
len += strlen(user_settings->line_quit) + 2; // two spaces
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SYS_MSG:
|
case SYS_MSG:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NAME_CHANGE:
|
||||||
|
len += strlen(user_settings->line_alert) + 2; // two spaces
|
||||||
|
break;
|
||||||
|
|
||||||
case PROMPT:
|
case PROMPT:
|
||||||
++len;
|
len += 2; // '$' char and a space
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -163,21 +408,17 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, cons
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg) {
|
uint16_t msg_len = 0;
|
||||||
snprintf(new_line->msg, sizeof(new_line->msg), "%s", msg);
|
|
||||||
len += strlen(new_line->msg);
|
|
||||||
|
|
||||||
int i;
|
if (frmt_msg[0]) {
|
||||||
|
snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
|
||||||
for (i = 0; msg[i]; ++i) {
|
msg_len = strlen(new_line->msg);
|
||||||
if (msg[i] == '\n')
|
len += msg_len;
|
||||||
++new_line->newlines;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmstmp) {
|
if (show_timestamp) {
|
||||||
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
|
get_time_str(new_line->timestr, sizeof(new_line->timestr));
|
||||||
len += strlen(new_line->timestamp);
|
len += strlen(new_line->timestr) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name1) {
|
if (name1) {
|
||||||
@ -190,59 +431,55 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, cons
|
|||||||
len += strlen(new_line->name2);
|
len += strlen(new_line->name2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_line->id = (hst->line_end->id + 1 + hst->queue_size) % INT_MAX;
|
||||||
new_line->len = len;
|
new_line->len = len;
|
||||||
|
new_line->msg_len = msg_len;
|
||||||
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);
|
line_info_init_line(self, new_line);
|
||||||
|
|
||||||
|
hst->queue[hst->queue_size++] = new_line;
|
||||||
|
|
||||||
|
return new_line->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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() */
|
||||||
static void line_info_check_queue(ToxWindow *self)
|
static void line_info_check_queue(ToxWindow *self)
|
||||||
{
|
{
|
||||||
struct history *hst = self->chatwin->hst;
|
struct history *hst = self->chatwin->hst;
|
||||||
struct line_info *line = line_info_ret_queue(hst);
|
struct line_info *line = line_info_ret_queue(hst);
|
||||||
|
|
||||||
if (line == NULL)
|
if (line == NULL) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (hst->start_id > user_settings->history_size)
|
if (hst->start_id > user_settings->history_size) {
|
||||||
line_info_root_fwd(hst);
|
line_info_root_fwd(hst);
|
||||||
|
}
|
||||||
|
|
||||||
line->id = hst->line_end->id + 1;
|
|
||||||
line->prev = hst->line_end;
|
line->prev = hst->line_end;
|
||||||
hst->line_end->next = line;
|
hst->line_end->next = line;
|
||||||
hst->line_end = line;
|
hst->line_end = line;
|
||||||
|
hst->line_end->id = line->id;
|
||||||
|
|
||||||
int y, y2, x, x2;
|
if (!self->scroll_pause) {
|
||||||
getmaxyx(self->window, y2, x2);
|
line_info_reset_start(self, hst);
|
||||||
getyx(self->chatwin->history, y, x);
|
|
||||||
(void) x;
|
|
||||||
|
|
||||||
if (x2 <= SIDEBAR_WIDTH)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
|
||||||
int lines = 1 + line->newlines + (line->len / (x2 - offst));
|
|
||||||
int max_y = y2 - CHATBOX_HEIGHT;
|
|
||||||
|
|
||||||
/* move line_start forward proportionate to the number of new lines */
|
|
||||||
if (y + lines - 1 >= max_y) {
|
|
||||||
while (lines > 0 && hst->line_start->next) {
|
|
||||||
lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst));
|
|
||||||
hst->line_start = hst->line_start->next;
|
|
||||||
++hst->start_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
if (ctx == NULL)
|
if (ctx == NULL) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct history *hst = ctx->hst;
|
struct history *hst = ctx->hst;
|
||||||
|
|
||||||
@ -250,83 +487,151 @@ void line_info_print(ToxWindow *self)
|
|||||||
line_info_check_queue(self);
|
line_info_check_queue(self);
|
||||||
|
|
||||||
WINDOW *win = ctx->history;
|
WINDOW *win = ctx->history;
|
||||||
|
|
||||||
wclear(win);
|
wclear(win);
|
||||||
int y2, x2;
|
|
||||||
|
int y2;
|
||||||
|
|
||||||
|
int x2;
|
||||||
|
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (x2 <= SIDEBAR_WIDTH)
|
if (x2 - 1 <= SIDEBAR_WIDTH) { // leave room on x axis for sidebar padding
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->is_groupchat)
|
if (self->type == WINDOW_TYPE_CONFERENCE) {
|
||||||
wmove(win, 0, 0);
|
wmove(win, 0, 0);
|
||||||
else
|
} else {
|
||||||
wmove(win, 2, 0);
|
wmove(win, TOP_BAR_HEIGHT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
struct line_info *line = hst->line_start->next;
|
struct line_info *line = hst->line_start->next;
|
||||||
int numlines = 0;
|
|
||||||
|
|
||||||
while (line && numlines++ <= y2) {
|
if (!line) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT;
|
||||||
|
const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2;
|
||||||
|
uint16_t numlines = line->format_lines;
|
||||||
|
int print_ret = 0;
|
||||||
|
|
||||||
|
while (line && numlines++ <= max_y && print_ret == 0) {
|
||||||
|
int y;
|
||||||
|
int x;
|
||||||
|
UNUSED_VAR(y);
|
||||||
|
|
||||||
|
getyx(win, y, x);
|
||||||
|
|
||||||
|
if (x > 0) { // Prevents us from printing off the screen
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t type = line->type;
|
uint8_t type = line->type;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
if (line->colour)
|
if (line->colour) {
|
||||||
nameclr = line->colour;
|
nameclr = line->colour;
|
||||||
else if (type == IN_MSG)
|
} else if (type == IN_MSG) {
|
||||||
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] == '>')
|
if (line->msg[0] == 0) {
|
||||||
|
waddch(win, '\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line->msg[0] == '>') {
|
||||||
wattron(win, COLOR_PAIR(GREEN));
|
wattron(win, COLOR_PAIR(GREEN));
|
||||||
|
} else if (line->msg[0] == '<') {
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
}
|
||||||
|
|
||||||
wprintw(win, "%s\n", line->msg);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
|
||||||
if (line->msg[0] == '>')
|
if (line->msg[0] == '>') {
|
||||||
wattroff(win, COLOR_PAIR(GREEN));
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
|
} else if (line->msg[0] == '<') {
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == OUT_MSG && !line->read_flag) {
|
||||||
|
if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||||
|
line->noread_flag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waddch(win, '\n');
|
||||||
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 ", user_settings->line_normal, line->name1);
|
||||||
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
wattroff(win, COLOR_PAIR(YELLOW));
|
wattroff(win, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
|
if (type == OUT_ACTION && !line->read_flag) {
|
||||||
|
if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||||
|
line->noread_flag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waddch(win, '\n');
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line->bold)
|
if (line->bold) {
|
||||||
wattron(win, A_BOLD);
|
wattron(win, A_BOLD);
|
||||||
|
}
|
||||||
|
|
||||||
if (line->colour)
|
if (line->colour) {
|
||||||
wattron(win, COLOR_PAIR(line->colour));
|
wattron(win, COLOR_PAIR(line->colour));
|
||||||
|
}
|
||||||
|
|
||||||
wprintw(win, "%s\n", line->msg);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
waddch(win, '\n');
|
||||||
|
|
||||||
if (line->bold)
|
if (line->bold) {
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
|
}
|
||||||
|
|
||||||
if (line->colour)
|
if (line->colour) {
|
||||||
wattroff(win, COLOR_PAIR(line->colour));
|
wattroff(win, COLOR_PAIR(line->colour));
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -335,37 +640,63 @@ void line_info_print(ToxWindow *self)
|
|||||||
wprintw(win, "$ ");
|
wprintw(win, "$ ");
|
||||||
wattroff(win, COLOR_PAIR(GREEN));
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
if (line->msg[0])
|
if (line->msg[0]) {
|
||||||
wprintw(win, "%s", line->msg);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
}
|
||||||
|
|
||||||
wprintw(win, "\n");
|
waddch(win, '\n');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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);
|
|
||||||
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
waddch(win, '\n');
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
waddch(win, '\n');
|
||||||
|
|
||||||
wattroff(win, COLOR_PAIR(line->colour));
|
wattroff(win, COLOR_PAIR(line->colour));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
|
||||||
wattron(win, A_BOLD);
|
wattron(win, A_BOLD);
|
||||||
wprintw(win, "%s\n", line->name2);
|
wprintw(win, "%s\n", line->name2);
|
||||||
@ -379,16 +710,53 @@ void line_info_print(ToxWindow *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* keep calling until queue is empty */
|
/* keep calling until queue is empty */
|
||||||
if (hst->queue_sz > 0)
|
if (hst->queue_size > 0) {
|
||||||
line_info_print(self);
|
line_info_print(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return true if all lines starting from `line` can fit on the screen.
|
||||||
|
*/
|
||||||
|
static bool line_info_screen_fit(ToxWindow *self, struct line_info *line)
|
||||||
|
{
|
||||||
|
if (!line) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x2;
|
||||||
|
int y2;
|
||||||
|
getmaxyx(self->chatwin->history, y2, x2);
|
||||||
|
|
||||||
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
|
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||||
|
const int max_y = y2 - top_offset;
|
||||||
|
|
||||||
|
uint16_t lines = line->format_lines;
|
||||||
|
|
||||||
|
while (line) {
|
||||||
|
if (lines > max_y) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines += line->format_lines;
|
||||||
|
line = line->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
while (line) {
|
while (line) {
|
||||||
if (line->id == id) {
|
if (line->id == id) {
|
||||||
|
size_t new_len = strlen(msg);
|
||||||
|
line->len = line->len - line->msg_len + new_len;
|
||||||
|
line->msg_len = new_len;
|
||||||
snprintf(line->msg, sizeof(line->msg), "%s", msg);
|
snprintf(line->msg, sizeof(line->msg), "%s", msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -397,47 +765,75 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static void line_info_goto_root(struct history *hst)
|
static void line_info_scroll_up(ToxWindow *self, struct history *hst)
|
||||||
{
|
{
|
||||||
hst->line_start = hst->line_root;
|
if (hst->line_start->prev) {
|
||||||
} */
|
|
||||||
|
|
||||||
static void line_info_scroll_up(struct history *hst)
|
|
||||||
{
|
|
||||||
if (hst->line_start->prev)
|
|
||||||
hst->line_start = hst->line_start->prev;
|
hst->line_start = hst->line_start->prev;
|
||||||
else beep();
|
self->scroll_pause = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_info_scroll_down(struct history *hst)
|
static void line_info_scroll_down(ToxWindow *self, struct history *hst)
|
||||||
{
|
{
|
||||||
if (hst->line_start->next)
|
struct line_info *next = hst->line_start->next;
|
||||||
hst->line_start = hst->line_start->next;
|
|
||||||
else beep();
|
if (next && self->scroll_pause) {
|
||||||
|
if (line_info_screen_fit(self, next->next)) {
|
||||||
|
line_info_reset_start(self, hst);
|
||||||
|
} else {
|
||||||
|
hst->line_start = next;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
line_info_reset_start(self, hst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
||||||
{
|
{
|
||||||
int x2, y2;
|
int x2;
|
||||||
|
int y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
(void) x2;
|
|
||||||
int jump_dist = y2 / 2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < jump_dist && hst->line_start->prev; ++i)
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
|
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||||
|
const int max_y = y2 - top_offset;
|
||||||
|
size_t jump_dist = max_y / 2;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < jump_dist && hst->line_start->prev; ++i) {
|
||||||
hst->line_start = hst->line_start->prev;
|
hst->line_start = hst->line_start->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->scroll_pause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_info_page_down(ToxWindow *self, struct history *hst)
|
static void line_info_page_down(ToxWindow *self, struct history *hst)
|
||||||
{
|
{
|
||||||
int x2, y2;
|
if (!self->scroll_pause) {
|
||||||
getmaxyx(self->window, y2, x2);
|
return;
|
||||||
(void) x2;
|
}
|
||||||
int jump_dist = y2 / 2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < jump_dist && hst->line_start->next; ++i)
|
int x2;
|
||||||
hst->line_start = hst->line_start->next;
|
int y2;
|
||||||
|
getmaxyx(self->chatwin->history, y2, x2);
|
||||||
|
|
||||||
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
|
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||||
|
const int max_y = y2 - top_offset;
|
||||||
|
size_t jump_dist = max_y / 2;
|
||||||
|
|
||||||
|
struct line_info *next = hst->line_start->next;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < jump_dist && next; ++i) {
|
||||||
|
if (line_info_screen_fit(self, next->next)) {
|
||||||
|
line_info_reset_start(self, hst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hst->line_start = next;
|
||||||
|
next = hst->line_start->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool line_info_onKey(ToxWindow *self, wint_t key)
|
bool line_info_onKey(ToxWindow *self, wint_t key)
|
||||||
@ -445,35 +841,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(self, 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(self, 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;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user