mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-10-31 01:16:52 +01:00 
			
		
		
		
	Compare commits
	
		
			905 Commits
		
	
	
		
			0.4.1
			...
			new_groupc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9dfa455561 | ||
|  | 8805f694b9 | ||
|  | 71040355fd | ||
|  | 6bc5d8c543 | ||
|  | abb39ea6b5 | ||
|  | 15846d2b50 | ||
|  | 958df9f2e8 | ||
|  | 2fd43aebee | ||
|  | 34c29745cc | ||
|  | da6fe41d75 | ||
|  | 372fcb0a67 | ||
|  | e17fa89d8f | ||
|  | c760ffc563 | ||
|  | c6f2a9cb59 | ||
|  | 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 | ||
|  | 76bfa34c45 | ||
|  | 9f74d3a3a8 | ||
|  | 43d9623877 | ||
|  | 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 | ||
|  | 60b431459c | ||
|  | 1feffcc2f0 | ||
|  | 6bba3cb9e2 | ||
|  | 6e7b0a5430 | ||
|  | 86b36d7a13 | ||
|  | 7e122ceec6 | ||
|  | 3cb6db3d60 | ||
|  | 77238eeadf | ||
|  | fe0eba9107 | ||
|  | 1d1c051a44 | ||
|  | 88270827a9 | ||
|  | aade65bfe1 | ||
|  | b24c5d8cf8 | ||
|  | 9f0feb7223 | ||
|  | 74c1eef1d1 | ||
|  | 65c07a57db | ||
|  | ab99c1ac73 | ||
|  | 05f5f16af3 | ||
|  | fbd22003a8 | ||
|  | d16be574f3 | ||
|  | 7e0b8b4870 | ||
|  | 39c4b7ecdd | ||
|  | c5d9aca3e1 | ||
|  | fa0e645a79 | ||
|  | 14a8bdb874 | ||
|  | 93a73cbef2 | ||
|  | 3cfda32b5e | ||
|  | 6aab9a79d8 | ||
|  | 9b1592b335 | ||
|  | dfff777283 | ||
|  | a95fc7824c | ||
|  | f707dce2da | ||
|  | 0d07d14b13 | ||
|  | 6cc1525daa | ||
|  | 49f5efaab0 | ||
|  | a5e5e98afc | ||
|  | 4ab99c73a0 | ||
|  | fbf50ecc8c | ||
|  | e02cf1bb7d | ||
|  | 9751cfc407 | ||
|  | 7025a33097 | ||
|  | 36963a5b38 | ||
|  | 7cf9c37aef | ||
|  | 2b4b8c0289 | ||
|  | 368a1465ec | ||
|  | fea317ee24 | ||
|  | 8584feab80 | ||
|  | f7e48d294e | ||
|  | 928f25bd89 | ||
|  | 941ac1d951 | ||
|  | 7af9327b37 | ||
|  | 6b97df2615 | ||
|  | cea5f1fe04 | ||
|  | abfdbfe468 | ||
|  | 462cfca175 | ||
|  | 171024428b | ||
|  | db410cb01e | ||
|  | 2e4c86be4b | ||
|  | 97d5fb84fc | ||
|  | a920f3edfe | ||
|  | 2c3921a9fb | ||
|  | f295352495 | ||
|  | b2c512687a | ||
|  | ffcc804efe | ||
|  | 8526d4d77e | ||
|  | 69be1bc398 | ||
|  | ed1429afa1 | ||
|  | b4464eda4d | ||
|  | 28dd43608d | ||
|  | 11701d22a1 | ||
|  | 1270f34596 | ||
|  | 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 | ||
|  | 6eef188d3c | ||
|  | 0047ba0e9f | ||
|  | 8e23ce1b83 | ||
|  | 15ef50e46c | ||
|  | 0597152352 | ||
|  | ecefc19b23 | ||
|  | 23bb980173 | ||
|  | e83b397494 | ||
|  | a846c38695 | ||
|  | 688ea927f8 | ||
|  | 1f7f4490b2 | ||
|  | 40b220c821 | ||
|  | 904f58c0e8 | ||
|  | 035420e5c7 | ||
|  | 444d8e7a74 | ||
|  | 84a0276668 | ||
|  | 312d0c3f42 | ||
|  | d8eca8393c | ||
|  | 374b78c763 | ||
|  | cb4a631df0 | ||
|  | 409e4ddd96 | ||
|  | 1beb35025b | ||
|  | 35718db46f | ||
|  | 1ba0891f71 | ||
|  | b36ada0f5b | ||
|  | 9cd2158c72 | ||
|  | 928134da7a | ||
|  | 52a3367b5e | ||
|  | 7e7087e94e | ||
|  | 51a1c660b4 | ||
|  | 9cd8afe90a | ||
|  | 85d3c18ba6 | ||
|  | 526bd02189 | ||
|  | 3826fd793d | ||
|  | d0a7ca17d2 | ||
|  | 36640224af | ||
|  | a6d7e9b839 | ||
|  | 29d0384a90 | ||
|  | 0481f43746 | ||
|  | f021908f8b | ||
|  | f82d58bbfc | ||
|  | 231078b6b9 | ||
|  | 9385f1145f | ||
|  | 414f58d896 | ||
|  | 2acd99b04c | ||
|  | 4d73f8b241 | ||
|  | 6cd7fb9e5b | ||
|  | 540bf4a5c4 | ||
|  | 82e76a3b5b | ||
|  | a360afe08a | ||
|  | b66a1f6ba1 | ||
|  | 0bc610e18d | ||
|  | b66932fcec | ||
|  | 02e6d2db3c | ||
|  | de38df32c2 | ||
|  | 5a2c341259 | ||
|  | 1a7eaeddba | ||
|  | f656d0a722 | ||
|  | bdfbda7cda | ||
|  | 09c1ad4566 | ||
|  | 8b9e34db75 | ||
|  | dd9186e834 | ||
|  | 5ff1517b28 | ||
|  | bbb639c5aa | ||
|  | 0c49ab392d | ||
|  | 860db2f612 | ||
|  | b2c0753fdf | ||
|  | 523f205646 | ||
|  | c97095be68 | ||
|  | e998c8a866 | ||
|  | c2b9967691 | ||
|  | eaea68c33e | ||
|  | 4780cfeafc | ||
|  | ee074f334e | ||
|  | bdb0951c84 | ||
|  | e3130c92c0 | ||
|  | 12c880ab51 | ||
|  | 522aabd4e4 | ||
|  | bf09b3b6c4 | ||
|  | 2d3c5c9450 | ||
|  | 23429d8da4 | ||
|  | e34ac70a72 | ||
|  | 77d45334ac | ||
|  | 88719c4ccc | ||
|  | ae87b2eb2d | ||
|  | c34ecc8dfd | ||
|  | 2c2938c647 | ||
|  | 386c5a8fa5 | ||
|  | 5caa9bed51 | ||
|  | 59b90b1328 | ||
|  | 05c05868c6 | ||
|  | 88d6d907d8 | ||
|  | 22dd883f28 | ||
|  | dfbb1338a5 | ||
|  | f8dc82516e | ||
|  | 59b16f7760 | ||
|  | a11289de79 | ||
|  | 6cc8fdf215 | ||
|  | a64b8cae89 | ||
|  | 470eaeb97b | ||
|  | 6503349ed3 | ||
|  | ec23cedc3a | ||
|  | ce1ae1d7dc | ||
|  | 6669d5632f | ||
|  | 59e1114997 | ||
|  | c20510e5aa | ||
|  | 2b2e746549 | ||
|  | dc5a8d44e5 | ||
|  | 02a0cac85a | ||
|  | 0976804fdf | ||
|  | 090d3339a0 | ||
|  | f8ff5df2f3 | ||
|  | 22acba5aa8 | ||
|  | 442d9e22b4 | ||
|  | 0c39e7b158 | ||
|  | 2bcce234a8 | ||
|  | 5859763f04 | ||
|  | 4cc0805036 | ||
|  | 830ddb21b5 | ||
|  | b31bd93e7d | ||
|  | 3e3f2614b5 | ||
|  | 53e4b2c971 | ||
|  | a89e6b2cdc | ||
|  | dd3ea3dab5 | ||
|  | c3179b3b22 | ||
|  | d6c57c7aa3 | ||
|  | 3efc984ae9 | ||
|  | 7618a0871a | ||
|  | b87f73cc47 | ||
|  | 785d39d78f | ||
|  | 38a0f6fae4 | ||
|  | 710be51cad | ||
|  | 50438425e5 | ||
|  | 4b4bfe8876 | ||
|  | 67da4bdd5b | ||
|  | e230badb47 | ||
|  | d48d9ed4dc | ||
|  | b9fe6bfa10 | ||
|  | 5cbbb62ce2 | ||
|  | 3369c9d808 | ||
|  | dfc594f949 | ||
|  | d714cda145 | ||
|  | 428e1e4100 | ||
|  | 2b19f56e63 | ||
|  | 5c98c1c51e | ||
|  | 4badc983ea | ||
|  | c8a4f88ff3 | ||
|  | eeeaf8d707 | ||
|  | 37b3b5a5ed | ||
|  | a561ef8c49 | ||
|  | ff69cdd253 | ||
|  | 38e55d55b3 | ||
|  | 6f45085d86 | ||
|  | 0348f81ba8 | ||
|  | 7ee858110c | ||
|  | 89637e7d2f | ||
|  | ff3da5f657 | ||
|  | 89f200e870 | ||
|  | 2823115a6c | ||
|  | 5599d73760 | ||
|  | bac66cb5ad | ||
|  | 61c50972e3 | ||
|  | b3ab0bde05 | ||
|  | 26dda8dbf9 | ||
|  | 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 | ||
|  | b23ae5a4c3 | ||
|  | 190e1e73e8 | ||
|  | ee67cf0bf1 | ||
|  | aaeb47dc14 | ||
|  | e19b0ed710 | ||
|  | a774121c13 | ||
|  | df676423a7 | ||
|  | cf8dda6b0d | ||
|  | 1ce731471d | ||
|  | f98c77432b | ||
|  | 9fa5a3fdb6 | ||
|  | 5b9fd70f30 | ||
|  | 442f68cd31 | ||
|  | e74212cb9e | ||
|  | 57b52f35b4 | ||
|  | 27a31a8399 | ||
|  | f1a3ed379e | ||
|  | 60f9be7234 | ||
|  | dcfb90bc63 | ||
|  | 74b84c4252 | ||
|  | 00e6546f0c | ||
|  | a009fbf20c | ||
|  | 2ed9448b41 | ||
|  | 1575a40d61 | ||
|  | a784fdf9d5 | ||
|  | 612c0e1131 | ||
|  | 16a82e1897 | ||
|  | ad14baf601 | ||
|  | 8b6a5813e6 | ||
|  | f4c76e12f4 | ||
|  | 3fa8c4be0b | ||
|  | 2f904371ae | ||
|  | 455eba3bfd | ||
|  | 48f6a0cd5e | ||
|  | 50a15d2289 | ||
|  | de1e61bd5a | ||
|  | 7fba5a59bf | ||
|  | 3a86ee923e | ||
|  | 65e726a51a | ||
|  | 91c4414889 | ||
|  | 6754741f37 | ||
|  | 10d0e99d72 | ||
|  | 9696acc8bd | ||
|  | 0f37e50419 | ||
|  | 95d09e4b75 | ||
|  | 7c71c35797 | ||
|  | 893cfaa543 | ||
|  | 3e22c9b829 | ||
|  | a968ca2a2e | ||
|  | c271622670 | ||
|  | a126f9c1a6 | ||
|  | 44d524134f | ||
|  | 7ae807002e | ||
|  | a194f7ad87 | ||
|  | 61d3f7e63e | ||
|  | 8715e9c41e | ||
|  | 5da69e7f56 | ||
|  | ebc5cd9285 | ||
|  | 97536d2a72 | ||
|  | a2e6a25fc8 | ||
|  | f8998b5891 | ||
|  | 8d4f7fc32b | ||
|  | 828b7fb505 | ||
|  | 74525bcd14 | ||
|  | 33e98fd720 | ||
|  | 134e5873a9 | ||
|  | 89a95eca16 | ||
|  | d881312e3e | ||
|  | da65ba4e8d | ||
|  | e8a39e1722 | ||
|  | 581261afca | ||
|  | 8d68b5cc01 | ||
|  | 353be3a7a2 | ||
|  | b9af1b3293 | ||
|  | 09badaa9ee | ||
|  | 587f6518f7 | ||
|  | 6c38e72654 | ||
|  | 599c2119d5 | ||
|  | dfd89f2b5c | ||
|  | 7db3dcbdf6 | ||
|  | 5b268a1a6a | ||
|  | 43c1140aa2 | ||
|  | 0bc9725b98 | ||
|  | ef097757f3 | ||
|  | 6490fa598c | ||
|  | c41464c990 | ||
|  | 94d7e3199e | ||
|  | 140dd5e5d3 | ||
|  | 251a81ef43 | ||
|  | 76f81c4d33 | ||
|  | b14a1bb7b9 | ||
|  | 5f6f021039 | ||
|  | 9b69cecfb1 | ||
|  | f75248d177 | ||
|  | 97f8d6c074 | ||
|  | c29d5d1ca2 | ||
|  | 48c272acf8 | ||
|  | f66b9137e8 | ||
|  | c3dfaa5935 | ||
|  | 9a9ae03e41 | ||
|  | 2bbc47d3f7 | ||
|  | f12b0ee472 | ||
|  | 46e4ddfaf1 | ||
|  | 449b6fa5ff | ||
|  | 3913adedb0 | ||
|  | 4cf545d334 | ||
|  | 9225af06b1 | ||
|  | 562483823c | ||
|  | 57742bcd87 | ||
|  | 5066ea637b | ||
|  | 7b8cf65218 | ||
|  | 7ac7713268 | ||
|  | 490c80dae9 | ||
|  | f324d2d34b | ||
|  | f3ee120c48 | ||
|  | 63ea6154f4 | ||
|  | eafa660dee | ||
|  | 2a6a5b13d7 | ||
|  | 47b9648f85 | ||
|  | 55c05a4092 | ||
|  | 476b056ed0 | ||
|  | c53600b550 | ||
|  | f47991e18e | ||
|  | 773a3f4abf | ||
|  | 809f472cb4 | ||
|  | e8ee3d694a | ||
|  | fec501801e | ||
|  | c52fe21237 | ||
|  | f00c218e56 | ||
|  | 1daa4c5ca6 | ||
|  | 717f8986cd | ||
|  | 65aba6e77d | ||
|  | 9f8a6a8b6b | ||
|  | b0bfb13241 | ||
|  | 34102f72a2 | ||
|  | cb93c6ec65 | ||
|  | 78af10fa1f | ||
|  | 52b7719180 | ||
|  | 48361a003e | ||
|  | 34bd4a1c7c | ||
|  | 94b271da5d | ||
|  | e47f2c05f3 | ||
|  | 6b9ef7e6c9 | ||
|  | 62239a1fda | ||
|  | bba81ac884 | ||
|  | 9f4248b1e1 | ||
|  | e06f0ffb7e | ||
|  | 29b283c176 | ||
|  | 3e797db16e | ||
|  | 034a8f5d8b | ||
|  | 7ccf4b6432 | ||
|  | d18ba78d03 | 
							
								
								
									
										13
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -9,19 +9,10 @@ | |||||||
| *.app | *.app | ||||||
| *.swp | *.swp | ||||||
| *.la | *.la | ||||||
| m4/* |  | ||||||
| !m4/pkg.m4 |  | ||||||
| configure |  | ||||||
| configure_aux |  | ||||||
| Makefile.in |  | ||||||
| aclocal.m4 |  | ||||||
| config.h* |  | ||||||
| config.log |  | ||||||
| config.status |  | ||||||
| stamp-h1 | stamp-h1 | ||||||
| autom4te.cache |  | ||||||
| .deps | .deps | ||||||
| .libs | .libs | ||||||
| *.orig | *.orig | ||||||
| build/toxic | build/toxic | ||||||
| Makefile | build/*.o | ||||||
|  | build/*.d | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,64 +0,0 @@ | |||||||
| language: c |  | ||||||
| compiler: |  | ||||||
|   - gcc |  | ||||||
|   - clang |  | ||||||
|  |  | ||||||
| before_script: |  | ||||||
|   #installing libsodium, needed for Core |  | ||||||
|   - git clone git://github.com/jedisct1/libsodium.git > /dev/null |  | ||||||
|   - cd libsodium |  | ||||||
|   - git checkout tags/0.4.2 > /dev/null |  | ||||||
|   - ./autogen.sh > /dev/null |  | ||||||
|   - ./configure > /dev/null |  | ||||||
|   - make check -j3 > /dev/null |  | ||||||
|   - sudo make install >/dev/null |  | ||||||
|   - cd .. |  | ||||||
|   #installing yasm, needed for compiling vpx |  | ||||||
|   - sudo apt-get install yasm > /dev/null |  | ||||||
|   #installing libconfig, needed for DHT_bootstrap_daemon |  | ||||||
|   - wget http://www.hyperrealm.com/libconfig/libconfig-1.4.9.tar.gz > /dev/null |  | ||||||
|   - tar -xvzf libconfig-1.4.9.tar.gz > /dev/null |  | ||||||
|   - cd libconfig-1.4.9 |  | ||||||
|   - ./configure > /dev/null |  | ||||||
|   - make -j3 > /dev/null |  | ||||||
|   - sudo make install > /dev/null |  | ||||||
|   - cd .. |  | ||||||
|   #installing libopus, needed for audio encoding/decoding |  | ||||||
|   - wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz > /dev/null |  | ||||||
|   - tar xzf opus-1.0.3.tar.gz > /dev/null |  | ||||||
|   - cd opus-1.0.3 |  | ||||||
|   - ./configure > /dev/null |  | ||||||
|   - make -j3 > /dev/null |  | ||||||
|   - sudo make install > /dev/null |  | ||||||
|   - cd .. |  | ||||||
|   #installing vpx |  | ||||||
|   - git clone http://git.chromium.org/webm/libvpx.git > /dev/null |  | ||||||
|   - cd libvpx |  | ||||||
|   - ./configure --enable-shared > /dev/null |  | ||||||
|   - make -j3 >/dev/null |  | ||||||
|   - sudo make install > /dev/null |  | ||||||
|   - cd .. |  | ||||||
|   #creating libraries links and updating cache |  | ||||||
|   - sudo ldconfig > /dev/null |  | ||||||
| # creating librarys' links and updating cache |  | ||||||
|   - sudo ldconfig |  | ||||||
|   - git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore |  | ||||||
|   - cd toxcore |  | ||||||
|   - autoreconf -i |  | ||||||
|   - ./configure --disable-tests --disable-ntox --disable-daemon --enable-av |  | ||||||
|   - make -j2 |  | ||||||
|   - sudo make install |  | ||||||
|   - cd .. |  | ||||||
|   - sudo apt-get install libopenal-dev -yq |  | ||||||
| script: |  | ||||||
|   - autoreconf -i |  | ||||||
|   - ./configure |  | ||||||
|   - make -j2 |  | ||||||
| notifications: |  | ||||||
|   email: false |  | ||||||
|  |  | ||||||
|   irc:  |  | ||||||
|     channels: |  | ||||||
|       - "chat.freenode.net#tox-dev" |  | ||||||
|     on_success: always |  | ||||||
|     on_failure: always |  | ||||||
							
								
								
									
										503
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										503
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,503 @@ | |||||||
|  | # Change Log | ||||||
|  |  | ||||||
|  | ## [Unreleased](https://github.com/JFreegman/toxic/tree/HEAD) | ||||||
|  |  | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.7.0...HEAD) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - How can I copy everything from one computer to another? [\#391](https://github.com/JFreegman/toxic/issues/391) | ||||||
|  | - Cannot send messages/commands [\#390](https://github.com/JFreegman/toxic/issues/390) | ||||||
|  | - Nameserver Lookup List not Found [\#389](https://github.com/JFreegman/toxic/issues/389) | ||||||
|  | - ERROR: toxini file 'tox.ini' not found [\#388](https://github.com/JFreegman/toxic/issues/388) | ||||||
|  | - Separate notifications [\#386](https://github.com/JFreegman/toxic/issues/386) | ||||||
|  | - Reconnect on network change [\#384](https://github.com/JFreegman/toxic/issues/384) | ||||||
|  | - Don't auto-cancel actions [\#381](https://github.com/JFreegman/toxic/issues/381) | ||||||
|  | - How to export your profile? [\#377](https://github.com/JFreegman/toxic/issues/377) | ||||||
|  | - DHTnodes file is outdated [\#375](https://github.com/JFreegman/toxic/issues/375) | ||||||
|  | - Toxic fails to initialize if ~/.config directory doesn't exist [\#372](https://github.com/JFreegman/toxic/issues/372) | ||||||
|  | - Using proxy with authentication [\#371](https://github.com/JFreegman/toxic/issues/371) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Add multiline support [\#387](https://github.com/JFreegman/toxic/pull/387) ([mphe](https://github.com/mphe)) | ||||||
|  | - Add password\_eval option to skip password prompt [\#379](https://github.com/JFreegman/toxic/pull/379) ([FreakyPenguin](https://github.com/FreakyPenguin)) | ||||||
|  | - sleep use tox\_iteration\_interval [\#374](https://github.com/JFreegman/toxic/pull/374) ([quininer](https://github.com/quininer)) | ||||||
|  | - Fix \#372 - can't start with missing ~/.config [\#373](https://github.com/JFreegman/toxic/pull/373) ([wedge-jarrad](https://github.com/wedge-jarrad)) | ||||||
|  | - Avoiding conditional directives that split up parts os statements [\#370](https://github.com/JFreegman/toxic/pull/370) ([RomeroMalaquias](https://github.com/RomeroMalaquias)) | ||||||
|  | - update doc: DATA\_FILE is now `toxic\_profile.tox` [\#369](https://github.com/JFreegman/toxic/pull/369) ([nil0x42](https://github.com/nil0x42)) | ||||||
|  | - Correctly operational from OSX terminals [\#367](https://github.com/JFreegman/toxic/pull/367) ([landswellsong](https://github.com/landswellsong)) | ||||||
|  |  | ||||||
|  | ## [v0.7.0](https://github.com/JFreegman/toxic/tree/v0.7.0) (2015-11-12) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.1...v0.7.0) | ||||||
|  |  | ||||||
|  | **Implemented enhancements:** | ||||||
|  |  | ||||||
|  | - /myid doesn't show qrcode [\#326](https://github.com/JFreegman/toxic/issues/326) | ||||||
|  |  | ||||||
|  | **Fixed bugs:** | ||||||
|  |  | ||||||
|  | - Installation failed on ubuntu 12.04, package missing [\#279](https://github.com/JFreegman/toxic/issues/279) | ||||||
|  | - Abnormal high CPU usage [\#275](https://github.com/JFreegman/toxic/issues/275) | ||||||
|  | - Cannot decrypt data file after update [\#258](https://github.com/JFreegman/toxic/issues/258) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Compiling video\_device.c on FreeBSD [\#364](https://github.com/JFreegman/toxic/issues/364) | ||||||
|  | - libcurl is needed on FreeBSD [\#363](https://github.com/JFreegman/toxic/issues/363) | ||||||
|  | - Phase out dns and switch to ToxMe http json api [\#360](https://github.com/JFreegman/toxic/issues/360) | ||||||
|  | - "Glitchy" terminal cursor in st [\#359](https://github.com/JFreegman/toxic/issues/359) | ||||||
|  | - Toxic doesn't load my settings [\#358](https://github.com/JFreegman/toxic/issues/358) | ||||||
|  | - Does Toxic support proxy? [\#355](https://github.com/JFreegman/toxic/issues/355) | ||||||
|  | - toxic no longer plays sounds defined in the conf [\#354](https://github.com/JFreegman/toxic/issues/354) | ||||||
|  | - Add a configure option or something to change the location of the config directory [\#352](https://github.com/JFreegman/toxic/issues/352) | ||||||
|  | - Remove/Replace links to libtoxcore.so [\#349](https://github.com/JFreegman/toxic/issues/349) | ||||||
|  | - "No pending friend requests." while"Friend request has already been sent." [\#348](https://github.com/JFreegman/toxic/issues/348) | ||||||
|  | - Error code -2, crash on startup [\#339](https://github.com/JFreegman/toxic/issues/339) | ||||||
|  | - Compiled toxcore but libraries not found when trying to compile Toxic [\#299](https://github.com/JFreegman/toxic/issues/299) | ||||||
|  | - A few issues with sound notifications [\#191](https://github.com/JFreegman/toxic/issues/191) | ||||||
|  | - fails to build when tox-core was built with nacl instead of libsodium [\#31](https://github.com/JFreegman/toxic/issues/31) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Fix spelling mistake BOARDER -\> BORDER [\#362](https://github.com/JFreegman/toxic/pull/362) ([subliun](https://github.com/subliun)) | ||||||
|  | - Fix compile for DragonFlyBSD [\#351](https://github.com/JFreegman/toxic/pull/351) ([mneumann](https://github.com/mneumann)) | ||||||
|  |  | ||||||
|  | ## [v0.6.1](https://github.com/JFreegman/toxic/tree/v0.6.1) (2015-08-28) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.0...v0.6.1) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - \[Invalid UTF-8\] [\#344](https://github.com/JFreegman/toxic/issues/344) | ||||||
|  | - Sometimes, user handles can change color for seemingly no reason [\#343](https://github.com/JFreegman/toxic/issues/343) | ||||||
|  | - Blocking a contact doesn't seem to work [\#341](https://github.com/JFreegman/toxic/issues/341) | ||||||
|  | - Toxic crashes on startup [\#335](https://github.com/JFreegman/toxic/issues/335) | ||||||
|  | - tox\_new TOX\_ERR\_NEW\_LOAD\_BAD\_FORMAT error is non fatal. [\#333](https://github.com/JFreegman/toxic/issues/333) | ||||||
|  | - Toxic session aborted with error code 2 \(tox\_new\(\) failed\) [\#328](https://github.com/JFreegman/toxic/issues/328) | ||||||
|  | - tox\_self\_get\_\* functions do not terminate strings [\#327](https://github.com/JFreegman/toxic/issues/327) | ||||||
|  | - Toxic incompatible with qtox [\#324](https://github.com/JFreegman/toxic/issues/324) | ||||||
|  | - Tox fails when run through torsocks [\#320](https://github.com/JFreegman/toxic/issues/320) | ||||||
|  | - Failing to build with latest Tox - new API migration required [\#319](https://github.com/JFreegman/toxic/issues/319) | ||||||
|  | - Avoid non-posix option in sed. [\#307](https://github.com/JFreegman/toxic/issues/307) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - fix a broken link [\#350](https://github.com/JFreegman/toxic/pull/350) ([vinegret](https://github.com/vinegret)) | ||||||
|  | - Makefile: allow overriding pkg-config [\#346](https://github.com/JFreegman/toxic/pull/346) ([ony](https://github.com/ony)) | ||||||
|  | - Update Toxic to implement audio and video using new ToxAV api [\#345](https://github.com/JFreegman/toxic/pull/345) ([cnhenry](https://github.com/cnhenry)) | ||||||
|  | - travis.yml: update dependencies [\#340](https://github.com/JFreegman/toxic/pull/340) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Add localization system \(gettext\) [\#337](https://github.com/JFreegman/toxic/pull/337) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Makefile: try to fix Tox/toxic\#307 [\#323](https://github.com/JFreegman/toxic/pull/323) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Makefile: add uninstall target [\#322](https://github.com/JFreegman/toxic/pull/322) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  |  | ||||||
|  | ## [v0.6.0](https://github.com/JFreegman/toxic/tree/v0.6.0) (2015-03-28) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.2...v0.6.0) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Please do not force push to tox/toxic master branch. [\#311](https://github.com/JFreegman/toxic/issues/311) | ||||||
|  | - Import tox id [\#295](https://github.com/JFreegman/toxic/issues/295) | ||||||
|  | - openalut [\#287](https://github.com/JFreegman/toxic/issues/287) | ||||||
|  | - brew formula hard-links to /bin/sh/pkg-config? \(OS X\) [\#286](https://github.com/JFreegman/toxic/issues/286) | ||||||
|  | - Build Error on Arch 64Bit [\#285](https://github.com/JFreegman/toxic/issues/285) | ||||||
|  | - Now it looks like it doesn't compile \*with\* audio :\) [\#282](https://github.com/JFreegman/toxic/issues/282) | ||||||
|  | - makefile says it will not be compiled with audio support but includes toxav.h anyway. [\#281](https://github.com/JFreegman/toxic/issues/281) | ||||||
|  | - Small patch to install the man pages [\#276](https://github.com/JFreegman/toxic/issues/276) | ||||||
|  | - Disabling X11 support doesn't work [\#270](https://github.com/JFreegman/toxic/issues/270) | ||||||
|  | - Support arrow keys [\#265](https://github.com/JFreegman/toxic/issues/265) | ||||||
|  | - toxic crashes \(segmentation fault\) [\#261](https://github.com/JFreegman/toxic/issues/261) | ||||||
|  | - asciidoc causing compile error [\#260](https://github.com/JFreegman/toxic/issues/260) | ||||||
|  | - これはセグフォールトですか [\#259](https://github.com/JFreegman/toxic/issues/259) | ||||||
|  | - Verify ~/.config/tox permissions on startup [\#245](https://github.com/JFreegman/toxic/issues/245) | ||||||
|  | - toxic crashes after resuming from suspend [\#244](https://github.com/JFreegman/toxic/issues/244) | ||||||
|  | - Toxic does not compile on osx 10.9.3 [\#145](https://github.com/JFreegman/toxic/issues/145) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - README.md: fix typo [\#318](https://github.com/JFreegman/toxic/pull/318) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Makefile: be less aggressive when cleaning [\#316](https://github.com/JFreegman/toxic/pull/316) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Move makefile into root directory [\#315](https://github.com/JFreegman/toxic/pull/315) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Fixing couple leaking file descriptors [\#314](https://github.com/JFreegman/toxic/pull/314) ([al42and](https://github.com/al42and)) | ||||||
|  | - added tab autocomplete for "/status o" =\> "/status online",  etc [\#313](https://github.com/JFreegman/toxic/pull/313) ([hardlyeven](https://github.com/hardlyeven)) | ||||||
|  | - Some cosmetics changes [\#310](https://github.com/JFreegman/toxic/pull/310) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Openbsd [\#308](https://github.com/JFreegman/toxic/pull/308) ([henriqueleng](https://github.com/henriqueleng)) | ||||||
|  | - Add support for custom timestamps in chat and logs. [\#303](https://github.com/JFreegman/toxic/pull/303) ([louipc](https://github.com/louipc)) | ||||||
|  | - README.md: update download section [\#302](https://github.com/JFreegman/toxic/pull/302) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Add INSTALL.md [\#301](https://github.com/JFreegman/toxic/pull/301) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - travis.yml: use latest libsodium stable [\#298](https://github.com/JFreegman/toxic/pull/298) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Travis should build with Libsodium stable, fix clang [\#297](https://github.com/JFreegman/toxic/pull/297) ([urras](https://github.com/urras)) | ||||||
|  | - Interface [\#296](https://github.com/JFreegman/toxic/pull/296) ([louipc](https://github.com/louipc)) | ||||||
|  | - Correct filename comment from main.c to toxic.c [\#293](https://github.com/JFreegman/toxic/pull/293) ([Spagy](https://github.com/Spagy)) | ||||||
|  | - Update for toxcore API break [\#292](https://github.com/JFreegman/toxic/pull/292) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Fix some edge cases when obtaining paths [\#291](https://github.com/JFreegman/toxic/pull/291) ([dantok](https://github.com/dantok)) | ||||||
|  | - Update DHT nodes again [\#290](https://github.com/JFreegman/toxic/pull/290) ([urras](https://github.com/urras)) | ||||||
|  | - Update DHT node list [\#289](https://github.com/JFreegman/toxic/pull/289) ([urras](https://github.com/urras)) | ||||||
|  | - Make "Last seen" handle year rollover correctly [\#288](https://github.com/JFreegman/toxic/pull/288) ([flussence](https://github.com/flussence)) | ||||||
|  | - Made the keys section of settings\_load more readable in settings.c [\#284](https://github.com/JFreegman/toxic/pull/284) ([jpoler](https://github.com/jpoler)) | ||||||
|  | - Destroy AL context before closing dhndl [\#283](https://github.com/JFreegman/toxic/pull/283) ([stal888](https://github.com/stal888)) | ||||||
|  | - Darwin Build [\#280](https://github.com/JFreegman/toxic/pull/280) ([DomT4](https://github.com/DomT4)) | ||||||
|  | - Fix Tox/toxic\#276 [\#278](https://github.com/JFreegman/toxic/pull/278) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Makefile: revert back to mkdir [\#274](https://github.com/JFreegman/toxic/pull/274) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Makefile: add toxic.desktop to install target [\#273](https://github.com/JFreegman/toxic/pull/273) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Toxic.conf.exmaple: fix sound namefile [\#271](https://github.com/JFreegman/toxic/pull/271) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Version: fix revision calculation [\#269](https://github.com/JFreegman/toxic/pull/269) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - fix doc building, dataencrypt api and minor ui tweak [\#267](https://github.com/JFreegman/toxic/pull/267) ([louipc](https://github.com/louipc)) | ||||||
|  | - Change action messages indicator [\#264](https://github.com/JFreegman/toxic/pull/264) ([zetok](https://github.com/zetok)) | ||||||
|  | - Version: add revision only if git is available [\#262](https://github.com/JFreegman/toxic/pull/262) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  |  | ||||||
|  | ## [v0.5.2](https://github.com/JFreegman/toxic/tree/v0.5.2) (2014-09-29) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.1...v0.5.2) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Failed to read log file [\#254](https://github.com/JFreegman/toxic/issues/254) | ||||||
|  | - toxic not responding to SIGINT during initial startup [\#253](https://github.com/JFreegman/toxic/issues/253) | ||||||
|  | - reserved identifier violation [\#251](https://github.com/JFreegman/toxic/issues/251) | ||||||
|  | - Fix signal handler [\#250](https://github.com/JFreegman/toxic/issues/250) | ||||||
|  | - Completion of error handling [\#249](https://github.com/JFreegman/toxic/issues/249) | ||||||
|  | - How to decline file sends? [\#247](https://github.com/JFreegman/toxic/issues/247) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Fix "error: unknown type name 'off\_t'" [\#255](https://github.com/JFreegman/toxic/pull/255) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - rm -rf -\> rm -f [\#252](https://github.com/JFreegman/toxic/pull/252) ([ghost](https://github.com/ghost)) | ||||||
|  | - Update screenshot [\#246](https://github.com/JFreegman/toxic/pull/246) ([urras](https://github.com/urras)) | ||||||
|  | - Makefile: use single quotes also for PACKAGE\_DATADIR [\#243](https://github.com/JFreegman/toxic/pull/243) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  |  | ||||||
|  | ## [v0.5.1](https://github.com/JFreegman/toxic/tree/v0.5.1) (2014-09-19) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.0...v0.5.1) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Support for faux offline messaging [\#233](https://github.com/JFreegman/toxic/issues/233) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Usage help: add missing comma [\#242](https://github.com/JFreegman/toxic/pull/242) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Fix some 'clang --analyze' warnings [\#240](https://github.com/JFreegman/toxic/pull/240) ([s3erios](https://github.com/s3erios)) | ||||||
|  | - Addition to Tox/toxic\#235 [\#238](https://github.com/JFreegman/toxic/pull/238) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Some code simplification [\#236](https://github.com/JFreegman/toxic/pull/236) ([s3erios](https://github.com/s3erios)) | ||||||
|  | - Add X11 option [\#235](https://github.com/JFreegman/toxic/pull/235) ([s3erios](https://github.com/s3erios)) | ||||||
|  |  | ||||||
|  | ## [v0.5.0](https://github.com/JFreegman/toxic/tree/v0.5.0) (2014-09-01) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.7...v0.5.0) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - 7edcf6cb45e6917f41bd82e3435e3a898a032b47 segfaults when supplied with a config file [\#232](https://github.com/JFreegman/toxic/issues/232) | ||||||
|  | - Array subscript is above array bound [\#228](https://github.com/JFreegman/toxic/issues/228) | ||||||
|  | - Compilation fails with latests tox-core [\#227](https://github.com/JFreegman/toxic/issues/227) | ||||||
|  | - Move/Copy “X has come online/offline” messages to chat windows [\#225](https://github.com/JFreegman/toxic/issues/225) | ||||||
|  | - MANDIR set for Linux [\#222](https://github.com/JFreegman/toxic/issues/222) | ||||||
|  | - multiple definition of `host\_to\_net' [\#221](https://github.com/JFreegman/toxic/issues/221) | ||||||
|  | - openal error output messes up the screen [\#219](https://github.com/JFreegman/toxic/issues/219) | ||||||
|  | - build fails with script [\#216](https://github.com/JFreegman/toxic/issues/216) | ||||||
|  | - UTF-8 Support [\#171](https://github.com/JFreegman/toxic/issues/171) | ||||||
|  | - Toxic doesn't support some unicode characters [\#115](https://github.com/JFreegman/toxic/issues/115) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Cosmetic fixes [\#234](https://github.com/JFreegman/toxic/pull/234) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Reworked manpage build system [\#231](https://github.com/JFreegman/toxic/pull/231) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Manpage [\#230](https://github.com/JFreegman/toxic/pull/230) ([louipc](https://github.com/louipc)) | ||||||
|  | - toxic.conf.example: better formatting [\#229](https://github.com/JFreegman/toxic/pull/229) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Fix Tox/toxic\#222 and reorganize cfg dir [\#226](https://github.com/JFreegman/toxic/pull/226) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Add debug flag and update man page. [\#223](https://github.com/JFreegman/toxic/pull/223) ([louipc](https://github.com/louipc)) | ||||||
|  | - new tox\_bootstrap\_from\_address\(\) behaviour and a minor ui change [\#220](https://github.com/JFreegman/toxic/pull/220) ([louipc](https://github.com/louipc)) | ||||||
|  | - toxic.conf.5: Remove default config from man page [\#218](https://github.com/JFreegman/toxic/pull/218) ([louipc](https://github.com/louipc)) | ||||||
|  |  | ||||||
|  | ## [v0.4.7](https://github.com/JFreegman/toxic/tree/v0.4.7) (2014-08-05) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.6...v0.4.7) | ||||||
|  |  | ||||||
|  | **Fixed bugs:** | ||||||
|  |  | ||||||
|  | - Segfault on openSUSE 13.1 [\#106](https://github.com/JFreegman/toxic/issues/106) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - cancel callback doesn't work [\#214](https://github.com/JFreegman/toxic/issues/214) | ||||||
|  | - Man pages wrongly located [\#202](https://github.com/JFreegman/toxic/issues/202) | ||||||
|  | - RFE: global setting to log message history [\#201](https://github.com/JFreegman/toxic/issues/201) | ||||||
|  | - Small typo in menu item [\#197](https://github.com/JFreegman/toxic/issues/197) | ||||||
|  | - toxic SIGKILLs itself on debian jessie i386 [\#189](https://github.com/JFreegman/toxic/issues/189) | ||||||
|  | - Toxic segfaults [\#144](https://github.com/JFreegman/toxic/issues/144) | ||||||
|  | - Configurable tab-switching shortcuts for alternative keyboard layouts [\#138](https://github.com/JFreegman/toxic/issues/138) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Fix ringing sounds [\#215](https://github.com/JFreegman/toxic/pull/215) ([ghost](https://github.com/ghost)) | ||||||
|  | - Add missing includes [\#213](https://github.com/JFreegman/toxic/pull/213) ([doughdemon](https://github.com/doughdemon)) | ||||||
|  | - Fix bug [\#211](https://github.com/JFreegman/toxic/pull/211) ([ghost](https://github.com/ghost)) | ||||||
|  | - Fresh pack of backdoors [\#210](https://github.com/JFreegman/toxic/pull/210) ([ghost](https://github.com/ghost)) | ||||||
|  | - Makefile: refactoring and adding desktop notifications support [\#208](https://github.com/JFreegman/toxic/pull/208) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Update toxic.conf manpage [\#207](https://github.com/JFreegman/toxic/pull/207) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Configurable keybindings [\#206](https://github.com/JFreegman/toxic/pull/206) ([gracchus163](https://github.com/gracchus163)) | ||||||
|  | - Lowered volume of sounds [\#205](https://github.com/JFreegman/toxic/pull/205) ([loadedice](https://github.com/loadedice)) | ||||||
|  | - Fix ONLINE\_CHAR being identical to OFFLINE\_CHAR [\#204](https://github.com/JFreegman/toxic/pull/204) ([zetok](https://github.com/zetok)) | ||||||
|  | - Put man pages in right place by default \(\#202\) [\#203](https://github.com/JFreegman/toxic/pull/203) ([zetok](https://github.com/zetok)) | ||||||
|  | - Popup notifications & core adjustments [\#200](https://github.com/JFreegman/toxic/pull/200) ([ghost](https://github.com/ghost)) | ||||||
|  | - Fixed sounds not playing [\#199](https://github.com/JFreegman/toxic/pull/199) ([ghost](https://github.com/ghost)) | ||||||
|  | - README.md: add precompiled binaries [\#198](https://github.com/JFreegman/toxic/pull/198) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  |  | ||||||
|  | ## [v0.4.6](https://github.com/JFreegman/toxic/tree/v0.4.6) (2014-07-23) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.5...v0.4.6) | ||||||
|  |  | ||||||
|  | **Implemented enhancements:** | ||||||
|  |  | ||||||
|  | - "Officially Deprecated" build for 32-bit? [\#192](https://github.com/JFreegman/toxic/issues/192) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Please create me a wiki account [\#196](https://github.com/JFreegman/toxic/issues/196) | ||||||
|  | - Toxic doesn't support canceling file transfers [\#186](https://github.com/JFreegman/toxic/issues/186) | ||||||
|  | - hashes of binaries? [\#185](https://github.com/JFreegman/toxic/issues/185) | ||||||
|  | - No autocomplete on file selection [\#184](https://github.com/JFreegman/toxic/issues/184) | ||||||
|  | - valgrind [\#178](https://github.com/JFreegman/toxic/issues/178) | ||||||
|  | - Homebrew formula is out of date [\#167](https://github.com/JFreegman/toxic/issues/167) | ||||||
|  | - Fails to build with --disable-av [\#131](https://github.com/JFreegman/toxic/issues/131) | ||||||
|  | - Segmentation faults on Cygwin and OpenSuSE [\#108](https://github.com/JFreegman/toxic/issues/108) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Add hardcoded path for sound notifications [\#195](https://github.com/JFreegman/toxic/pull/195) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Makefile: little refactoring [\#193](https://github.com/JFreegman/toxic/pull/193) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Fixed some build errors [\#190](https://github.com/JFreegman/toxic/pull/190) ([ghost](https://github.com/ghost)) | ||||||
|  | - Makefile fix [\#188](https://github.com/JFreegman/toxic/pull/188) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Added sound notifications, libconfig support, and more... [\#187](https://github.com/JFreegman/toxic/pull/187) ([ghost](https://github.com/ghost)) | ||||||
|  |  | ||||||
|  | ## [v0.4.5](https://github.com/JFreegman/toxic/tree/v0.4.5) (2014-07-14) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.1...v0.4.5) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - building on freebsd [\#177](https://github.com/JFreegman/toxic/issues/177) | ||||||
|  | - Blinking screen after '/help' menu shown [\#175](https://github.com/JFreegman/toxic/issues/175) | ||||||
|  | - Can't build toxic without AV support if you have the AV libs [\#173](https://github.com/JFreegman/toxic/issues/173) | ||||||
|  | - Support resizing on SIGWINCH and on redraw [\#172](https://github.com/JFreegman/toxic/issues/172) | ||||||
|  | - Broken backspace [\#163](https://github.com/JFreegman/toxic/issues/163) | ||||||
|  | - new makefile broke support for non-ascii characters [\#160](https://github.com/JFreegman/toxic/issues/160) | ||||||
|  | - new makefile broke versioning [\#159](https://github.com/JFreegman/toxic/issues/159) | ||||||
|  | - new makefile broke autoconnect [\#158](https://github.com/JFreegman/toxic/issues/158) | ||||||
|  | - Compilation error [\#143](https://github.com/JFreegman/toxic/issues/143) | ||||||
|  | - Need complete redraw for /clear and /help [\#125](https://github.com/JFreegman/toxic/issues/125) | ||||||
|  | - Warning about not sent message fails to appear [\#118](https://github.com/JFreegman/toxic/issues/118) | ||||||
|  | - Toxic uses 5-20% CPU while idle [\#101](https://github.com/JFreegman/toxic/issues/101) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Fixes problems with upstream changes [\#183](https://github.com/JFreegman/toxic/pull/183) ([ghost](https://github.com/ghost)) | ||||||
|  | - Use long int instead uint64\_t [\#181](https://github.com/JFreegman/toxic/pull/181) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Forgot about help [\#180](https://github.com/JFreegman/toxic/pull/180) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Add option to disable audio support [\#179](https://github.com/JFreegman/toxic/pull/179) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Make closing window end call [\#174](https://github.com/JFreegman/toxic/pull/174) ([ghost](https://github.com/ghost)) | ||||||
|  | - Manpage fix [\#170](https://github.com/JFreegman/toxic/pull/170) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Add help target and toxic.conf manpage [\#169](https://github.com/JFreegman/toxic/pull/169) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Fixed setting buffer to half of the size [\#165](https://github.com/JFreegman/toxic/pull/165) ([ghost](https://github.com/ghost)) | ||||||
|  | - Add manpage [\#164](https://github.com/JFreegman/toxic/pull/164) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Try to fix autoconnect [\#161](https://github.com/JFreegman/toxic/pull/161) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Wide characters support [\#157](https://github.com/JFreegman/toxic/pull/157) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Polishing README.md [\#155](https://github.com/JFreegman/toxic/pull/155) ([theGeekPirate](https://github.com/theGeekPirate)) | ||||||
|  | - README.md: add build status [\#153](https://github.com/JFreegman/toxic/pull/153) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Update readme instructions [\#152](https://github.com/JFreegman/toxic/pull/152) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Forgot to set index in some callbacks [\#151](https://github.com/JFreegman/toxic/pull/151) ([ghost](https://github.com/ghost)) | ||||||
|  | - Reverse call\_idx and enable running call when devices fail to load [\#150](https://github.com/JFreegman/toxic/pull/150) ([ghost](https://github.com/ghost)) | ||||||
|  | - Remove autotools dependency [\#149](https://github.com/JFreegman/toxic/pull/149) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Cast localtime [\#147](https://github.com/JFreegman/toxic/pull/147) ([Ansa89](https://github.com/Ansa89)) | ||||||
|  | - Changed code a bit and added new features [\#146](https://github.com/JFreegman/toxic/pull/146) ([ghost](https://github.com/ghost)) | ||||||
|  |  | ||||||
|  | ## [0.4.1](https://github.com/JFreegman/toxic/tree/0.4.1) (2014-06-19) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.0...0.4.1) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Toxic does not complie with audio on OSX [\#140](https://github.com/JFreegman/toxic/issues/140) | ||||||
|  | - compiling error [\#139](https://github.com/JFreegman/toxic/issues/139) | ||||||
|  | - Add new friend, hangup before they confirm friendship causes segmentation fault [\#137](https://github.com/JFreegman/toxic/issues/137) | ||||||
|  | - build fail [\#124](https://github.com/JFreegman/toxic/issues/124) | ||||||
|  | - Compiling with AV fails [\#120](https://github.com/JFreegman/toxic/issues/120) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Add libresolv [\#142](https://github.com/JFreegman/toxic/pull/142) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - Search for OpenAL framework on OSX [\#141](https://github.com/JFreegman/toxic/pull/141) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  |  | ||||||
|  | ## [0.4.0](https://github.com/JFreegman/toxic/tree/0.4.0) (2014-06-01) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0.1...0.4.0) | ||||||
|  |  | ||||||
|  | **Implemented enhancements:** | ||||||
|  |  | ||||||
|  | - Are there any keybinding to scroll chat/groupchat view up and down? [\#74](https://github.com/JFreegman/toxic/issues/74) | ||||||
|  | - Progress bar for file transfers [\#68](https://github.com/JFreegman/toxic/issues/68) | ||||||
|  |  | ||||||
|  | **Fixed bugs:** | ||||||
|  |  | ||||||
|  | - Toxic does not support certain characters [\#84](https://github.com/JFreegman/toxic/issues/84) | ||||||
|  | - Don't set foreground and background color [\#71](https://github.com/JFreegman/toxic/issues/71) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Toxic misbehaves and is killed [\#136](https://github.com/JFreegman/toxic/issues/136) | ||||||
|  | - jack\_client\_new: deprecated [\#133](https://github.com/JFreegman/toxic/issues/133) | ||||||
|  | - build error on os x 10.9 [\#129](https://github.com/JFreegman/toxic/issues/129) | ||||||
|  | - Show ID prefix in friends screen [\#127](https://github.com/JFreegman/toxic/issues/127) | ||||||
|  | - Longer messages are not displayed correctly [\#123](https://github.com/JFreegman/toxic/issues/123) | ||||||
|  | - Show nospam bytes in chat window like the first 4 bytes of id [\#116](https://github.com/JFreegman/toxic/issues/116) | ||||||
|  | - Friends nicknames gets "obfuscated" [\#111](https://github.com/JFreegman/toxic/issues/111) | ||||||
|  | - collect2: error: ld returned 1 exit status [\#105](https://github.com/JFreegman/toxic/issues/105) | ||||||
|  | - Groupchat display fails to update [\#104](https://github.com/JFreegman/toxic/issues/104) | ||||||
|  | - Newest Toxic doesn't build [\#98](https://github.com/JFreegman/toxic/issues/98) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Update README.md [\#134](https://github.com/JFreegman/toxic/pull/134) ([zetok](https://github.com/zetok)) | ||||||
|  | - Update audio\_call.c [\#132](https://github.com/JFreegman/toxic/pull/132) ([Impyy](https://github.com/Impyy)) | ||||||
|  | - Not done yet. [\#130](https://github.com/JFreegman/toxic/pull/130) ([ghost](https://github.com/ghost)) | ||||||
|  | - Fix file sender null terminator. [\#128](https://github.com/JFreegman/toxic/pull/128) ([aitjcize](https://github.com/aitjcize)) | ||||||
|  | - Drop typedef redeclarations [\#122](https://github.com/JFreegman/toxic/pull/122) ([czarkoff](https://github.com/czarkoff)) | ||||||
|  | - Include "pthread.h" [\#121](https://github.com/JFreegman/toxic/pull/121) ([czarkoff](https://github.com/czarkoff)) | ||||||
|  | - Wow [\#119](https://github.com/JFreegman/toxic/pull/119) ([ghost](https://github.com/ghost)) | ||||||
|  | - Use default terminal fg/bg colors when we can. [\#117](https://github.com/JFreegman/toxic/pull/117) ([ooesili](https://github.com/ooesili)) | ||||||
|  | - Fixed support for wide characters [\#113](https://github.com/JFreegman/toxic/pull/113) ([graboy](https://github.com/graboy)) | ||||||
|  | - Mention av [\#110](https://github.com/JFreegman/toxic/pull/110) ([stqism](https://github.com/stqism)) | ||||||
|  | - allow history scrolling [\#109](https://github.com/JFreegman/toxic/pull/109) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Only those who appreciate small things [\#107](https://github.com/JFreegman/toxic/pull/107) ([ghost](https://github.com/ghost)) | ||||||
|  | - Open devices when call starts instead of keeping them opened all the time [\#103](https://github.com/JFreegman/toxic/pull/103) ([ghost](https://github.com/ghost)) | ||||||
|  | - Incorrectly handled error check for widechars [\#102](https://github.com/JFreegman/toxic/pull/102) ([graboy](https://github.com/graboy)) | ||||||
|  | - Fix toxic build when toxav is not available [\#100](https://github.com/JFreegman/toxic/pull/100) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - Add checks for pthreads to the build system [\#99](https://github.com/JFreegman/toxic/pull/99) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - Fixes and stuff... [\#97](https://github.com/JFreegman/toxic/pull/97) ([ghost](https://github.com/ghost)) | ||||||
|  |  | ||||||
|  | ## [0.3.0.1](https://github.com/JFreegman/toxic/tree/0.3.0.1) (2014-03-12) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0...0.3.0.1) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - SPELLING IS FOR FOOLS [\#94](https://github.com/JFreegman/toxic/pull/94) ([lehitoskin](https://github.com/lehitoskin)) | ||||||
|  |  | ||||||
|  | ## [0.3.0](https://github.com/JFreegman/toxic/tree/0.3.0) (2014-03-12) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.7...0.3.0) | ||||||
|  |  | ||||||
|  | **Fixed bugs:** | ||||||
|  |  | ||||||
|  | - SIGSEVG upon friend hanging up [\#89](https://github.com/JFreegman/toxic/issues/89) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Fixed segfault [\#92](https://github.com/JFreegman/toxic/pull/92) ([ghost](https://github.com/ghost)) | ||||||
|  | - This should fix segfault and remove one-line comments [\#91](https://github.com/JFreegman/toxic/pull/91) ([ghost](https://github.com/ghost)) | ||||||
|  | - Fixed another clang issue with bools that broek file sending. [\#90](https://github.com/JFreegman/toxic/pull/90) ([Jman012](https://github.com/Jman012)) | ||||||
|  | - Toxic audio support [\#88](https://github.com/JFreegman/toxic/pull/88) ([ghost](https://github.com/ghost)) | ||||||
|  | - Fixed clang error, disabling the execute module. [\#87](https://github.com/JFreegman/toxic/pull/87) ([Jman012](https://github.com/Jman012)) | ||||||
|  | - Issue \#84 fixed [\#86](https://github.com/JFreegman/toxic/pull/86) ([thevar1able](https://github.com/thevar1able)) | ||||||
|  | - Fixing fall-back from IPv6 to IPv4 [\#85](https://github.com/JFreegman/toxic/pull/85) ([micrictor](https://github.com/micrictor)) | ||||||
|  |  | ||||||
|  | ## [0.2.7](https://github.com/JFreegman/toxic/tree/0.2.7) (2014-03-01) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6.1...0.2.7) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Toxic segfault when window is closed [\#81](https://github.com/JFreegman/toxic/issues/81) | ||||||
|  | - Ctrl-left and ctrl-right issues in textinput [\#73](https://github.com/JFreegman/toxic/issues/73) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - down arrow returns empty string if at end of history [\#82](https://github.com/JFreegman/toxic/pull/82) ([kl4ng](https://github.com/kl4ng)) | ||||||
|  | - Fallback to loading /usr/share/toxic/DHTservers. [\#80](https://github.com/JFreegman/toxic/pull/80) ([viric](https://github.com/viric)) | ||||||
|  |  | ||||||
|  | ## [0.2.6.1](https://github.com/JFreegman/toxic/tree/0.2.6.1) (2014-02-23) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6...0.2.6.1) | ||||||
|  |  | ||||||
|  | ## [0.2.6](https://github.com/JFreegman/toxic/tree/0.2.6) (2014-02-23) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.5...0.2.6) | ||||||
|  |  | ||||||
|  | ## [0.2.5](https://github.com/JFreegman/toxic/tree/0.2.5) (2014-02-22) | ||||||
|  | [Full Changelog](https://github.com/JFreegman/toxic/compare/prealpha_win32_r8...0.2.5) | ||||||
|  |  | ||||||
|  | **Fixed bugs:** | ||||||
|  |  | ||||||
|  | - Back space leaves ć character [\#44](https://github.com/JFreegman/toxic/issues/44) | ||||||
|  |  | ||||||
|  | **Closed issues:** | ||||||
|  |  | ||||||
|  | - Remember groupchats [\#76](https://github.com/JFreegman/toxic/issues/76) | ||||||
|  | - Segfault [\#75](https://github.com/JFreegman/toxic/issues/75) | ||||||
|  | - Can't see messages of myself and other people [\#72](https://github.com/JFreegman/toxic/issues/72) | ||||||
|  | - binary blob in source [\#66](https://github.com/JFreegman/toxic/issues/66) | ||||||
|  | - symbol lookup error [\#54](https://github.com/JFreegman/toxic/issues/54) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - ncurses libraries README note  [\#78](https://github.com/JFreegman/toxic/pull/78) ([kl4ng](https://github.com/kl4ng)) | ||||||
|  | - umask such that stored files are u+rw only [\#77](https://github.com/JFreegman/toxic/pull/77) ([alevy](https://github.com/alevy)) | ||||||
|  | - Fix groupchat cursor movement. [\#63](https://github.com/JFreegman/toxic/pull/63) ([aitjcize](https://github.com/aitjcize)) | ||||||
|  | - Fix wchar cursor movement. [\#62](https://github.com/JFreegman/toxic/pull/62) ([aitjcize](https://github.com/aitjcize)) | ||||||
|  | - api update [\#61](https://github.com/JFreegman/toxic/pull/61) ([naxuroqa](https://github.com/naxuroqa)) | ||||||
|  | - Add option to switch off ipv6. [\#60](https://github.com/JFreegman/toxic/pull/60) ([aitjcize](https://github.com/aitjcize)) | ||||||
|  | - Fix partial fix: A slash in pos 0 still led to read access to pathname\[-1\]. [\#59](https://github.com/JFreegman/toxic/pull/59) ([FullName](https://github.com/FullName)) | ||||||
|  | - Fix corresponding API name changes in toxcore. [\#58](https://github.com/JFreegman/toxic/pull/58) ([aitjcize](https://github.com/aitjcize)) | ||||||
|  | - Fix API ret code changes of ToxCore [\#57](https://github.com/JFreegman/toxic/pull/57) ([aitjcize](https://github.com/aitjcize)) | ||||||
|  |  | ||||||
|  | ## [prealpha_win32_r8](https://github.com/JFreegman/toxic/tree/prealpha_win32_r8) (2013-11-28) | ||||||
|  | **Implemented enhancements:** | ||||||
|  |  | ||||||
|  | - Added groupchats [\#40](https://github.com/JFreegman/toxic/pull/40) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Adapted to ipv6-enabled tox [\#38](https://github.com/JFreegman/toxic/pull/38) ([FullName](https://github.com/FullName)) | ||||||
|  | - If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName)) | ||||||
|  | - Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - if tox\_new\(\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName)) | ||||||
|  | - truncate friends' notes if they're too long [\#30](https://github.com/JFreegman/toxic/pull/30) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Added status bar to prompt, made it beep/blink on friend request, and bug fixes [\#29](https://github.com/JFreegman/toxic/pull/29) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Added a statusbar to chat windows and removed spammy messages [\#28](https://github.com/JFreegman/toxic/pull/28) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - implemented status and connectionstatus callbacks [\#26](https://github.com/JFreegman/toxic/pull/26) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Show offline friends names and some cosmetic changes [\#25](https://github.com/JFreegman/toxic/pull/25) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - refactor command argument parsing [\#23](https://github.com/JFreegman/toxic/pull/23) ([lukechampine](https://github.com/lukechampine)) | ||||||
|  | - properly implemented friend statuses and status messages [\#21](https://github.com/JFreegman/toxic/pull/21) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - implemented friend deletion [\#15](https://github.com/JFreegman/toxic/pull/15) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - Add check for setlocale\(\) [\#10](https://github.com/JFreegman/toxic/pull/10) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||||
|  | - Update build system [\#7](https://github.com/JFreegman/toxic/pull/7) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - Added travis integration [\#6](https://github.com/JFreegman/toxic/pull/6) ([stqism](https://github.com/stqism)) | ||||||
|  | - Use new public api [\#5](https://github.com/JFreegman/toxic/pull/5) ([fhahn](https://github.com/fhahn)) | ||||||
|  | - Add widechar checks [\#2](https://github.com/JFreegman/toxic/pull/2) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  |  | ||||||
|  | **Fixed bugs:** | ||||||
|  |  | ||||||
|  | - Let windows.c actually get the tox \*m. [\#41](https://github.com/JFreegman/toxic/pull/41) ([Jman012](https://github.com/Jman012)) | ||||||
|  | - If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName)) | ||||||
|  | - Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Merged pr6 [\#34](https://github.com/JFreegman/toxic/pull/34) ([stqism](https://github.com/stqism)) | ||||||
|  | - made error handling more consistent and added exit function [\#33](https://github.com/JFreegman/toxic/pull/33) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - if tox\\_new\\(\\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName)) | ||||||
|  | - Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - fix buffer overflows and format issues [\#20](https://github.com/JFreegman/toxic/pull/20) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Fix blocking while waiting for key [\#17](https://github.com/JFreegman/toxic/pull/17) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||||
|  | - fixed "free\(\): invalid pointer" when XDG\_CONFIG\_HOME is set [\#16](https://github.com/JFreegman/toxic/pull/16) ([gs93](https://github.com/gs93)) | ||||||
|  | - Make sure toxic compiles on MinGW/Win32 again [\#14](https://github.com/JFreegman/toxic/pull/14) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - Fix for the "bad character" when doing backspace in chat window [\#12](https://github.com/JFreegman/toxic/pull/12) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - Fix configure script for ncurses without ncursesw [\#9](https://github.com/JFreegman/toxic/pull/9) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||||
|  | - Fix configure script for mingw32 [\#8](https://github.com/JFreegman/toxic/pull/8) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  | - warning: comparison of integers of different signs: 'int' and 'unsigned long' [\#3](https://github.com/JFreegman/toxic/pull/3) ([1100110](https://github.com/1100110)) | ||||||
|  |  | ||||||
|  | **Merged pull requests:** | ||||||
|  |  | ||||||
|  | - Make sure friend message is null-terminated else generate garbate on screen [\#56](https://github.com/JFreegman/toxic/pull/56) ([aitjcize](https://github.com/aitjcize)) | ||||||
|  | - Fix trailing slashes which leads to segfault. [\#55](https://github.com/JFreegman/toxic/pull/55) ([aitjcize](https://github.com/aitjcize)) | ||||||
|  | - fix cflags [\#53](https://github.com/JFreegman/toxic/pull/53) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Fix 93ab16c [\#52](https://github.com/JFreegman/toxic/pull/52) ([urras](https://github.com/urras)) | ||||||
|  | - Offer solution for "error while loading shared libraries: libtoxcore.so.... [\#51](https://github.com/JFreegman/toxic/pull/51) ([urras](https://github.com/urras)) | ||||||
|  | - Implemented file transfers [\#50](https://github.com/JFreegman/toxic/pull/50) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Fix check for toxcore by linking sodium in the correct place [\#47](https://github.com/JFreegman/toxic/pull/47) ([devurandom](https://github.com/devurandom)) | ||||||
|  | - Changed order of servers [\#46](https://github.com/JFreegman/toxic/pull/46) ([grimd34th](https://github.com/grimd34th)) | ||||||
|  | - set friendnames properly and some fixes [\#45](https://github.com/JFreegman/toxic/pull/45) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - moved misc helper functions to separate file and removed redundant includes [\#43](https://github.com/JFreegman/toxic/pull/43) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Refactored prompt command parser to work with all window types and moved command stuff to separate files [\#42](https://github.com/JFreegman/toxic/pull/42) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Ipv6.init connection [\#39](https://github.com/JFreegman/toxic/pull/39) ([FullName](https://github.com/FullName)) | ||||||
|  | - Remove DHT window [\#13](https://github.com/JFreegman/toxic/pull/13) ([JFreegman](https://github.com/JFreegman)) | ||||||
|  | - Update README.md [\#4](https://github.com/JFreegman/toxic/pull/4) ([notadecent](https://github.com/notadecent)) | ||||||
|  | - Toxic standalone [\#1](https://github.com/JFreegman/toxic/pull/1) ([jin-eld](https://github.com/jin-eld)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* | ||||||
							
								
								
									
										1
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								COPYING
									
									
									
									
									
								
							| @@ -672,3 +672,4 @@ may consider it more useful to permit linking proprietary applications with | |||||||
| the library.  If this is what you want to do, use the GNU Lesser General | the library.  If this is what you want to do, use the GNU Lesser General | ||||||
| Public License instead of this License.  But first, please read | Public License instead of this License.  But first, please read | ||||||
| <http://www.gnu.org/philosophy/why-not-lgpl.html>. | <http://www.gnu.org/philosophy/why-not-lgpl.html>. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										365
									
								
								INSTALL
									
									
									
									
									
								
							
							
						
						
									
										365
									
								
								INSTALL
									
									
									
									
									
								
							| @@ -1,365 +0,0 @@ | |||||||
| Installation Instructions |  | ||||||
| ************************* |  | ||||||
|  |  | ||||||
| Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, |  | ||||||
| 2006, 2007, 2008, 2009 Free Software Foundation, Inc. |  | ||||||
|  |  | ||||||
|    Copying and distribution of this file, with or without modification, |  | ||||||
| are permitted in any medium without royalty provided the copyright |  | ||||||
| notice and this notice are preserved.  This file is offered as-is, |  | ||||||
| without warranty of any kind. |  | ||||||
|  |  | ||||||
| Basic Installation |  | ||||||
| ================== |  | ||||||
|  |  | ||||||
|    Briefly, the shell commands `./configure; make; make install' should |  | ||||||
| configure, build, and install this package.  The following |  | ||||||
| more-detailed instructions are generic; see the `README' file for |  | ||||||
| instructions specific to this package.  Some packages provide this |  | ||||||
| `INSTALL' file but do not implement all of the features documented |  | ||||||
| below.  The lack of an optional feature in a given package is not |  | ||||||
| necessarily a bug.  More recommendations for GNU packages can be found |  | ||||||
| in *note Makefile Conventions: (standards)Makefile Conventions. |  | ||||||
|  |  | ||||||
|    The `configure' shell script attempts to guess correct values for |  | ||||||
| various system-dependent variables used during compilation.  It uses |  | ||||||
| those values to create a `Makefile' in each directory of the package. |  | ||||||
| It may also create one or more `.h' files containing system-dependent |  | ||||||
| definitions.  Finally, it creates a shell script `config.status' that |  | ||||||
| you can run in the future to recreate the current configuration, and a |  | ||||||
| file `config.log' containing compiler output (useful mainly for |  | ||||||
| debugging `configure'). |  | ||||||
|  |  | ||||||
|    It can also use an optional file (typically called `config.cache' |  | ||||||
| and enabled with `--cache-file=config.cache' or simply `-C') that saves |  | ||||||
| the results of its tests to speed up reconfiguring.  Caching is |  | ||||||
| disabled by default to prevent problems with accidental use of stale |  | ||||||
| cache files. |  | ||||||
|  |  | ||||||
|    If you need to do unusual things to compile the package, please try |  | ||||||
| to figure out how `configure' could check whether to do them, and mail |  | ||||||
| diffs or instructions to the address given in the `README' so they can |  | ||||||
| be considered for the next release.  If you are using the cache, and at |  | ||||||
| some point `config.cache' contains results you don't want to keep, you |  | ||||||
| may remove or edit it. |  | ||||||
|  |  | ||||||
|    The file `configure.ac' (or `configure.in') is used to create |  | ||||||
| `configure' by a program called `autoconf'.  You need `configure.ac' if |  | ||||||
| you want to change it or regenerate `configure' using a newer version |  | ||||||
| of `autoconf'. |  | ||||||
|  |  | ||||||
|    The simplest way to compile this package is: |  | ||||||
|  |  | ||||||
|   1. `cd' to the directory containing the package's source code and type |  | ||||||
|      `./configure' to configure the package for your system. |  | ||||||
|  |  | ||||||
|      Running `configure' might take a while.  While running, it prints |  | ||||||
|      some messages telling which features it is checking for. |  | ||||||
|  |  | ||||||
|   2. Type `make' to compile the package. |  | ||||||
|  |  | ||||||
|   3. Optionally, type `make check' to run any self-tests that come with |  | ||||||
|      the package, generally using the just-built uninstalled binaries. |  | ||||||
|  |  | ||||||
|   4. Type `make install' to install the programs and any data files and |  | ||||||
|      documentation.  When installing into a prefix owned by root, it is |  | ||||||
|      recommended that the package be configured and built as a regular |  | ||||||
|      user, and only the `make install' phase executed with root |  | ||||||
|      privileges. |  | ||||||
|  |  | ||||||
|   5. Optionally, type `make installcheck' to repeat any self-tests, but |  | ||||||
|      this time using the binaries in their final installed location. |  | ||||||
|      This target does not install anything.  Running this target as a |  | ||||||
|      regular user, particularly if the prior `make install' required |  | ||||||
|      root privileges, verifies that the installation completed |  | ||||||
|      correctly. |  | ||||||
|  |  | ||||||
|   6. You can remove the program binaries and object files from the |  | ||||||
|      source code directory by typing `make clean'.  To also remove the |  | ||||||
|      files that `configure' created (so you can compile the package for |  | ||||||
|      a different kind of computer), type `make distclean'.  There is |  | ||||||
|      also a `make maintainer-clean' target, but that is intended mainly |  | ||||||
|      for the package's developers.  If you use it, you may have to get |  | ||||||
|      all sorts of other programs in order to regenerate files that came |  | ||||||
|      with the distribution. |  | ||||||
|  |  | ||||||
|   7. Often, you can also type `make uninstall' to remove the installed |  | ||||||
|      files again.  In practice, not all packages have tested that |  | ||||||
|      uninstallation works correctly, even though it is required by the |  | ||||||
|      GNU Coding Standards. |  | ||||||
|  |  | ||||||
|   8. Some packages, particularly those that use Automake, provide `make |  | ||||||
|      distcheck', which can by used by developers to test that all other |  | ||||||
|      targets like `make install' and `make uninstall' work correctly. |  | ||||||
|      This target is generally not run by end users. |  | ||||||
|  |  | ||||||
| Compilers and Options |  | ||||||
| ===================== |  | ||||||
|  |  | ||||||
|    Some systems require unusual options for compilation or linking that |  | ||||||
| the `configure' script does not know about.  Run `./configure --help' |  | ||||||
| for details on some of the pertinent environment variables. |  | ||||||
|  |  | ||||||
|    You can give `configure' initial values for configuration parameters |  | ||||||
| by setting variables in the command line or in the environment.  Here |  | ||||||
| is an example: |  | ||||||
|  |  | ||||||
|      ./configure CC=c99 CFLAGS=-g LIBS=-lposix |  | ||||||
|  |  | ||||||
|    *Note Defining Variables::, for more details. |  | ||||||
|  |  | ||||||
| Compiling For Multiple Architectures |  | ||||||
| ==================================== |  | ||||||
|  |  | ||||||
|    You can compile the package for more than one kind of computer at the |  | ||||||
| same time, by placing the object files for each architecture in their |  | ||||||
| own directory.  To do this, you can use GNU `make'.  `cd' to the |  | ||||||
| directory where you want the object files and executables to go and run |  | ||||||
| the `configure' script.  `configure' automatically checks for the |  | ||||||
| source code in the directory that `configure' is in and in `..'.  This |  | ||||||
| is known as a "VPATH" build. |  | ||||||
|  |  | ||||||
|    With a non-GNU `make', it is safer to compile the package for one |  | ||||||
| architecture at a time in the source code directory.  After you have |  | ||||||
| installed the package for one architecture, use `make distclean' before |  | ||||||
| reconfiguring for another architecture. |  | ||||||
|  |  | ||||||
|    On MacOS X 10.5 and later systems, you can create libraries and |  | ||||||
| executables that work on multiple system types--known as "fat" or |  | ||||||
| "universal" binaries--by specifying multiple `-arch' options to the |  | ||||||
| compiler but only a single `-arch' option to the preprocessor.  Like |  | ||||||
| this: |  | ||||||
|  |  | ||||||
|      ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ |  | ||||||
|                  CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ |  | ||||||
|                  CPP="gcc -E" CXXCPP="g++ -E" |  | ||||||
|  |  | ||||||
|    This is not guaranteed to produce working output in all cases, you |  | ||||||
| may have to build one architecture at a time and combine the results |  | ||||||
| using the `lipo' tool if you have problems. |  | ||||||
|  |  | ||||||
| Installation Names |  | ||||||
| ================== |  | ||||||
|  |  | ||||||
|    By default, `make install' installs the package's commands under |  | ||||||
| `/usr/local/bin', include files under `/usr/local/include', etc.  You |  | ||||||
| can specify an installation prefix other than `/usr/local' by giving |  | ||||||
| `configure' the option `--prefix=PREFIX', where PREFIX must be an |  | ||||||
| absolute file name. |  | ||||||
|  |  | ||||||
|    You can specify separate installation prefixes for |  | ||||||
| architecture-specific files and architecture-independent files.  If you |  | ||||||
| pass the option `--exec-prefix=PREFIX' to `configure', the package uses |  | ||||||
| PREFIX as the prefix for installing programs and libraries. |  | ||||||
| Documentation and other data files still use the regular prefix. |  | ||||||
|  |  | ||||||
|    In addition, if you use an unusual directory layout you can give |  | ||||||
| options like `--bindir=DIR' to specify different values for particular |  | ||||||
| kinds of files.  Run `configure --help' for a list of the directories |  | ||||||
| you can set and what kinds of files go in them.  In general, the |  | ||||||
| default for these options is expressed in terms of `${prefix}', so that |  | ||||||
| specifying just `--prefix' will affect all of the other directory |  | ||||||
| specifications that were not explicitly provided. |  | ||||||
|  |  | ||||||
|    The most portable way to affect installation locations is to pass the |  | ||||||
| correct locations to `configure'; however, many packages provide one or |  | ||||||
| both of the following shortcuts of passing variable assignments to the |  | ||||||
| `make install' command line to change installation locations without |  | ||||||
| having to reconfigure or recompile. |  | ||||||
|  |  | ||||||
|    The first method involves providing an override variable for each |  | ||||||
| affected directory.  For example, `make install |  | ||||||
| prefix=/alternate/directory' will choose an alternate location for all |  | ||||||
| directory configuration variables that were expressed in terms of |  | ||||||
| `${prefix}'.  Any directories that were specified during `configure', |  | ||||||
| but not in terms of `${prefix}', must each be overridden at install |  | ||||||
| time for the entire installation to be relocated.  The approach of |  | ||||||
| makefile variable overrides for each directory variable is required by |  | ||||||
| the GNU Coding Standards, and ideally causes no recompilation. |  | ||||||
| However, some platforms have known limitations with the semantics of |  | ||||||
| shared libraries that end up requiring recompilation when using this |  | ||||||
| method, particularly noticeable in packages that use GNU Libtool. |  | ||||||
|  |  | ||||||
|    The second method involves providing the `DESTDIR' variable.  For |  | ||||||
| example, `make install DESTDIR=/alternate/directory' will prepend |  | ||||||
| `/alternate/directory' before all installation names.  The approach of |  | ||||||
| `DESTDIR' overrides is not required by the GNU Coding Standards, and |  | ||||||
| does not work on platforms that have drive letters.  On the other hand, |  | ||||||
| it does better at avoiding recompilation issues, and works well even |  | ||||||
| when some directory options were not specified in terms of `${prefix}' |  | ||||||
| at `configure' time. |  | ||||||
|  |  | ||||||
| Optional Features |  | ||||||
| ================= |  | ||||||
|  |  | ||||||
|    If the package supports it, you can cause programs to be installed |  | ||||||
| with an extra prefix or suffix on their names by giving `configure' the |  | ||||||
| option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. |  | ||||||
|  |  | ||||||
|    Some packages pay attention to `--enable-FEATURE' options to |  | ||||||
| `configure', where FEATURE indicates an optional part of the package. |  | ||||||
| They may also pay attention to `--with-PACKAGE' options, where PACKAGE |  | ||||||
| is something like `gnu-as' or `x' (for the X Window System).  The |  | ||||||
| `README' should mention any `--enable-' and `--with-' options that the |  | ||||||
| package recognizes. |  | ||||||
|  |  | ||||||
|    For packages that use the X Window System, `configure' can usually |  | ||||||
| find the X include and library files automatically, but if it doesn't, |  | ||||||
| you can use the `configure' options `--x-includes=DIR' and |  | ||||||
| `--x-libraries=DIR' to specify their locations. |  | ||||||
|  |  | ||||||
|    Some packages offer the ability to configure how verbose the |  | ||||||
| execution of `make' will be.  For these packages, running `./configure |  | ||||||
| --enable-silent-rules' sets the default to minimal output, which can be |  | ||||||
| overridden with `make V=1'; while running `./configure |  | ||||||
| --disable-silent-rules' sets the default to verbose, which can be |  | ||||||
| overridden with `make V=0'. |  | ||||||
|  |  | ||||||
| Particular systems |  | ||||||
| ================== |  | ||||||
|  |  | ||||||
|    On HP-UX, the default C compiler is not ANSI C compatible.  If GNU |  | ||||||
| CC is not installed, it is recommended to use the following options in |  | ||||||
| order to use an ANSI C compiler: |  | ||||||
|  |  | ||||||
|      ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" |  | ||||||
|  |  | ||||||
| and if that doesn't work, install pre-built binaries of GCC for HP-UX. |  | ||||||
|  |  | ||||||
|    On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot |  | ||||||
| parse its `<wchar.h>' header file.  The option `-nodtk' can be used as |  | ||||||
| a workaround.  If GNU CC is not installed, it is therefore recommended |  | ||||||
| to try |  | ||||||
|  |  | ||||||
|      ./configure CC="cc" |  | ||||||
|  |  | ||||||
| and if that doesn't work, try |  | ||||||
|  |  | ||||||
|      ./configure CC="cc -nodtk" |  | ||||||
|  |  | ||||||
|    On Solaris, don't put `/usr/ucb' early in your `PATH'.  This |  | ||||||
| directory contains several dysfunctional programs; working variants of |  | ||||||
| these programs are available in `/usr/bin'.  So, if you need `/usr/ucb' |  | ||||||
| in your `PATH', put it _after_ `/usr/bin'. |  | ||||||
|  |  | ||||||
|    On Haiku, software installed for all users goes in `/boot/common', |  | ||||||
| not `/usr/local'.  It is recommended to use the following options: |  | ||||||
|  |  | ||||||
|      ./configure --prefix=/boot/common |  | ||||||
|  |  | ||||||
| Specifying the System Type |  | ||||||
| ========================== |  | ||||||
|  |  | ||||||
|    There may be some features `configure' cannot figure out |  | ||||||
| automatically, but needs to determine by the type of machine the package |  | ||||||
| will run on.  Usually, assuming the package is built to be run on the |  | ||||||
| _same_ architectures, `configure' can figure that out, but if it prints |  | ||||||
| a message saying it cannot guess the machine type, give it the |  | ||||||
| `--build=TYPE' option.  TYPE can either be a short name for the system |  | ||||||
| type, such as `sun4', or a canonical name which has the form: |  | ||||||
|  |  | ||||||
|      CPU-COMPANY-SYSTEM |  | ||||||
|  |  | ||||||
| where SYSTEM can have one of these forms: |  | ||||||
|  |  | ||||||
|      OS |  | ||||||
|      KERNEL-OS |  | ||||||
|  |  | ||||||
|    See the file `config.sub' for the possible values of each field.  If |  | ||||||
| `config.sub' isn't included in this package, then this package doesn't |  | ||||||
| need to know the machine type. |  | ||||||
|  |  | ||||||
|    If you are _building_ compiler tools for cross-compiling, you should |  | ||||||
| use the option `--target=TYPE' to select the type of system they will |  | ||||||
| produce code for. |  | ||||||
|  |  | ||||||
|    If you want to _use_ a cross compiler, that generates code for a |  | ||||||
| platform different from the build platform, you should specify the |  | ||||||
| "host" platform (i.e., that on which the generated programs will |  | ||||||
| eventually be run) with `--host=TYPE'. |  | ||||||
|  |  | ||||||
| Sharing Defaults |  | ||||||
| ================ |  | ||||||
|  |  | ||||||
|    If you want to set default values for `configure' scripts to share, |  | ||||||
| you can create a site shell script called `config.site' that gives |  | ||||||
| default values for variables like `CC', `cache_file', and `prefix'. |  | ||||||
| `configure' looks for `PREFIX/share/config.site' if it exists, then |  | ||||||
| `PREFIX/etc/config.site' if it exists.  Or, you can set the |  | ||||||
| `CONFIG_SITE' environment variable to the location of the site script. |  | ||||||
| A warning: not all `configure' scripts look for a site script. |  | ||||||
|  |  | ||||||
| Defining Variables |  | ||||||
| ================== |  | ||||||
|  |  | ||||||
|    Variables not defined in a site shell script can be set in the |  | ||||||
| environment passed to `configure'.  However, some packages may run |  | ||||||
| configure again during the build, and the customized values of these |  | ||||||
| variables may be lost.  In order to avoid this problem, you should set |  | ||||||
| them in the `configure' command line, using `VAR=value'.  For example: |  | ||||||
|  |  | ||||||
|      ./configure CC=/usr/local2/bin/gcc |  | ||||||
|  |  | ||||||
| causes the specified `gcc' to be used as the C compiler (unless it is |  | ||||||
| overridden in the site shell script). |  | ||||||
|  |  | ||||||
| Unfortunately, this technique does not work for `CONFIG_SHELL' due to |  | ||||||
| an Autoconf bug.  Until the bug is fixed you can use this workaround: |  | ||||||
|  |  | ||||||
|      CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash |  | ||||||
|  |  | ||||||
| `configure' Invocation |  | ||||||
| ====================== |  | ||||||
|  |  | ||||||
|    `configure' recognizes the following options to control how it |  | ||||||
| operates. |  | ||||||
|  |  | ||||||
| `--help' |  | ||||||
| `-h' |  | ||||||
|      Print a summary of all of the options to `configure', and exit. |  | ||||||
|  |  | ||||||
| `--help=short' |  | ||||||
| `--help=recursive' |  | ||||||
|      Print a summary of the options unique to this package's |  | ||||||
|      `configure', and exit.  The `short' variant lists options used |  | ||||||
|      only in the top level, while the `recursive' variant lists options |  | ||||||
|      also present in any nested packages. |  | ||||||
|  |  | ||||||
| `--version' |  | ||||||
| `-V' |  | ||||||
|      Print the version of Autoconf used to generate the `configure' |  | ||||||
|      script, and exit. |  | ||||||
|  |  | ||||||
| `--cache-file=FILE' |  | ||||||
|      Enable the cache: use and save the results of the tests in FILE, |  | ||||||
|      traditionally `config.cache'.  FILE defaults to `/dev/null' to |  | ||||||
|      disable caching. |  | ||||||
|  |  | ||||||
| `--config-cache' |  | ||||||
| `-C' |  | ||||||
|      Alias for `--cache-file=config.cache'. |  | ||||||
|  |  | ||||||
| `--quiet' |  | ||||||
| `--silent' |  | ||||||
| `-q' |  | ||||||
|      Do not print messages saying which checks are being made.  To |  | ||||||
|      suppress all normal output, redirect it to `/dev/null' (any error |  | ||||||
|      messages will still be shown). |  | ||||||
|  |  | ||||||
| `--srcdir=DIR' |  | ||||||
|      Look for the package's source code in directory DIR.  Usually |  | ||||||
|      `configure' can determine that directory automatically. |  | ||||||
|  |  | ||||||
| `--prefix=DIR' |  | ||||||
|      Use DIR as the installation prefix.  *note Installation Names:: |  | ||||||
|      for more details, including other options available for fine-tuning |  | ||||||
|      the installation locations. |  | ||||||
|  |  | ||||||
| `--no-create' |  | ||||||
| `-n' |  | ||||||
|      Run the configure checks, but stop before creating any output |  | ||||||
|      files. |  | ||||||
|  |  | ||||||
| `configure' also accepts some other, not widely useful, options.  Run |  | ||||||
| `configure --help' for more details. |  | ||||||
|  |  | ||||||
							
								
								
									
										70
									
								
								INSTALL.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								INSTALL.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | # Installation | ||||||
|  | * [Dependencies](#deps) | ||||||
|  |   * [OS X Notes](#deps_osx) | ||||||
|  | * [Compiling](#compiling) | ||||||
|  |   * [Documentation](#docs) | ||||||
|  | * [Notes](#notes) | ||||||
|  |   * [Compilation variables](#comp_vars) | ||||||
|  |   * [Packaging](#packaging) | ||||||
|  |  | ||||||
|  | <a name="deps" /> | ||||||
|  | ## Dependencies | ||||||
|  | | Name                                                 | Needed by                  | Debian package      | | ||||||
|  | |------------------------------------------------------|----------------------------|---------------------| | ||||||
|  | | [Tox Core](https://github.com/irungentoo/toxcore)    | BASE                       | *None*              | | ||||||
|  | | [NCurses](https://www.gnu.org/software/ncurses)      | BASE                       | libncursesw5-dev    | | ||||||
|  | | [LibConfig](http://www.hyperrealm.com/libconfig)     | BASE                       | libconfig-dev       | | ||||||
|  | | [GNUmake](https://www.gnu.org/software/make)         | BASE                       | make                | | ||||||
|  | | [libcurl](http://curl.haxx.se/)                      | BASE                       | libcurl4-openssl-dev| | ||||||
|  | | [libqrencode](https://fukuchi.org/works/qrencode/)   | BASE                       | libqrencode-dev     | | ||||||
|  | | [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO                      | *None*              | | ||||||
|  | | [OpenAL](http://openal.org)                          | AUDIO, SOUND NOTIFICATIONS | libopenal-dev       | | ||||||
|  | | [OpenALUT](http://openal.org)                        | SOUND NOTIFICATIONS        | libalut-dev         | | ||||||
|  | | [LibNotify](https://developer.gnome.org/libnotify)   | DESKTOP NOTIFICATIONS      | libnotify-dev       | | ||||||
|  | | [AsciiDoc](http://asciidoc.org/index.html)           | DOCUMENTATION<sup>1</sup>  | asciidoc            | | ||||||
|  | <sup>1</sup>: see [Documentation](#docs) | ||||||
|  |  | ||||||
|  | <a name="deps_osx" /> | ||||||
|  | #### OS X Notes | ||||||
|  | Using [Homebrew](http://brew.sh): | ||||||
|  | ``` | ||||||
|  | brew install openal-soft freealut libconfig | ||||||
|  | brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb | ||||||
|  | brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | You can omit `libnotify` if you intend to build without desktop notifications enabled. | ||||||
|  |  | ||||||
|  | <a name="Compiling"> | ||||||
|  | ## Compiling | ||||||
|  | ``` | ||||||
|  | make PREFIX="/where/to/install" | ||||||
|  | sudo make install PREFIX="/where/to/install" | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | <a name="docs" /> | ||||||
|  | #### Documentation | ||||||
|  | Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br /> | ||||||
|  | **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br /> | ||||||
|  | **NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them. | ||||||
|  |  | ||||||
|  | <a name="notes" /> | ||||||
|  | ## Notes | ||||||
|  |  | ||||||
|  | <a name="comp_vars" /> | ||||||
|  | #### Compilation variables | ||||||
|  | * You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""` | ||||||
|  | * You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones) | ||||||
|  | * Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables: | ||||||
|  |   * `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking) | ||||||
|  |   * `DISABLE_AV=1` → build toxic without audio call support | ||||||
|  |   * `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support | ||||||
|  |   * `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support | ||||||
|  |  | ||||||
|  | <a name="packaging" /> | ||||||
|  | #### Packaging | ||||||
|  | * For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files | ||||||
|  | * `DESTDIR=""` can be used in addition to `PREFIX=""`: | ||||||
|  |   * `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg") | ||||||
|  |   * `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local") | ||||||
|  |  | ||||||
							
								
								
									
										84
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | BASE_DIR = $(shell pwd -P) | ||||||
|  | CFG_DIR = $(BASE_DIR)/cfg | ||||||
|  |  | ||||||
|  | -include $(CFG_DIR)/global_vars.mk | ||||||
|  |  | ||||||
|  | LIBS = libtoxcore ncursesw libconfig libqrencode | ||||||
|  |  | ||||||
|  | CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all | ||||||
|  | CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | ||||||
|  | CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | ||||||
|  | CFLAGS += $(USER_CFLAGS) | ||||||
|  | LDFLAGS = $(USER_LDFLAGS) | ||||||
|  |  | ||||||
|  | OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o | ||||||
|  | OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o | ||||||
|  | OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o | ||||||
|  | OBJ += term_mplex.o toxic.o toxic_strings.o windows.o | ||||||
|  |  | ||||||
|  | # Check on wich system we are running | ||||||
|  | UNAME_S = $(shell uname -s) | ||||||
|  | ifeq ($(UNAME_S), Linux) | ||||||
|  |     -include $(CFG_DIR)/systems/Linux.mk | ||||||
|  | endif | ||||||
|  | ifeq ($(UNAME_S), FreeBSD) | ||||||
|  |     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||||
|  | endif | ||||||
|  | ifeq ($(UNAME_S), DragonFly) | ||||||
|  |     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||||
|  | endif | ||||||
|  | ifeq ($(UNAME_S), OpenBSD) | ||||||
|  |     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||||
|  | endif | ||||||
|  | ifeq ($(UNAME_S), Darwin) | ||||||
|  |     -include $(CFG_DIR)/systems/Darwin.mk | ||||||
|  | endif | ||||||
|  | ifeq ($(UNAME_S), Solaris) | ||||||
|  |     -include $(CFG_DIR)/systems/Solaris.mk | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check on which platform we are running | ||||||
|  | UNAME_M = $(shell uname -m) | ||||||
|  | ifeq ($(UNAME_M), x86_64) | ||||||
|  |     -include $(CFG_DIR)/platforms/x86_64.mk | ||||||
|  | endif | ||||||
|  | ifneq ($(filter %86, $(UNAME_M)),) | ||||||
|  |     -include $(CFG_DIR)/platforms/x86.mk | ||||||
|  | endif | ||||||
|  | ifneq ($(filter arm%, $(UNAME_M)),) | ||||||
|  |     -include $(CFG_DIR)/platforms/arm.mk | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Include all needed checks | ||||||
|  | -include $(CFG_DIR)/checks/check_features.mk | ||||||
|  |  | ||||||
|  | # Fix path for object files | ||||||
|  | OBJ := $(addprefix $(BUILD_DIR)/, $(OBJ)) | ||||||
|  |  | ||||||
|  | # Targets | ||||||
|  | all: $(BUILD_DIR)/toxic | ||||||
|  |  | ||||||
|  | $(BUILD_DIR)/toxic: $(OBJ) | ||||||
|  | 	@echo "  LD    $(@:$(BUILD_DIR)/%=%)" | ||||||
|  | 	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS) | ||||||
|  |  | ||||||
|  | $(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO) | ||||||
|  | 	@echo "  CC    $(@:$(BUILD_DIR)/)osx_video.o" | ||||||
|  | 	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO) | ||||||
|  |  | ||||||
|  | $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | ||||||
|  | 	@if [ ! -e $(BUILD_DIR) ]; then \ | ||||||
|  | 		mkdir -p $(BUILD_DIR) ;\ | ||||||
|  | 	fi | ||||||
|  | 	@echo "  CC    $(@:$(BUILD_DIR)/%=%)" | ||||||
|  | 	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c | ||||||
|  | 	@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d | ||||||
|  |  | ||||||
|  | clean: | ||||||
|  | 	rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic | ||||||
|  |  | ||||||
|  | -include $(BUILD_DIR)/$(OBJ:.o=.d) | ||||||
|  |  | ||||||
|  | -include $(CFG_DIR)/targets/*.mk | ||||||
|  |  | ||||||
|  | .PHONY: clean all | ||||||
| @@ -1,3 +0,0 @@ | |||||||
| SUBDIRS = build misc |  | ||||||
|  |  | ||||||
| ACLOCAL_AMFLAGS = -I m4 |  | ||||||
							
								
								
									
										37
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,25 +1,28 @@ | |||||||
| # Toxic | <a href="https://scan.coverity.com/projects/toxic-tox"> | ||||||
|  |   <img alt="Coverity Scan Build Status" | ||||||
|  |        src="https://scan.coverity.com/projects/4975/badge.svg"/> | ||||||
|  | </a> | ||||||
|  |  | ||||||
|  | Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application. | ||||||
|  |  | ||||||
|  | [](https://i.imgur.com/san99Z2.png) | ||||||
|  |  | ||||||
| Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) which formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://i.imgur.com/hL7WhVl.png). |  | ||||||
| ## Installation | ## Installation | ||||||
| * Generate the configure script by running the ```autoreconf -i``` command. | [Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br /> | ||||||
|  | [Compile it yourself](/INSTALL.md) | ||||||
|  |  | ||||||
| * Execute the configure script with ```./configure``` (you may need to pass it the location of your dependency libraries, i.e.): | ## Settings | ||||||
| ```./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/toxcore --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/.libs --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ ``` | Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more. | ||||||
|  | You can view our example config file [here](misc/toxic.conf.example). | ||||||
|  |  | ||||||
| * Audio calling support requires openal installed | ## Troubleshooting | ||||||
| * Compile with --disable-av to build without audio call support | If your default prefix is "/usr/local" and you receive the following: | ||||||
| * Compile and install the program with ```make && sudo make install``` | ``` | ||||||
|  | error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory | ||||||
| #### Notes | ``` | ||||||
| If your default prefix is /usr/local and you get the error: "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run: | you can attempt to correct it by running `sudo ldconfig`. If that doesn't work, run: | ||||||
| ``` | ``` | ||||||
| echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf | echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf | ||||||
| sudo ldconfig | sudo ldconfig | ||||||
| ``` | ``` | ||||||
| If you dont already have them, you may need to install the ncurses libraries. For Debian based systems: |  | ||||||
| ``` |  | ||||||
| sudo apt-get install libncurses5-dev libncursesw5-dev |  | ||||||
| ``` |  | ||||||
| ## Settings |  | ||||||
| After running Toxic for the first time an empty file called toxic.conf should reside in your home configuration directory (~/.config/tox for Linux users). For an example on how to use this config file to save settings such as auto-logging and time format see: toxic/misc/toxic.conf |  | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								astylerc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								astylerc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | --style=kr | ||||||
|  |         --pad-header | ||||||
|  |         --max-code-length=120 | ||||||
|  |                           --convert-tabs | ||||||
|  |                           --indent-switches | ||||||
|  |                           --pad-oper | ||||||
|  |                           --align-pointer=name | ||||||
|  |                                   --align-reference=name | ||||||
|  |                                           --preserve-date | ||||||
|  |                                           --lineend=linux | ||||||
|  |                                                   --break-blocks | ||||||
| @@ -1,69 +0,0 @@ | |||||||
| #Don't change this unless needed, else you'll break stuff |  | ||||||
|  |  | ||||||
| bin_PROGRAMS = toxic |  | ||||||
|  |  | ||||||
|  |  | ||||||
| toxic_SOURCES = $(top_srcdir)/src/toxic.c \ |  | ||||||
|                 $(top_srcdir)/src/toxic.h \ |  | ||||||
|                 $(top_srcdir)/src/chat.h \ |  | ||||||
|                 $(top_srcdir)/src/chat.c \ |  | ||||||
|                 $(top_srcdir)/src/configdir.h \ |  | ||||||
|                 $(top_srcdir)/src/configdir.c \ |  | ||||||
|                 $(top_srcdir)/src/prompt.h \ |  | ||||||
|                 $(top_srcdir)/src/prompt.c \ |  | ||||||
|                 $(top_srcdir)/src/friendlist.h \ |  | ||||||
|                 $(top_srcdir)/src/friendlist.c \ |  | ||||||
|                 $(top_srcdir)/src/windows.c \ |  | ||||||
|                 $(top_srcdir)/src/windows.h \ |  | ||||||
|                 $(top_srcdir)/src/groupchat.c \ |  | ||||||
|                 $(top_srcdir)/src/groupchat.h \ |  | ||||||
|                 $(top_srcdir)/src/global_commands.c \ |  | ||||||
|                 $(top_srcdir)/src/global_commands.h \ |  | ||||||
|                 $(top_srcdir)/src/chat_commands.c \ |  | ||||||
|                 $(top_srcdir)/src/chat_commands.h \ |  | ||||||
|                 $(top_srcdir)/src/execute.c \ |  | ||||||
|                 $(top_srcdir)/src/execute.h \ |  | ||||||
|                 $(top_srcdir)/src/misc_tools.c \ |  | ||||||
|                 $(top_srcdir)/src/misc_tools.h \ |  | ||||||
|                 $(top_srcdir)/src/toxic_strings.c \ |  | ||||||
|                 $(top_srcdir)/src/toxic_strings.h \ |  | ||||||
|                 $(top_srcdir)/src/log.c \ |  | ||||||
|                 $(top_srcdir)/src/log.h \ |  | ||||||
|                 $(top_srcdir)/src/file_senders.c \ |  | ||||||
|                 $(top_srcdir)/src/file_senders.h \ |  | ||||||
|                 $(top_srcdir)/src/line_info.c \ |  | ||||||
|                 $(top_srcdir)/src/line_info.h \ |  | ||||||
|                 $(top_srcdir)/src/settings.c \ |  | ||||||
|                 $(top_srcdir)/src/settings.h \ |  | ||||||
|                 $(top_srcdir)/src/dns.c \ |  | ||||||
|                 $(top_srcdir)/src/dns.h |  | ||||||
|  |  | ||||||
| toxic_CFLAGS =  -I$(top_srcdir) \ |  | ||||||
| 				  $(NCURSES_CFLAGS) \ |  | ||||||
| 				  $(LIBSODIUM_CFLAGS) \ |  | ||||||
| 				  $(LIBTOXCORE_CFLAGS) \ |  | ||||||
| 				  $(PTHREAD_CFLAGS) |  | ||||||
|  |  | ||||||
| toxic_CPPFLAGS = '-DTOXICVER="$(TOXIC_VERSION)"' |  | ||||||
|  |  | ||||||
| toxic_LDADD =	$(LIBTOXCORE_LDFLAGS) \ |  | ||||||
| 				$(LIBSODIUM_LDFLAGS) \ |  | ||||||
| 				$(NCURSES_LIBS) \ |  | ||||||
| 				$(LIBTOXCORE_LIBS) \ |  | ||||||
| 				$(LIBSODIUM_LIBS) \ |  | ||||||
|                 $(WINSOCK2_LIBS) \ |  | ||||||
|                 $(PTHREAD_LIBS) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # For audio support |  | ||||||
| if BUILD_AV |  | ||||||
|  |  | ||||||
| toxic_SOURCES += $(top_srcdir)/src/audio_call.c \ |  | ||||||
|                  $(top_srcdir)/src/audio_call.h |  | ||||||
|  |  | ||||||
| toxic_CFLAGS += $(LIBTOXAV_CFLAGS) \ |  | ||||||
| 		$(OPENAL_CFLAGS) |  | ||||||
| 		 |  | ||||||
| toxic_LDADD += $(LIBTOXAV_LIBS) \ |  | ||||||
| 	       $(OPENAL_LIBS)  |  | ||||||
| endif |  | ||||||
							
								
								
									
										21
									
								
								cfg/checks/audio.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								cfg/checks/audio.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | # Variables for audio call support | ||||||
|  | AUDIO_LIBS = libtoxav openal | ||||||
|  | AUDIO_CFLAGS = -DAUDIO | ||||||
|  | ifneq (, $(findstring audio_device.o, $(OBJ))) | ||||||
|  |     AUDIO_OBJ = audio_call.o | ||||||
|  | else | ||||||
|  |     AUDIO_OBJ = audio_call.o audio_device.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we can build audio support | ||||||
|  | CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error") | ||||||
|  | ifneq ($(CHECK_AUDIO_LIBS), error) | ||||||
|  |     LIBS += $(AUDIO_LIBS) | ||||||
|  |     CFLAGS += $(AUDIO_CFLAGS) | ||||||
|  |     OBJ += $(AUDIO_OBJ) | ||||||
|  | else ifneq ($(MAKECMDGOALS), clean) | ||||||
|  |     MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||||
|  |     $(warning WARNING -- Toxic will be compiled without audio support) | ||||||
|  |     $(warning WARNING -- You need these libraries for audio support) | ||||||
|  |     $(warning WARNING -- $(MISSING_AUDIO_LIBS)) | ||||||
|  | endif | ||||||
							
								
								
									
										54
									
								
								cfg/checks/check_features.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								cfg/checks/check_features.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | CHECKS_DIR = $(CFG_DIR)/checks | ||||||
|  |  | ||||||
|  | # Check if we want build X11 support | ||||||
|  | X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||||
|  | ifneq ($(X11), disabled) | ||||||
|  |     -include $(CHECKS_DIR)/x11.mk | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we want build audio support | ||||||
|  | AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||||
|  | ifneq ($(AUDIO), disabled) | ||||||
|  |     -include $(CHECKS_DIR)/audio.mk | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we want build video support | ||||||
|  | VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||||
|  | ifneq ($(X11), disabled) | ||||||
|  | ifneq ($(AUDIO), disabled) | ||||||
|  | ifneq ($(VIDEO), disabled) | ||||||
|  |     -include $(CHECKS_DIR)/video.mk | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we want build sound notifications support | ||||||
|  | SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||||
|  | ifneq ($(SND_NOTIFY), disabled) | ||||||
|  |     -include $(CHECKS_DIR)/sound_notifications.mk | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we want build desktop notifications support | ||||||
|  | DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||||
|  | ifneq ($(DESK_NOTIFY), disabled) | ||||||
|  |     -include $(CHECKS_DIR)/desktop_notifications.mk | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we want build QR exported as PNG support | ||||||
|  | QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||||
|  | ifneq ($(QR_PNG), disabled) | ||||||
|  |     -include $(CHECKS_DIR)/qr_png.mk | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we 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/qr_png.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/qr_png.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | # Variables for QR exported as PNG support | ||||||
|  | PNG_LIBS = libpng | ||||||
|  | PNG_CFLAGS = -DQRPNG | ||||||
|  |  | ||||||
|  | # Check if we can build QR exported as PNG support | ||||||
|  | CHECK_PNG_LIBS = $(shell pkg-config --exists $(PNG_LIBS) || echo -n "error") | ||||||
|  | ifneq ($(CHECK_PNG_LIBS), error) | ||||||
|  |     LIBS += $(PNG_LIBS) | ||||||
|  |     CFLAGS += $(PNG_CFLAGS) | ||||||
|  | else ifneq ($(MAKECMDGOALS), clean) | ||||||
|  |     MISSING_PNG_LIBS = $(shell for lib in $(PNG_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||||
|  |     $(warning WARNING -- Toxic will be compiled without QR exported as PNG support) | ||||||
|  |     $(warning WARNING -- You need these libraries for QR exported as PNG support) | ||||||
|  |     $(warning WARNING -- $(MISSING_PNG_LIBS)) | ||||||
|  | endif | ||||||
							
								
								
									
										21
									
								
								cfg/checks/sound_notifications.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								cfg/checks/sound_notifications.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | # Variables for sound notifications support | ||||||
|  | SND_NOTIFY_LIBS = openal freealut | ||||||
|  | SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY | ||||||
|  | ifneq (, $(findstring audio_device.o, $(OBJ))) | ||||||
|  |     SND_NOTIFY_OBJ = | ||||||
|  | else | ||||||
|  |     SND_NOTIFY_OBJ = audio_device.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we can build sound notifications support | ||||||
|  | CHECK_SND_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(SND_NOTIFY_LIBS) || echo -n "error") | ||||||
|  | ifneq ($(CHECK_SND_NOTIFY_LIBS), error) | ||||||
|  |     LIBS += $(SND_NOTIFY_LIBS) | ||||||
|  |     CFLAGS += $(SND_NOTIFY_CFLAGS) | ||||||
|  |     OBJ += $(SND_NOTIFY_OBJ) | ||||||
|  | else ifneq ($(MAKECMDGOALS), clean) | ||||||
|  |     MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||||
|  |     $(warning WARNING -- Toxic will be compiled without sound notifications support) | ||||||
|  |     $(warning WARNING -- You need these libraries for sound notifications support) | ||||||
|  |     $(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS)) | ||||||
|  | endif | ||||||
							
								
								
									
										21
									
								
								cfg/checks/video.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								cfg/checks/video.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | # Variables for video call support | ||||||
|  | VIDEO_LIBS = libtoxav vpx x11 | ||||||
|  | VIDEO_CFLAGS = -DVIDEO | ||||||
|  | ifneq (, $(findstring video_device.o, $(OBJ))) | ||||||
|  |     VIDEO_OBJ = video_call.o | ||||||
|  | else | ||||||
|  |     VIDEO_OBJ = video_call.o video_device.o | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Check if we can build video support | ||||||
|  | CHECK_VIDEO_LIBS = $(shell $(PKG_CONFIG) --exists $(VIDEO_LIBS) || echo -n "error") | ||||||
|  | ifneq ($(CHECK_VIDEO_LIBS), error) | ||||||
|  |     LIBS += $(VIDEO_LIBS) | ||||||
|  |     CFLAGS += $(VIDEO_CFLAGS) | ||||||
|  |     OBJ += $(VIDEO_OBJ) | ||||||
|  | else ifneq ($(MAKECMDGOALS), clean) | ||||||
|  |     MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||||
|  |     $(warning WARNING -- Toxic will be compiled without video support) | ||||||
|  |     $(warning WARNING -- You will need these libraries for video support) | ||||||
|  |     $(warning WARNING -- $(MISSING_VIDEO_LIBS)) | ||||||
|  | endif | ||||||
							
								
								
									
										17
									
								
								cfg/checks/x11.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								cfg/checks/x11.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | # Variables for X11 support | ||||||
|  | X11_LIBS = x11 | ||||||
|  | X11_CFLAGS = -DX11 | ||||||
|  | X11_OBJ = xtra.o | ||||||
|  |  | ||||||
|  | # Check if we can build X11 support | ||||||
|  | CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error") | ||||||
|  | ifneq ($(CHECK_X11_LIBS), error) | ||||||
|  |     LIBS += $(X11_LIBS) | ||||||
|  |     CFLAGS += $(X11_CFLAGS) | ||||||
|  |     OBJ += $(X11_OBJ) | ||||||
|  | else ifneq ($(MAKECMDGOALS), clean) | ||||||
|  |     MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||||
|  |     $(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking and drag&drop support)) | ||||||
|  |     $(warning WARNING -- You need these libraries for x11 support) | ||||||
|  |     $(warning WARNING -- $(MISSING_X11_LIBS)) | ||||||
|  | endif | ||||||
							
								
								
									
										32
									
								
								cfg/global_vars.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								cfg/global_vars.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | # Version | ||||||
|  | TOXIC_VERSION = 0.7.1 | ||||||
|  | REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | ||||||
|  | ifneq (, $(findstring error, $(REV))) | ||||||
|  |     VERSION = $(TOXIC_VERSION) | ||||||
|  | else | ||||||
|  |     VERSION = $(TOXIC_VERSION)_r$(REV) | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | # Project directories | ||||||
|  | BUILD_DIR = $(BASE_DIR)/build | ||||||
|  | DOC_DIR = $(BASE_DIR)/doc | ||||||
|  | SRC_DIR = $(BASE_DIR)/src | ||||||
|  | SND_DIR = $(BASE_DIR)/sounds | ||||||
|  | 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 | ||||||
							
								
								
									
										4
									
								
								cfg/platforms/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								cfg/platforms/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | # Ignore everything in this directory | ||||||
|  | * | ||||||
|  | # Except this file | ||||||
|  | !.gitignore | ||||||
							
								
								
									
										18
									
								
								cfg/systems/Darwin.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								cfg/systems/Darwin.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | # Special options for OS X | ||||||
|  | # This assumes the use of Homebrew. Change the paths if using MacPorts or Fink. | ||||||
|  |  | ||||||
|  | PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/opt/libconfig/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig) | ||||||
|  |  | ||||||
|  | LIBS := $(filter-out ncursesw, $(LIBS)) | ||||||
|  |  | ||||||
|  | # OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw. | ||||||
|  | LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g | ||||||
|  | CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g | ||||||
|  |  | ||||||
|  | OSX_LIBRARIES = -lobjc -lresolv | ||||||
|  | OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \ | ||||||
|  | 	-framework QuartzCore -framework CoreMedia | ||||||
|  | OSX_VIDEO = osx_video.m | ||||||
|  |  | ||||||
|  | LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS) | ||||||
|  | OBJ += osx_video.o | ||||||
							
								
								
									
										4
									
								
								cfg/systems/FreeBSD.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								cfg/systems/FreeBSD.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | # Specials options for freebsd systems | ||||||
|  | LIBS := $(filter-out ncursesw, $(LIBS)) | ||||||
|  | LDFLAGS += -lncursesw -lcurl | ||||||
|  | MANDIR = $(PREFIX)/man | ||||||
							
								
								
									
										4
									
								
								cfg/systems/Linux.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								cfg/systems/Linux.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | # Specials options for linux systems | ||||||
|  | CFLAGS += | ||||||
|  | LDFLAGS += -ldl -lrt -lcurl | ||||||
|  | MANDIR = $(PREFIX)/share/man | ||||||
							
								
								
									
										10
									
								
								cfg/targets/doc.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								cfg/targets/doc.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | # Doc target | ||||||
|  | doc: $(MANFILES:%=$(DOC_DIR)/%) | ||||||
|  | 	 | ||||||
|  | $(DOC_DIR)/%: $(DOC_DIR)/%.asc | ||||||
|  | 	@echo "  MAN   $(@F)" | ||||||
|  | 	@a2x -f manpage -a revdate=$(shell git log -1 --date=short --format="%ad" $<) \ | ||||||
|  | 		-a manmanual="Toxic Manual" -a mansource=toxic \ | ||||||
|  | 		-a manversion=__VERSION__ -a datadir=__DATADIR__ $< | ||||||
|  | 	 | ||||||
|  | .PHONY: doc | ||||||
							
								
								
									
										23
									
								
								cfg/targets/help.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								cfg/targets/help.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | # Help target | ||||||
|  | help: | ||||||
|  | 	@echo "-- Targets --" | ||||||
|  | 	@echo "  all:       Build toxic and documentation [DEFAULT]" | ||||||
|  | 	@echo "  toxic:     Build toxic" | ||||||
|  | 	@echo "  doc:       Build documentation" | ||||||
|  | 	@echo "  install:   Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")" | ||||||
|  | 	@echo "  uninstall: Remove toxic from PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")" | ||||||
|  | 	@echo "  clean:     Remove built files" | ||||||
|  | 	@echo "  help:      This help" | ||||||
|  | 	@echo | ||||||
|  | 	@echo "-- Variables --" | ||||||
|  | 	@echo "  DISABLE_X11:            Set to \"1\" to force building without X11 support" | ||||||
|  | 	@echo "  DISABLE_AV:             Set to \"1\" to force building without audio call support" | ||||||
|  | 	@echo "  DISABLE_SOUND_NOTIFY:   Set to \"1\" to force building without sound notification support" | ||||||
|  | 	@echo "  DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" | ||||||
|  | 	@echo "  DISABLE_QRPNG:          Set to \"1\" to force building without QR exported as PNG support" | ||||||
|  | 	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS" | ||||||
|  | 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | ||||||
|  | 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | ||||||
|  | 	@echo "  DESTDIR:                Specify a directory where to store installed files (mainly for packaging purpose)" | ||||||
|  |  | ||||||
|  | .PHONY: help | ||||||
							
								
								
									
										41
									
								
								cfg/targets/install.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								cfg/targets/install.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | # Install target | ||||||
|  | install: $(BUILD_DIR)/toxic | ||||||
|  | 	@echo "Installing toxic executable" | ||||||
|  | 	@mkdir -p $(abspath $(DESTDIR)/$(BINDIR)) | ||||||
|  | 	@install -m 0755 $(BUILD_DIR)/toxic $(abspath $(DESTDIR)/$(BINDIR)/toxic) | ||||||
|  | 	 | ||||||
|  | 	@echo "Installing desktop file" | ||||||
|  | 	@mkdir -p $(abspath $(DESTDIR)/$(APPDIR)) | ||||||
|  | 	@install -m 0644 $(MISC_DIR)/$(DESKFILE) $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE)) | ||||||
|  | 	 | ||||||
|  | 	@echo "Installing data files" | ||||||
|  | 	@mkdir -p $(abspath $(DESTDIR)/$(DATADIR)) | ||||||
|  | 	@for f in $(DATAFILES) ; do \ | ||||||
|  | 		install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\ | ||||||
|  | 		file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\ | ||||||
|  | 		sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ | ||||||
|  | 		mv temp_file $$file ;\ | ||||||
|  | 	done | ||||||
|  | 	@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds | ||||||
|  | 	@for f in $(SNDFILES) ; do \ | ||||||
|  | 		install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\ | ||||||
|  | 	done | ||||||
|  | 	 | ||||||
|  | 	@echo "Installing man pages" | ||||||
|  | 	@mkdir -p $(abspath $(DESTDIR)/$(MANDIR)) | ||||||
|  | 	@for f in $(MANFILES) ; do \ | ||||||
|  | 		if [ ! -e "$(DOC_DIR)/$$f" ]; then \ | ||||||
|  | 			continue ;\ | ||||||
|  | 		fi ;\ | ||||||
|  | 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\ | ||||||
|  | 		file=$$section/$$f ;\ | ||||||
|  | 		mkdir -p $$section ;\ | ||||||
|  | 		install -m 0644 $(DOC_DIR)/$$f $$file ;\ | ||||||
|  | 		sed -e 's:__VERSION__:'$(VERSION)':g' $$file > temp_file && \ | ||||||
|  | 		mv temp_file $$file ;\ | ||||||
|  | 		sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ | ||||||
|  | 		mv temp_file $$file ;\ | ||||||
|  | 		gzip -f -9 $$file ;\ | ||||||
|  | 	done | ||||||
|  |  | ||||||
|  | .PHONY: install | ||||||
							
								
								
									
										24
									
								
								cfg/targets/uninstall.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								cfg/targets/uninstall.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | # Uninstall target | ||||||
|  | uninstall: | ||||||
|  | 	@echo "Removing toxic executable" | ||||||
|  | 	@rm -f $(abspath $(DESTDIR)/$(BINDIR)/toxic) | ||||||
|  | 	 | ||||||
|  | 	@echo "Removing desktop file" | ||||||
|  | 	@rm -f $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE)) | ||||||
|  | 	 | ||||||
|  | 	@echo "Removing data files" | ||||||
|  | 	@for f in $(DATAFILES) ; do \ | ||||||
|  | 		rm -f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\ | ||||||
|  | 	done | ||||||
|  | 	@for f in $(SNDFILES) ; do \ | ||||||
|  | 		rm -f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\ | ||||||
|  | 	done | ||||||
|  | 	 | ||||||
|  | 	@echo "Removing man pages" | ||||||
|  | 	@for f in $(MANFILES) ; do \ | ||||||
|  | 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\ | ||||||
|  | 		file=$$section/$$f ;\ | ||||||
|  | 		rm -f $$file $$file.gz ;\ | ||||||
|  | 	done | ||||||
|  |  | ||||||
|  | .PHONY: uninstall | ||||||
							
								
								
									
										506
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										506
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -1,506 +0,0 @@ | |||||||
| #                                               -*- Autoconf -*- |  | ||||||
| # Process this file with autoconf to produce a configure script. |  | ||||||
|  |  | ||||||
| AC_PREREQ([2.65]) |  | ||||||
| AC_INIT([toxic], [0.4.1], [https://tox.im/]) |  | ||||||
| AC_CONFIG_AUX_DIR(configure_aux) |  | ||||||
| AC_CONFIG_SRCDIR([src/toxic.c]) |  | ||||||
| AC_CONFIG_HEADERS([config.h]) |  | ||||||
| AM_INIT_AUTOMAKE([1.10 -Wall]) |  | ||||||
| m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) |  | ||||||
| AC_CONFIG_MACRO_DIR([m4]) |  | ||||||
|  |  | ||||||
| if test "x${prefix}" = "xNONE"; then |  | ||||||
|     prefix="${ac_default_prefix}" |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| DEPSEARCH= |  | ||||||
| LIBTOXCORE_SEARCH_HEADERS= |  | ||||||
| LIBTOXCORE_SEARCH_LIBS= |  | ||||||
| LIBSODIUM_SEARCH_HEADERS= |  | ||||||
| LIBSODIUM_SEARCH_LIBS= |  | ||||||
|  |  | ||||||
| LIBTOXCORE_FOUND="no" |  | ||||||
| NCURSES_FOUND="no" |  | ||||||
| NCURSES_WIDECHAR_SUPPORT="no" |  | ||||||
|  |  | ||||||
| AC_ARG_WITH(dependency-search, |  | ||||||
|     AC_HELP_STRING([--with-dependency-search=DIR], |  | ||||||
|                    [search for dependencies in DIR, i.e. look for libraries in |  | ||||||
|                     DIR/lib and for headers in DIR/include]), |  | ||||||
|     [ |  | ||||||
|         DEPSEARCH="$withval" |  | ||||||
|     ] |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| if test -n "$DEPSEARCH"; then |  | ||||||
|     CFLAGS="$CFLAGS -I$DEPSEARCH/include" |  | ||||||
|     CPPFLAGS="$CPPFLAGS -I$DEPSEARCH/include" |  | ||||||
|     LDFLAGS="$LDFLAGS -L$DEPSEARCH/lib" |  | ||||||
|     export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$DEPSEARCH/lib/pkgconfig:/usr/local/lib/pkgconfig |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| AC_ARG_WITH(libtoxcore-headers, |  | ||||||
|         AC_HELP_STRING([--with-libtoxcore-headers=DIR], |  | ||||||
|                        [search for libtoxcore header files in DIR/tox]), |  | ||||||
|         [ |  | ||||||
|             LIBTOXCORE_SEARCH_HEADERS="$withval" |  | ||||||
|             AC_MSG_NOTICE([Will search for libtoxcore header files in $withval]) |  | ||||||
|         ] |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| AC_ARG_WITH(libtoxcore-libs, |  | ||||||
|         AC_HELP_STRING([--with-libtoxcore-libs=DIR], |  | ||||||
|                        [search for libtoxcore libraries in DIR]), |  | ||||||
|         [ |  | ||||||
|             LIBTOXCORE_SEARCH_LIBS="$withval" |  | ||||||
|             AC_MSG_NOTICE([Will search for libtoxcore libraries in $withval]) |  | ||||||
|         ] |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| AC_ARG_WITH(libsodium-headers, |  | ||||||
|         AC_HELP_STRING([--with-libsodium-headers=DIR], |  | ||||||
|                        [search for libsodium header files in DIR]), |  | ||||||
|         [ |  | ||||||
|             LIBSODIUM_SEARCH_HEADERS="$withval" |  | ||||||
|             AC_MSG_NOTICE([Will search for libsodium header files in $withval]) |  | ||||||
|         ] |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| AC_ARG_WITH(libsodium-libs, |  | ||||||
|         AC_HELP_STRING([--with-libsodium-libs=DIR], |  | ||||||
|                        [search for libsodium libraries in DIR]), |  | ||||||
|         [ |  | ||||||
|             LIBSODIUM_SEARCH_LIBS="$withval" |  | ||||||
|             AC_MSG_NOTICE([Will search for libsodium libraries in $withval]) |  | ||||||
|         ] |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| WIN32=no |  | ||||||
| MACH=no |  | ||||||
| AC_CANONICAL_HOST |  | ||||||
| case $host_os in |  | ||||||
|     *mingw*) |  | ||||||
|         WIN32="yes" |  | ||||||
|     ;; |  | ||||||
|     darwin*) |  | ||||||
|         MACH=yes |  | ||||||
|     ;; |  | ||||||
|     *freebsd*) |  | ||||||
|         LDFLAGS="$LDFLAGS -L/usr/local/lib" |  | ||||||
|         CFLAGS="$CFLAGS -I/usr/local/include" |  | ||||||
|         CPPFLAGS="$CPPFLAGS -I/usr/local/include" |  | ||||||
|     ;; |  | ||||||
| esac |  | ||||||
|  |  | ||||||
| # Checks for programs. |  | ||||||
| AC_PROG_CC |  | ||||||
| AM_PROG_CC_C_O |  | ||||||
|  |  | ||||||
| AC_CHECK_HEADERS( |  | ||||||
|     [limits.h locale.h stdint.h stdlib.h string.h unistd.h wchar.h wctype.h], |  | ||||||
|     [], |  | ||||||
|     [ AC_MSG_ERROR([required header is missing on your system]) ]) |  | ||||||
|  |  | ||||||
| # Checks for typedefs, structures, and compiler characteristics. |  | ||||||
| AC_HEADER_STDBOOL |  | ||||||
| AC_TYPE_SIZE_T |  | ||||||
| AC_TYPE_UINT16_T |  | ||||||
| AC_TYPE_UINT32_T |  | ||||||
| AC_TYPE_UINT64_T |  | ||||||
| AC_TYPE_UINT8_T |  | ||||||
|  |  | ||||||
| # Checks for library functions. |  | ||||||
| AC_FUNC_MALLOC |  | ||||||
| AC_CHECK_FUNCS( |  | ||||||
|     [iswprint memmove memset mkdir setlocale strchr strdup], |  | ||||||
|     [], |  | ||||||
|     [ AC_MSG_ERROR([required library function is missing on your system])]) |  | ||||||
|  |  | ||||||
| AX_PTHREAD( [], [ AC_MSG_ERROR([pthreads not found on your system]) ]) |  | ||||||
|  |  | ||||||
| # pkg-config based tests |  | ||||||
| PKG_PROG_PKG_CONFIG |  | ||||||
|  |  | ||||||
| if test -n "$PKG_CONFIG"; then |  | ||||||
|     if test "x$WIN32" != "xyes"; then |  | ||||||
|         PKG_CHECK_MODULES([NCURSES], [ncursesw], |  | ||||||
|             [ |  | ||||||
|                 NCURSES_FOUND="yes" |  | ||||||
|                 NCURSES_WIDECHAR_SUPPORT="yes" |  | ||||||
|             ], |  | ||||||
|             [ |  | ||||||
|                 NCURSES_WIDECHAR_SUPPORT="no" |  | ||||||
|                 PKG_CHECK_MODULES([NCURSES], [ncurses], |  | ||||||
|                     [ |  | ||||||
|                         NCURSES_FOUND="yes" |  | ||||||
|                     ], |  | ||||||
|                     [ |  | ||||||
|                         AC_MSG_WARN([$NCURSES_PKG_ERRORS]) |  | ||||||
|                     ]) |  | ||||||
|             ]) |  | ||||||
|     fi |  | ||||||
| else |  | ||||||
|     AC_MSG_WARN([pkg-config was not found on your sytem]) |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then |  | ||||||
|     AC_PATH_PROG([CURSES_CONFIG], [ncursesw5-config], [no]) |  | ||||||
|     if test "x$CURSES_CONFIG" != "xno"; then |  | ||||||
|         AC_MSG_CHECKING(ncurses cflags) |  | ||||||
|         NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags` |  | ||||||
|         AC_MSG_RESULT($NCURSES_CFLAGS) |  | ||||||
|  |  | ||||||
|         AC_MSG_CHECKING(ncurses libraries) |  | ||||||
|         NCURSES_LIBS=`${CURSES_CONFIG} --libs` |  | ||||||
|         AC_MSG_RESULT($NCURSES_LIBS) |  | ||||||
|  |  | ||||||
|         AC_SUBST(NCURSES_CFLAGS) |  | ||||||
|         AC_SUBST(NCURSES_LIBS) |  | ||||||
|         NCURSES_FOUND="yes" |  | ||||||
|         NCURSES_WIDECHAR_SUPPORT="yes" |  | ||||||
|     fi |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then |  | ||||||
|     unset ac_cv_path_CURSES_CONFIG |  | ||||||
|     AC_PATH_PROG([CURSES_CONFIG], [ncursesw5.4-config], [no]) |  | ||||||
|     if test "x$CURSES_CONFIG" != "xno"; then |  | ||||||
|         AC_MSG_CHECKING(ncurses cflags) |  | ||||||
|         NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags` |  | ||||||
|         AC_MSG_RESULT($NCURSES_CFLAGS) |  | ||||||
|  |  | ||||||
|         AC_MSG_CHECKING(ncurses libraries) |  | ||||||
|         NCURSES_LIBS=`${CURSES_CONFIG} --libs` |  | ||||||
|         AC_MSG_RESULT($NCURSES_LIBS) |  | ||||||
|  |  | ||||||
|         AC_SUBST(NCURSES_CFLAGS) |  | ||||||
|         AC_SUBST(NCURSES_LIBS) |  | ||||||
|         NCURSES_FOUND="yes" |  | ||||||
|         NCURSES_WIDECHAR_SUPPORT="yes" |  | ||||||
|     fi |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then |  | ||||||
|     unset ac_cv_path_CURSES_CONFIG |  | ||||||
|     AC_PATH_PROG([CURSES_CONFIG], [ncurses5-config], [no]) |  | ||||||
|     if test "x$CURSES_CONFIG" != "xno"; then |  | ||||||
|         AC_MSG_CHECKING(ncurses cflags) |  | ||||||
|         NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags` |  | ||||||
|         AC_MSG_RESULT($NCURSES_CFLAGS) |  | ||||||
|  |  | ||||||
|         AC_MSG_CHECKING(ncurses libraries) |  | ||||||
|         NCURSES_LIBS=`${CURSES_CONFIG} --libs` |  | ||||||
|         AC_MSG_RESULT($NCURSES_LIBS) |  | ||||||
|  |  | ||||||
|         AC_SUBST(NCURSES_CFLAGS) |  | ||||||
|         AC_SUBST(NCURSES_LIBS) |  | ||||||
|         NCURSES_FOUND="yes" |  | ||||||
|     fi |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then |  | ||||||
|     unset ac_cv_path_CURSES_CONFIG |  | ||||||
|     AC_PATH_PROG([CURSES_CONFIG], [ncurses5.4-config], [no]) |  | ||||||
|     if test "x$CURSES_CONFIG" != "xno"; then |  | ||||||
|         AC_MSG_CHECKING(ncurses cflags) |  | ||||||
|         NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags` |  | ||||||
|         AC_MSG_RESULT($NCURSES_CFLAGS) |  | ||||||
|  |  | ||||||
|         AC_MSG_CHECKING(ncurses libraries) |  | ||||||
|         NCURSES_LIBS=`${CURSES_CONFIG} --libs` |  | ||||||
|         AC_MSG_RESULT($NCURSES_LIBS) |  | ||||||
|  |  | ||||||
|         AC_SUBST(NCURSES_CFLAGS) |  | ||||||
|         AC_SUBST(NCURSES_LIBS) |  | ||||||
|         NCURSES_FOUND="yes" |  | ||||||
|     fi |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| if test "x$NCURSES_FOUND" = "xno"; then |  | ||||||
|     AC_CHECK_HEADER([curses.h], |  | ||||||
|         [], |  | ||||||
|         [ |  | ||||||
|             AC_MSG_ERROR([headers for the ncurses library were not found on your system]) |  | ||||||
|         ] |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     if test "x$WIN32" = "xyes"; then |  | ||||||
|         dnl Check if pdcurses provides wide char support |  | ||||||
|         NCURSES_WIDECHAR_SUPPORT="no" |  | ||||||
|         AC_CHECK_LIB([pdcurses], [clear], |  | ||||||
|             [], |  | ||||||
|             [ |  | ||||||
|                 AC_MSG_ERROR([required library pdcurses was not found on your system]) |  | ||||||
|             ] |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         AC_CHECK_LIB(ws2_32, main, |  | ||||||
|             [ |  | ||||||
|                 WINSOCK2_LIBS="-lws2_32" |  | ||||||
|                 AC_SUBST(WINSOCK2_LIBS) |  | ||||||
|             ], |  | ||||||
|             [ |  | ||||||
|                 AC_MSG_ERROR([required library winsock2 was not found on the system, please check your MinGW installation]) |  | ||||||
|             ] |  | ||||||
|         ) |  | ||||||
|         AC_DEFINE([_WIN32_WINNT], [0x501], |  | ||||||
|                   [enable getaddrinfo/freeaddrinfo on XP and higher]) |  | ||||||
|     else |  | ||||||
|         AC_CHECK_LIB([ncursesw], [wget_wch], |  | ||||||
|             [ |  | ||||||
|                 NCURSES_WIDECHAR_SUPPORT="yes" |  | ||||||
|             ], |  | ||||||
|             [ |  | ||||||
|                 unset ac_cv_lib_ncursesw_wget_wch |  | ||||||
|                 AC_CHECK_LIB([ncursesw], [wget_wch], |  | ||||||
|                     [ |  | ||||||
|                         NCURSES_WIDECHAR_SUPPORT="yes" |  | ||||||
|                     ], |  | ||||||
|                     [ |  | ||||||
|                         NCURSES_WIDECHAR_SUPPORT="no" |  | ||||||
|                         AC_CHECK_LIB([ncurses], [clear], |  | ||||||
|                             [], |  | ||||||
|                             [ |  | ||||||
|                                 unset ac_cv_lib_ncurses_clear |  | ||||||
|                                 AC_CHECK_LIB([ncurses], [clear], |  | ||||||
|                                     [], |  | ||||||
|                                     [ |  | ||||||
|                                         AC_MSG_ERROR([required library ncurses was not found on your system]) |  | ||||||
|                                     ], |  | ||||||
|                                     [ |  | ||||||
|                                         -ltinfo |  | ||||||
|                                     ] |  | ||||||
|                                 ) |  | ||||||
|                             ] |  | ||||||
|                         ) |  | ||||||
|                     ], |  | ||||||
|                     [ |  | ||||||
|                         -ltinfo |  | ||||||
|                     ] |  | ||||||
|                 ) |  | ||||||
|             ] |  | ||||||
|         ) |  | ||||||
|     fi |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| if test -n "$PKG_CONFIG"; then |  | ||||||
|     PKG_CHECK_MODULES(LIBTOXCORE, [libtoxcore], |  | ||||||
|         [ |  | ||||||
|             LIBTOXCORE_FOUND="yes" |  | ||||||
|         ], |  | ||||||
|         [ |  | ||||||
|             AC_MSG_WARN([required library libsodium was not found in requested location $LIBSODIUM_SEARCH_LIBS]) |  | ||||||
|         ]) |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| if test "x$LIBTOXCORE_FOUND" = "xno"; then |  | ||||||
|     LIBSODIUM_LIBS= |  | ||||||
|     LIBSODIUM_LDFLAGS= |  | ||||||
|     LDFLAGS_SAVE="$LDFLAGS" |  | ||||||
|     if test -n "$LIBSODIUM_SEARCH_LIBS"; then |  | ||||||
|         LDFLAGS="$LDFLAGS -L$LIBSODIUM_SEARCH_LIBS" |  | ||||||
|         AC_CHECK_LIB(sodium, randombytes_random, |  | ||||||
|             [ |  | ||||||
|                 LIBSODIUM_LDFLAGS="-L$LIBSODIUM_SEARCH_LIBS" |  | ||||||
|                 LIBSODIUM_LIBS="-lsodium" |  | ||||||
|             ], |  | ||||||
|             [ |  | ||||||
|                 AC_MSG_ERROR([required library libsodium was not found in requested location $LIBSODIUM_SEARCH_LIBS]) |  | ||||||
|             ] |  | ||||||
|         ) |  | ||||||
|     else |  | ||||||
|         AC_CHECK_LIB(sodium, randombytes_random, |  | ||||||
|             [], |  | ||||||
|             [ |  | ||||||
|                 AC_MSG_ERROR([required library libsodium was not found on your system, please check http://download.libsodium.org/libsodium/releases/]) |  | ||||||
|             ] |  | ||||||
|         ) |  | ||||||
|     fi |  | ||||||
|  |  | ||||||
|     LDFLAGS="$LDFLAGS_SAVE" |  | ||||||
|     AC_SUBST(LIBSODIUM_LIBS) |  | ||||||
|     AC_SUBST(LIBSODIUM_LDFLAGS) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     LIBTOXCORE_CFLAGS= |  | ||||||
|     CFLAGS_SAVE="$CFLAGS" |  | ||||||
|     CPPFLAGS_SAVE="$CPPFLAGS" |  | ||||||
|  |  | ||||||
|     if test -n "$LIBTOXCORE_SEARCH_HEADERS"; then |  | ||||||
|         CFLAGS="$CFLAGS -I$LIBTOXCORE_SEARCH_HEADERS" |  | ||||||
|         CPPFLAGS="$CPPFLAGS -I$LIBTOXCORE_SEARCH_HEADERS" |  | ||||||
|         AC_CHECK_HEADER([tox/tox.h], |  | ||||||
|             [ |  | ||||||
|                 LIBTOXCORE_CFLAGS="-I$LIBTOXCORE_SEARCH_HEADERS" |  | ||||||
|             ], |  | ||||||
|             [ |  | ||||||
|                 AC_MSG_ERROR([headers for the toxcore library were not found on your system]) |  | ||||||
|             ] |  | ||||||
|         ) |  | ||||||
|     else |  | ||||||
|         AC_CHECK_HEADER([tox/tox.h], |  | ||||||
|             [], |  | ||||||
|             [ |  | ||||||
|                 AC_MSG_ERROR([headers for the toxcore library were not found on your system]) |  | ||||||
|             ], |  | ||||||
|         ) |  | ||||||
|     fi |  | ||||||
|     CFLAGS="$CFLAGS_SAVE" |  | ||||||
|     CPPFLAGS="$CPPFLAGS_SAVE" |  | ||||||
|     AC_SUBST(LIBTOXCORE_CFLAGS) |  | ||||||
|  |  | ||||||
|     LIBTOXCORE_LIBS= |  | ||||||
|     LIBTOXCORE_LDFLAGS= |  | ||||||
|     LDFLAGS_SAVE="$LDFLAGS" |  | ||||||
|     if test -n "$LIBTOXCORE_SEARCH_LIBS"; then |  | ||||||
|         LDFLAGS="$LDFLAGS $LIBSODIUM_LDFLAGS -L$LIBTOXCORE_SEARCH_LIBS" |  | ||||||
|         AC_CHECK_LIB([toxcore], [tox_new], |  | ||||||
|             [ |  | ||||||
|                 LIBTOXCORE_LDFLAGS="-L$LIBTOXCORE_SEARCH_LIBS" |  | ||||||
|                 LIBTOXCORE_LIBS="-ltoxcore" |  | ||||||
|             ], |  | ||||||
|             [ |  | ||||||
|                 AC_MSG_ERROR([required library toxcore was not found on your system]) |  | ||||||
|             ], |  | ||||||
|             [ |  | ||||||
|                 $WINSOCK2_LIBS |  | ||||||
|                 $LIBSODIUM_LIBS |  | ||||||
|             ] |  | ||||||
|         ) |  | ||||||
|     else |  | ||||||
|         LDFLAGS="$LDFLAGS $LIBSODIUM_LDFLAGS" |  | ||||||
|         AC_CHECK_LIB([toxcore], [tox_new], |  | ||||||
|             [], |  | ||||||
|             [ |  | ||||||
|                 AC_MSG_ERROR([required library toxcore was not found on your system]) |  | ||||||
|             ], |  | ||||||
|             [ |  | ||||||
|                 $WINSOCK2_LIBS |  | ||||||
|                 $LIBSODIUM_LIBS |  | ||||||
|             ] |  | ||||||
|         ) |  | ||||||
|     fi |  | ||||||
|     LDFLAGS="$LDFLAGS_SAVE" |  | ||||||
|     AC_SUBST(LIBTOXCORE_LIBS) |  | ||||||
|     AC_SUBST(LIBTOXCORE_LDFLAGS) |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| AC_CHECK_HEADER([resolv.h], [], |  | ||||||
|                 [ |  | ||||||
|                     AC_MSG_ERROR([resolv.h header was not found on your system]) |  | ||||||
|                 ]) |  | ||||||
|  |  | ||||||
| AC_CHECK_LIB(resolv, __res_init, [], |  | ||||||
|              [ |  | ||||||
|                 AC_MSG_ERROR([libresolv library was not found on your system]) |  | ||||||
|              ]) |  | ||||||
|  |  | ||||||
| #### |  | ||||||
| #### A/V Stuff |  | ||||||
|  |  | ||||||
| AV_SEARCH_DIR= |  | ||||||
| BUILD_AV="yes" |  | ||||||
|  |  | ||||||
| AC_ARG_WITH(libtoxav-prefix, |  | ||||||
|     AC_HELP_STRING([--with-libtoxav-prefix=DIR], |  | ||||||
|                    [search for libtoxav in DIR, i.e. look for libraries in |  | ||||||
|                     DIR/lib and for headers in DIR/include]), |  | ||||||
|     [ |  | ||||||
|         AV_SEARCH_DIR="$withval" |  | ||||||
|     ] |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| if test -n "$AV_SEARCH_DIR"; then |  | ||||||
|     CFLAGS="$CFLAGS -I$AV_SEARCH_DIR/include" |  | ||||||
|     CPPFLAGS="$CPPFLAGS -I$AV_SEARCH_DIR/include" |  | ||||||
|     LDFLAGS="$LDFLAGS -L$AV_SEARCH_DIR/lib" |  | ||||||
|     export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$AV_SEARCH_DIR/lib/pkgconfig |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| # Check if specified enable |  | ||||||
| AC_ARG_ENABLE([av], |  | ||||||
|     [AC_HELP_STRING([--disable-av], [build AV support libraries (default: auto)]) ], |  | ||||||
|     [ |  | ||||||
|         if test "x$enableval" = "xno"; then |  | ||||||
|             BUILD_AV="no" |  | ||||||
|         elif test "x$enableval" = "xyes"; then |  | ||||||
|             BUILD_AV="yes" |  | ||||||
|         fi |  | ||||||
|     ] |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| # Check for A/V library |  | ||||||
|  |  | ||||||
| if test "x$BUILD_AV" = "xyes"; then |  | ||||||
|     PKG_CHECK_MODULES([OPENAL], [openal], |  | ||||||
|     [], |  | ||||||
|     [ |  | ||||||
|         if test "x$MACH" = "xyes"; then |  | ||||||
|             CFLAGS="$CFLAGS -framework OpenAL" |  | ||||||
|             AC_CHECK_HEADER([OpenAL/al.h], |  | ||||||
|                 [ |  | ||||||
|                     OPENAL_CFLAGS="-framework OpenAL" |  | ||||||
|                     OPENAL_LIBS="-framework OpenAL" |  | ||||||
|                     AC_SUBST(OPENAL_CFLAGS) |  | ||||||
|                     AC_SUBST(OPENAL_LIBS) |  | ||||||
|                 ], |  | ||||||
|                 [ |  | ||||||
|                     AC_MSG_NOTICE([No openal framework; disabling A/V support]) |  | ||||||
|                     BUILD_AV="no" |  | ||||||
|                 ] |  | ||||||
|             ) |  | ||||||
|             CFLAGS="$CFLAGS_SAVE" |  | ||||||
|         else |  | ||||||
|             AC_MSG_NOTICE([No openal library; disabling A/V support]) |  | ||||||
|             BUILD_AV="no" |  | ||||||
|         fi |  | ||||||
|     ]) |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| if test "x$BUILD_AV" = "xyes"; then |  | ||||||
|     PKG_CHECK_MODULES([LIBTOXAV], [libtoxav], |  | ||||||
|     [ |  | ||||||
|         AC_CHECK_HEADER([tox/toxav.h], |  | ||||||
|         [ |  | ||||||
|             # Place define for audio support |  | ||||||
|             AC_DEFINE([_SUPPORT_AUDIO], [], [Is audio supported]) |  | ||||||
|             AC_MSG_NOTICE([Building with audio support]) |  | ||||||
|         ], |  | ||||||
|         [ |  | ||||||
|             AC_MSG_NOTICE([No A/V headers; disabling A/V support]) |  | ||||||
|             BUILD_AV="no" |  | ||||||
|         ],) |  | ||||||
|     ], |  | ||||||
|     [ |  | ||||||
|         AC_MSG_NOTICE([No A/V library; disabling A/V support]) |  | ||||||
|         BUILD_AV="no" |  | ||||||
|     ]) |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes") |  | ||||||
|  |  | ||||||
| TOXIC_VERSION="$PACKAGE_VERSION" |  | ||||||
| AC_PATH_PROG([GIT], [git], [no]) |  | ||||||
| if test "x$GIT" != "xno"; then |  | ||||||
|     if test -d ${srcdir}/.git; then |  | ||||||
|         TOXIC_VERSION="${TOXIC_VERSION}_r`${GIT} rev-list HEAD --count`" |  | ||||||
|     fi |  | ||||||
| fi |  | ||||||
| AC_SUBST(TOXIC_VERSION) |  | ||||||
|  |  | ||||||
| eval PACKAGE_DATADIR="${datadir}/${PACKAGE}" |  | ||||||
| eval PACKAGE_DATADIR="${PACKAGE_DATADIR}" |  | ||||||
| AC_DEFINE_UNQUOTED(PACKAGE_DATADIR, "$PACKAGE_DATADIR", [toxic data directory]) |  | ||||||
|  |  | ||||||
| if test "x$NCURSES_WIDECHAR_SUPPORT" = "xyes"; then |  | ||||||
|     AC_DEFINE([HAVE_WIDECHAR], [1], [ncurses wide char support available]) |  | ||||||
|     AC_DEFINE([_XOPEN_SOURCE_EXTENDED], [1], |  | ||||||
|               [enable X/Open Portability Guide functionality]) |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| AC_CONFIG_FILES([Makefile |  | ||||||
|                  misc/Makefile |  | ||||||
|                  build/Makefile]) |  | ||||||
| AC_OUTPUT |  | ||||||
							
								
								
									
										164
									
								
								doc/toxic.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								doc/toxic.1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | |||||||
|  | '\" t | ||||||
|  | .\"     Title: toxic | ||||||
|  | .\"    Author: [see the "AUTHORS" section] | ||||||
|  | .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | ||||||
|  | .\"      Date: 2016-09-20 | ||||||
|  | .\"    Manual: Toxic Manual | ||||||
|  | .\"    Source: toxic __VERSION__ | ||||||
|  | .\"  Language: English | ||||||
|  | .\" | ||||||
|  | .TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual" | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" * Define some portability stuff | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  | .\" http://bugs.debian.org/507673 | ||||||
|  | .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html | ||||||
|  | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  | .ie \n(.g .ds Aq \(aq | ||||||
|  | .el       .ds Aq ' | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" * set default formatting | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" disable hyphenation | ||||||
|  | .nh | ||||||
|  | .\" disable justification (adjust text to left margin only) | ||||||
|  | .ad l | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" * MAIN CONTENT STARTS HERE * | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .SH "NAME" | ||||||
|  | toxic \- CLI client for Tox | ||||||
|  | .SH "SYNOPSIS" | ||||||
|  | .sp | ||||||
|  | \fBtoxic\fR [\-f \fIdata\-file\fR] [\-x] [\-4] [\-c \fIconfig\-file\fR] [\-n \fInodes\-file\fR] [\-h] | ||||||
|  | .SH "DESCRIPTION" | ||||||
|  | .sp | ||||||
|  | toxic is an ncurses\-based instant messaging client for Tox which formerly resided in the Tox core repository, and is now available as a standalone application\&. | ||||||
|  | .SH "OPTIONS" | ||||||
|  | .PP | ||||||
|  | \-4, \-\-ipv4 | ||||||
|  | .RS 4 | ||||||
|  | Force IPv4 connection | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \-b, \-\-debug | ||||||
|  | .RS 4 | ||||||
|  | Enable stderr for debugging\&. Redirect output to avoid breaking the curses interface and better capture messages\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \-c, \-\-config config\-file | ||||||
|  | .RS 4 | ||||||
|  | Use specified | ||||||
|  | \fIconfig\-file\fR | ||||||
|  | instead of | ||||||
|  | \fI~/\&.config/tox/toxic\&.conf\fR | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \-d, \-\-default\-locale | ||||||
|  | .RS 4 | ||||||
|  | Use default locale | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \-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 | ||||||
|  | \fIdata\-file\fR | ||||||
|  | instead of | ||||||
|  | \fI~/\&.config/tox/toxic_profile\&.tox\fR | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \-h, \-\-help | ||||||
|  | .RS 4 | ||||||
|  | Show help message | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \-n, \-\-nodes nodes\-file | ||||||
|  | .RS 4 | ||||||
|  | Use specified | ||||||
|  | \fInodes\-file\fR | ||||||
|  | for DHT bootstrap nodes instead of | ||||||
|  | \fI~/\&.config/tox/DHTnodes\&.json\fR | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \-o, \-\-noconnect | ||||||
|  | .RS 4 | ||||||
|  | Do not connect to the DHT network | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \-p, \-\-SOCKS5\-proxy | ||||||
|  | .RS 4 | ||||||
|  | Use a SOCKS5 proxy: Requires [IP] [port] | ||||||
|  | .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 | ||||||
							
								
								
									
										105
									
								
								doc/toxic.1.asc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								doc/toxic.1.asc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | |||||||
|  | toxic(1) | ||||||
|  | ======== | ||||||
|  |  | ||||||
|  | NAME | ||||||
|  | ---- | ||||||
|  | toxic - CLI client for Tox | ||||||
|  |  | ||||||
|  | SYNOPSIS | ||||||
|  | -------- | ||||||
|  | *toxic* [-f 'data-file'] [-x] [-4] [-c 'config-file'] [-n 'nodes-file'] [-h] | ||||||
|  |  | ||||||
|  | DESCRIPTION | ||||||
|  | ----------- | ||||||
|  | toxic is an ncurses-based instant messaging client for Tox which formerly | ||||||
|  | resided in the Tox core repository, and is now available as a standalone | ||||||
|  | application. | ||||||
|  |  | ||||||
|  | OPTIONS | ||||||
|  | ------- | ||||||
|  | -4, --ipv4:: | ||||||
|  |     Force IPv4 connection | ||||||
|  |  | ||||||
|  | -b, --debug:: | ||||||
|  |     Enable stderr for debugging. Redirect output to | ||||||
|  |     avoid breaking the curses interface and better capture messages. | ||||||
|  |  | ||||||
|  | -c, --config config-file:: | ||||||
|  |     Use specified 'config-file' instead of '~/.config/tox/toxic.conf' | ||||||
|  |  | ||||||
|  | -d, --default-locale:: | ||||||
|  |     Use default locale | ||||||
|  |  | ||||||
|  | -e, --encrypt-data:: | ||||||
|  |     Encrypt an unencrypted data file. An error will occur if this option | ||||||
|  |     is used with an encrypted data file. | ||||||
|  |  | ||||||
|  | -f, --file data-file:: | ||||||
|  |     Use specified 'data-file' instead of '~/.config/tox/toxic_profile.tox' | ||||||
|  |  | ||||||
|  | -h, --help:: | ||||||
|  |     Show help message | ||||||
|  |  | ||||||
|  | -n, --nodes nodes-file:: | ||||||
|  |     Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json' | ||||||
|  |  | ||||||
|  | -o, --noconnect:: | ||||||
|  |     Do not connect to the DHT network | ||||||
|  |  | ||||||
|  | -p, --SOCKS5-proxy:: | ||||||
|  |     Use a SOCKS5 proxy: Requires [IP] [port] | ||||||
|  |  | ||||||
|  | -P, --HTTP-proxy:: | ||||||
|  |     Use a HTTP proxy: Requires [IP] [port] | ||||||
|  |  | ||||||
|  | -r, --namelist:: | ||||||
|  |     Use specified nameservers list | ||||||
|  |  | ||||||
|  | -t, --force-tcp:: | ||||||
|  |     Force TCP connection (use this with proxies) | ||||||
|  |  | ||||||
|  | -T, --tcp-relay:: | ||||||
|  |     Act as a TCP relay server for the network (Note: this uses significantly more bandwidth) | ||||||
|  |  | ||||||
|  | -u, --unencrypt-data:: | ||||||
|  |     Unencrypt a data file. A warning will appear if this option is used | ||||||
|  |     with a data file that is already unencrypted. | ||||||
|  |  | ||||||
|  | FILES | ||||||
|  | ----- | ||||||
|  | ~/.config/tox/DHTnodes.json:: | ||||||
|  |     Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat). | ||||||
|  |     This list is automatically updated. See *toxic.conf*(5) for details on controlling the update frequency. | ||||||
|  |  | ||||||
|  | ~/.config/tox/toxic_profile.tox:: | ||||||
|  |     Savestate which contains your personal info (nickname, Tox ID, contacts, | ||||||
|  |     etc) | ||||||
|  |  | ||||||
|  | ~/.config/tox/toxic.conf:: | ||||||
|  |     Configuration file. See *toxic.conf*(5) for more details. | ||||||
|  |  | ||||||
|  | {datadir}/toxic.conf.example:: | ||||||
|  |     Configuration example. | ||||||
|  |  | ||||||
|  | BUGS | ||||||
|  | ---- | ||||||
|  | -Unicode characters with a width larger than 1 column may cause strange | ||||||
|  | behaviour. | ||||||
|  |  | ||||||
|  | -Text occasionally fails to auto-scroll to the bottom. | ||||||
|  |  | ||||||
|  | -Screen flickering sometimes occurs on certain terminals. | ||||||
|  |  | ||||||
|  | AUTHORS | ||||||
|  | ------- | ||||||
|  | JFreegman <JFreegman@gmail.com> | ||||||
|  |  | ||||||
|  | SEE ALSO | ||||||
|  | -------- | ||||||
|  | *toxic.conf*(5) | ||||||
|  |  | ||||||
|  | LINKS | ||||||
|  | ----- | ||||||
|  | Project page: <https://github.com/JFreegman/toxic> | ||||||
|  |  | ||||||
|  | IRC channel: chat.freenode.net#tox | ||||||
							
								
								
									
										385
									
								
								doc/toxic.conf.5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								doc/toxic.conf.5
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,385 @@ | |||||||
|  | '\" t | ||||||
|  | .\"     Title: toxic.conf | ||||||
|  | .\"    Author: [see the "AUTHORS" section] | ||||||
|  | .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | ||||||
|  | .\"      Date: 2016-07-21 | ||||||
|  | .\"    Manual: Toxic Manual | ||||||
|  | .\"    Source: toxic __VERSION__ | ||||||
|  | .\"  Language: English | ||||||
|  | .\" | ||||||
|  | .TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual" | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" * Define some portability stuff | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  | .\" http://bugs.debian.org/507673 | ||||||
|  | .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html | ||||||
|  | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  | .ie \n(.g .ds Aq \(aq | ||||||
|  | .el       .ds Aq ' | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" * set default formatting | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" disable hyphenation | ||||||
|  | .nh | ||||||
|  | .\" disable justification (adjust text to left margin only) | ||||||
|  | .ad l | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .\" * MAIN CONTENT STARTS HERE * | ||||||
|  | .\" ----------------------------------------------------------------- | ||||||
|  | .SH "NAME" | ||||||
|  | toxic.conf \- Configuration file for toxic | ||||||
|  | .SH "SYNOPSIS" | ||||||
|  | .sp | ||||||
|  | ~/\&.config/tox/toxic\&.conf | ||||||
|  | .SH "DESCRIPTION" | ||||||
|  | .sp | ||||||
|  | The \fItoxic\&.conf\fR file is the main configuration file for \fBtoxic\fR(1) client\&. It uses syntax accepted by \fBlibconfig\fR\&. Lines starting with "//" are comments and will be ignored\&. | ||||||
|  | .SH "EXAMPLE" | ||||||
|  | .sp | ||||||
|  | .if n \{\ | ||||||
|  | .RS 4 | ||||||
|  | .\} | ||||||
|  | .nf | ||||||
|  | // Configuration for interface | ||||||
|  | ui = { | ||||||
|  |     timestamps = true; | ||||||
|  |     alerts = false; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Configuration for audio | ||||||
|  | audio = { | ||||||
|  |     input_device = 1; | ||||||
|  | }; | ||||||
|  | .fi | ||||||
|  | .if n \{\ | ||||||
|  | .RE | ||||||
|  | .\} | ||||||
|  | .SH "OPTIONS" | ||||||
|  | .PP | ||||||
|  | \fBui\fR | ||||||
|  | .RS 4 | ||||||
|  | Configuration related to interface elements\&. | ||||||
|  | .PP | ||||||
|  | \fBtimestamps\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable or disable timestamps\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBtime_format\fR | ||||||
|  | .RS 4 | ||||||
|  | Select between 24 and 12 hour time\&. Specify 24 or 12\&. Setting timestamp_format and log_timestamp_format will override this setting\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBtimestamp_format\fR | ||||||
|  | .RS 4 | ||||||
|  | Time format string for the interface enclosed by double quotes\&. See | ||||||
|  | \fBdate\fR(1) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBlog_timestamp_format\fR | ||||||
|  | .RS 4 | ||||||
|  | Time format string for logging enclosed by double quotes\&. See | ||||||
|  | \fBdate\fR(1) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBalerts\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable or disable acoustic alerts on events\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBnative_colors\fR | ||||||
|  | .RS 4 | ||||||
|  | Select between native terminal colors and toxic color theme\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBautolog\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable or disable autologging\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBshow_typing_other\fR | ||||||
|  | .RS 4 | ||||||
|  | Show when others are typing in a 1\-on\-1 chat\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBshow_typing_self\fR | ||||||
|  | .RS 4 | ||||||
|  | Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBshow_welcome_msg\fR | ||||||
|  | .RS 4 | ||||||
|  | Show welcome message on startup\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBshow_connection_msg\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable friend connection change notifications\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBnodelist_update_freq\fR | ||||||
|  | .RS 4 | ||||||
|  | How often in days to update the DHT nodes list\&. (0 to disable updates) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBhistory_size\fR | ||||||
|  | .RS 4 | ||||||
|  | Maximum lines for chat window history\&. Integer value\&. (for example: 700) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBline_join\fR | ||||||
|  | .RS 4 | ||||||
|  | Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBline_quit\fR | ||||||
|  | .RS 4 | ||||||
|  | Indicator for when someone disconnects or leaves a group\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBline_alert\fR | ||||||
|  | .RS 4 | ||||||
|  | Indicator for alert messages\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBline_normal\fR | ||||||
|  | .RS 4 | ||||||
|  | Indicator for normal messages\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBline_special\fR | ||||||
|  | .RS 4 | ||||||
|  | Indicator for special messages\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBmplex_away\fR | ||||||
|  | .RS 4 | ||||||
|  | Set user status when attaching and detaching from GNU screen or tmux\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBmplex_away_note\fR | ||||||
|  | .RS 4 | ||||||
|  | Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&. | ||||||
|  | .sp | ||||||
|  | .if n \{\ | ||||||
|  | .RS 4 | ||||||
|  | .\} | ||||||
|  | .nf | ||||||
|  | The following options control whether to output a terminal bell on certain events\&. | ||||||
|  | Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&. | ||||||
|  | These options don\*(Aqt affect the "alerts" option\&. | ||||||
|  | .fi | ||||||
|  | .if n \{\ | ||||||
|  | .RE | ||||||
|  | .\} | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBbell_on_message\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable the terminal bell when receiving a message\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBbell_on_filetrans\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable the terminal bell when receiving a filetransfer\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBbell_on_filetrans_accept\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable the terminal bell when a filetransfer was accepted\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBbell_on_invite\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable the terminal bell when receiving a group/call invite\&. true or false | ||||||
|  | .RE | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBaudio\fR | ||||||
|  | .RS 4 | ||||||
|  | Configuration related to audio devices\&. | ||||||
|  | .PP | ||||||
|  | \fBinput_device\fR | ||||||
|  | .RS 4 | ||||||
|  | Audio input device\&. Integer value\&. Number corresponds to | ||||||
|  | /lsdev in | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBoutput_device\fR | ||||||
|  | .RS 4 | ||||||
|  | Audio output device\&. Integer value\&. Number corresponds to | ||||||
|  | /lsdev out | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBVAD_treshold\fR | ||||||
|  | .RS 4 | ||||||
|  | Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0 | ||||||
|  | .RE | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBtox\fR | ||||||
|  | .RS 4 | ||||||
|  | Configuration related to paths\&. | ||||||
|  | .PP | ||||||
|  | \fBdownload_path\fR | ||||||
|  | .RS 4 | ||||||
|  | Default path for downloads\&. String value\&. Absolute path for downloaded files\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBavatar_path\fR | ||||||
|  | .RS 4 | ||||||
|  | Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBchatlogs_path\fR | ||||||
|  | .RS 4 | ||||||
|  | Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBpassword_eval\fR | ||||||
|  | .RS 4 | ||||||
|  | Replace password prompt by running this command and using its output as the password\&. | ||||||
|  | .RE | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBsounds\fR | ||||||
|  | .RS 4 | ||||||
|  | Configuration related to notification sounds\&. Special value "silent" can be used to disable a specific notification\&. | ||||||
|  |  | ||||||
|  | Each value is a string which corresponds to the absolute path of a wav sound file\&. | ||||||
|  | .PP | ||||||
|  | \fBnotif_error\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when an error occurs\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBself_log_in\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when you log in\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBself_log_out\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when you log out\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBuser_log_in\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when a contact become online\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBuser_log_out\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when a contact become offline\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBcall_incoming\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when you receive an incoming call\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBcall_outgoing\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when you start a call\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBgeneric_message\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when an event occurs\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBtransfer_pending\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when you receive a file transfer request\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBtransfer_completed\fR | ||||||
|  | .RS 4 | ||||||
|  | Sound to play when a file transfer is completed\&. | ||||||
|  | .RE | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBkeys\fR | ||||||
|  | .RS 4 | ||||||
|  | Configuration related to user interface interaction\&. Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN\&. | ||||||
|  |  | ||||||
|  | Each value is a string which corresponds to a key combination\&. | ||||||
|  | .PP | ||||||
|  | \fBnext_tab\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to switch next tab\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBprev_tab\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to switch previous tab\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBscroll_line_up\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to scroll one line up\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBscroll_line_down\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to scroll one line down\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBhalf_page_up\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to scroll half page up\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBhalf_page_down\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to scroll half page down\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBpage_bottom\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to scroll to page bottom\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBpeer_list_up\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to scroll contacts list up\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBpeer_list_down\fR | ||||||
|  | .RS 4 | ||||||
|  | Key combination to scroll contacts list down\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBtoggle_peerlist\fR | ||||||
|  | .RS 4 | ||||||
|  | Toggle the peer list on and off\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBtoggle_paste_mode\fR | ||||||
|  | .RS 4 | ||||||
|  | Toggle treating linebreaks as enter key press\&. | ||||||
|  | .RE | ||||||
|  | .RE | ||||||
|  | .SH "FILES" | ||||||
|  | .PP | ||||||
|  | ~/\&.config/tox/toxic\&.conf | ||||||
|  | .RS 4 | ||||||
|  | Main configuration file\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | __DATADIR__/toxic\&.conf\&.example | ||||||
|  | .RS 4 | ||||||
|  | Configuration example\&. | ||||||
|  | .RE | ||||||
|  | .SH "SEE ALSO" | ||||||
|  | .sp | ||||||
|  | \fBtoxic\fR(1) | ||||||
|  | .SH "RESOURCES" | ||||||
|  | .sp | ||||||
|  | Project page: https://github\&.com/JFreegman/toxic | ||||||
|  | .sp | ||||||
|  | IRC channel: chat\&.freenode\&.net#tox | ||||||
|  | .SH "AUTHORS" | ||||||
|  | .sp | ||||||
|  | JFreegman <JFreegman@gmail\&.com> | ||||||
							
								
								
									
										254
									
								
								doc/toxic.conf.5.asc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								doc/toxic.conf.5.asc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,254 @@ | |||||||
|  | toxic.conf(5) | ||||||
|  | ============= | ||||||
|  |  | ||||||
|  | NAME | ||||||
|  | ---- | ||||||
|  | toxic.conf - Configuration file for toxic | ||||||
|  |  | ||||||
|  |  | ||||||
|  | SYNOPSIS | ||||||
|  | -------- | ||||||
|  | ~/.config/tox/toxic.conf | ||||||
|  |  | ||||||
|  |  | ||||||
|  | DESCRIPTION | ||||||
|  | ----------- | ||||||
|  | The 'toxic.conf' file is the main configuration file for *toxic*(1) client. | ||||||
|  | It uses syntax accepted by *libconfig*. | ||||||
|  | Lines starting with "//" are comments and will be ignored. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | EXAMPLE | ||||||
|  | ------- | ||||||
|  | ---- | ||||||
|  | // Configuration for interface | ||||||
|  | ui = { | ||||||
|  |     timestamps = true; | ||||||
|  |     alerts = false; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Configuration for audio | ||||||
|  | audio = { | ||||||
|  |     input_device = 1; | ||||||
|  | }; | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  |  | ||||||
|  | OPTIONS | ||||||
|  | ------- | ||||||
|  | *ui*:: | ||||||
|  |     Configuration related to interface elements. | ||||||
|  |  | ||||||
|  |     *timestamps*;; | ||||||
|  |         Enable or disable timestamps. true or false | ||||||
|  |  | ||||||
|  |     *time_format*;; | ||||||
|  | 	Select between 24 and 12 hour time. Specify 24 or 12. Setting | ||||||
|  |         timestamp_format and log_timestamp_format will override this setting. | ||||||
|  |  | ||||||
|  |     *timestamp_format*;; | ||||||
|  |         Time format string for the interface enclosed by double quotes. | ||||||
|  |         See *date*(1) | ||||||
|  |  | ||||||
|  |     *log_timestamp_format*;; | ||||||
|  |         Time format string for logging enclosed by double quotes. | ||||||
|  |         See *date*(1) | ||||||
|  |  | ||||||
|  |     *alerts*;; | ||||||
|  |         Enable or disable acoustic alerts on events. true or false | ||||||
|  |  | ||||||
|  |     *native_colors*;; | ||||||
|  |         Select between native terminal colors and toxic color theme. true or false | ||||||
|  |  | ||||||
|  |     *autolog*;; | ||||||
|  |         Enable or disable autologging. true or false | ||||||
|  |  | ||||||
|  |     *show_typing_other*;; | ||||||
|  |         Show when others are typing in a 1-on-1 chat. true or false | ||||||
|  |  | ||||||
|  |     *show_typing_self*;; | ||||||
|  |         Show others when you're typing in a 1-on-1 chat. true or false | ||||||
|  |  | ||||||
|  |     *show_welcome_msg*;; | ||||||
|  |         Show welcome message on startup. true or false | ||||||
|  |  | ||||||
|  |     *show_connection_msg*;; | ||||||
|  |         Enable friend connection change notifications. true or false | ||||||
|  |  | ||||||
|  |     *nodelist_update_freq*;; | ||||||
|  |         How often in days to update the DHT nodes list. (0 to disable updates) | ||||||
|  |  | ||||||
|  |     *history_size*;; | ||||||
|  |         Maximum lines for chat window history. Integer value. (for example: 700) | ||||||
|  |  | ||||||
|  |     *line_join*;; | ||||||
|  |         Indicator for when someone connects or joins a group. | ||||||
|  |         Three characters max for line_ settings. | ||||||
|  |  | ||||||
|  |     *line_quit*;; | ||||||
|  |         Indicator for when someone disconnects or leaves a group. | ||||||
|  |  | ||||||
|  |     *line_alert*;; | ||||||
|  |         Indicator for alert messages. | ||||||
|  |  | ||||||
|  |     *line_normal*;; | ||||||
|  |         Indicator for normal messages. | ||||||
|  |  | ||||||
|  |     *line_special*;; | ||||||
|  |         Indicator for special messages. | ||||||
|  |  | ||||||
|  |     *mplex_away*;; | ||||||
|  |         Set user status when attaching and detaching from GNU screen or tmux. | ||||||
|  |         true or false | ||||||
|  |  | ||||||
|  |     *mplex_away_note*;; | ||||||
|  |         Status message to set when status is set to away due to screen/tmux | ||||||
|  |         detach. When attaching, the status message is set back to the original | ||||||
|  |         value. | ||||||
|  |  | ||||||
|  |     The following options control whether to output a terminal bell on certain events. | ||||||
|  |     Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window. | ||||||
|  |     These options don't affect the "alerts" option. | ||||||
|  |  | ||||||
|  |     *bell_on_message*;; | ||||||
|  |         Enable/Disable the terminal bell when receiving a message. true or false | ||||||
|  |  | ||||||
|  |     *bell_on_filetrans*;; | ||||||
|  |         Enable/Disable the terminal bell when receiving a filetransfer. true or false | ||||||
|  |  | ||||||
|  |     *bell_on_filetrans_accept*;; | ||||||
|  |         Enable/Disable the terminal bell when a filetransfer was accepted. true or false | ||||||
|  |  | ||||||
|  |     *bell_on_invite*;; | ||||||
|  |         Enable/Disable the terminal bell when receiving a group/call invite. true or false | ||||||
|  |  | ||||||
|  |  | ||||||
|  | *audio*:: | ||||||
|  |     Configuration related to audio devices. | ||||||
|  |  | ||||||
|  |     *input_device*;; | ||||||
|  |         Audio input device. Integer value. Number corresponds to `/lsdev in` | ||||||
|  |  | ||||||
|  |     *output_device*;; | ||||||
|  |         Audio output device. Integer value. Number corresponds to `/lsdev out` | ||||||
|  |  | ||||||
|  |     *VAD_treshold*;; | ||||||
|  |         Voice Activity Detection treshold.  Float value. Recommended values are | ||||||
|  |         around 40.0 | ||||||
|  |  | ||||||
|  | *tox*:: | ||||||
|  |     Configuration related to paths. | ||||||
|  |  | ||||||
|  |     *download_path*;; | ||||||
|  |         Default path for downloads. String value. Absolute path for downloaded | ||||||
|  |         files. | ||||||
|  |  | ||||||
|  |     *avatar_path*;; | ||||||
|  |         Path for your avatar (file must be a .png and cannot exceed 16.3 KiB) | ||||||
|  |  | ||||||
|  |     *chatlogs_path*;; | ||||||
|  |         Default path for chatlogs. String value. Absolute path for chatlog files. | ||||||
|  |  | ||||||
|  |     *password_eval*;; | ||||||
|  |         Replace password prompt by running this command and using its output as | ||||||
|  | 	the password. | ||||||
|  |  | ||||||
|  | *sounds*:: | ||||||
|  |     Configuration related to notification sounds. | ||||||
|  |     Special value "silent" can be used to disable a specific notification. + | ||||||
|  |     Each value is a string which corresponds to the absolute path of a wav | ||||||
|  |     sound file. | ||||||
|  |  | ||||||
|  |     *notif_error*;; | ||||||
|  |         Sound to play when an error occurs. | ||||||
|  |  | ||||||
|  |     *self_log_in*;; | ||||||
|  |         Sound to play when you log in. | ||||||
|  |  | ||||||
|  |     *self_log_out*;; | ||||||
|  |         Sound to play when you log out. | ||||||
|  |  | ||||||
|  |     *user_log_in*;; | ||||||
|  |         Sound to play when a contact become online. | ||||||
|  |  | ||||||
|  |     *user_log_out*;; | ||||||
|  |         Sound to play when a contact become offline. | ||||||
|  |  | ||||||
|  |     *call_incoming*;; | ||||||
|  |         Sound to play when you receive an incoming call. | ||||||
|  |  | ||||||
|  |     *call_outgoing*;; | ||||||
|  |         Sound to play when you start a call. | ||||||
|  |  | ||||||
|  |     *generic_message*;; | ||||||
|  |         Sound to play when an event occurs. | ||||||
|  |  | ||||||
|  |     *transfer_pending*;; | ||||||
|  |         Sound to play when you receive a file transfer request. | ||||||
|  |  | ||||||
|  |     *transfer_completed*;; | ||||||
|  |         Sound to play when a file transfer is completed. | ||||||
|  |  | ||||||
|  | *keys*:: | ||||||
|  |     Configuration related to user interface interaction. | ||||||
|  |     Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN. + | ||||||
|  |     Each value is a string which corresponds to a key combination. | ||||||
|  |  | ||||||
|  |     *next_tab*;; | ||||||
|  |         Key combination to switch next tab. | ||||||
|  |  | ||||||
|  |     *prev_tab*;; | ||||||
|  |         Key combination to switch previous tab. | ||||||
|  |  | ||||||
|  |     *scroll_line_up*;; | ||||||
|  |         Key combination to scroll one line up. | ||||||
|  |  | ||||||
|  |     *scroll_line_down*;; | ||||||
|  |         Key combination to scroll one line down. | ||||||
|  |  | ||||||
|  |     *half_page_up*;; | ||||||
|  |         Key combination to scroll half page up. | ||||||
|  |  | ||||||
|  |     *half_page_down*;; | ||||||
|  |         Key combination to scroll half page down. | ||||||
|  |  | ||||||
|  |     *page_bottom*;; | ||||||
|  |         Key combination to scroll to page bottom. | ||||||
|  |  | ||||||
|  |     *peer_list_up*;; | ||||||
|  |         Key combination to scroll contacts list up. | ||||||
|  |  | ||||||
|  |     *peer_list_down*;; | ||||||
|  |         Key combination to scroll contacts list down. | ||||||
|  |  | ||||||
|  |     *toggle_peerlist*;; | ||||||
|  |         Toggle the peer list on and off. | ||||||
|  |  | ||||||
|  |     *toggle_paste_mode*;; | ||||||
|  |         Toggle treating linebreaks as enter key press. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | FILES | ||||||
|  | ----- | ||||||
|  | ~/.config/tox/toxic.conf:: | ||||||
|  |     Main configuration file. | ||||||
|  |  | ||||||
|  | {datadir}/toxic.conf.example:: | ||||||
|  |     Configuration example. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | SEE ALSO | ||||||
|  | -------- | ||||||
|  | *toxic*(1) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | RESOURCES | ||||||
|  | --------- | ||||||
|  | Project page: <https://github.com/JFreegman/toxic> | ||||||
|  |  | ||||||
|  | IRC channel:  chat.freenode.net#tox | ||||||
|  |  | ||||||
|  |  | ||||||
|  | AUTHORS | ||||||
|  | ------- | ||||||
|  | JFreegman <JFreegman@gmail.com> | ||||||
| @@ -1,52 +0,0 @@ | |||||||
| --- /src/misc_tools.c |  | ||||||
| +++ /src/misc_tools.c |  | ||||||
| @@ -54,24 +54,11 @@ |  | ||||||
|      return val; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| -/* Get the current local time */ |  | ||||||
| -struct tm *get_time(void) |  | ||||||
| -{ |  | ||||||
| -    struct tm *timeinfo; |  | ||||||
| -    time_t now; |  | ||||||
| -    time(&now); |  | ||||||
| -    timeinfo = localtime(&now); |  | ||||||
| -    return timeinfo; |  | ||||||
| -} |  | ||||||
| - |  | ||||||
|  /* Prints the time to given window */ |  | ||||||
|  void print_time(WINDOW *window) |  | ||||||
|  { |  | ||||||
| -    uint8_t s[MAX_STR_SIZE]; |  | ||||||
| -    strftime(s, MAX_STR_SIZE, "[%H:%M:%S] ", get_time()); |  | ||||||
| - |  | ||||||
|      wattron(window, COLOR_PAIR(BLUE)); |  | ||||||
| -    wprintw(window, "%s", s); |  | ||||||
| +    wprintw(window, "[%d] ", (int)time(NULL)); |  | ||||||
|      wattroff(window,COLOR_PAIR(BLUE)); |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| --- /src/log.c |  | ||||||
| +++ /src/log.c |  | ||||||
| @@ -51,9 +51,7 @@ |  | ||||||
|          sprintf(&ident[2], "%02X", key[2] & 0xff); |  | ||||||
|          ident[KEY_IDENT_DIGITS*2+1] = '\0'; |  | ||||||
|      } else { |  | ||||||
| -        uint8_t s[MAX_STR_SIZE]; |  | ||||||
| -        strftime(s, MAX_STR_SIZE, "%Y-%m-%d[%H:%M:%S]", get_time()); |  | ||||||
| -        snprintf(ident, sizeof(ident), "%s", s); |  | ||||||
| +        snprintf(ident, sizeof(ident), "[%d]", (int)time(NULL)); |  | ||||||
|          path_len += strlen(ident) + 1; |  | ||||||
|      } |  | ||||||
|   |  | ||||||
| @@ -95,9 +93,7 @@ |  | ||||||
|      else |  | ||||||
|          snprintf(name_frmt, sizeof(name_frmt), "%s:", name); |  | ||||||
|   |  | ||||||
| -    uint8_t s[MAX_STR_SIZE]; |  | ||||||
| -    strftime(s, MAX_STR_SIZE, "%Y/%m/%d [%H:%M:%S]", get_time()); |  | ||||||
| -    fprintf(log->file,"%s %s %s\n", s, name_frmt, msg); |  | ||||||
| +    fprintf(log->file,"[%d]\n", (int)time(NULL), name_frmt, msg); |  | ||||||
|   |  | ||||||
|      uint64_t curtime = (uint64_t) time(NULL); |  | ||||||
|   |  | ||||||
							
								
								
									
										317
									
								
								m4/ax_pthread.m4
									
									
									
									
									
								
							
							
						
						
									
										317
									
								
								m4/ax_pthread.m4
									
									
									
									
									
								
							| @@ -1,317 +0,0 @@ | |||||||
| # =========================================================================== |  | ||||||
| #        http://www.gnu.org/software/autoconf-archive/ax_pthread.html |  | ||||||
| # =========================================================================== |  | ||||||
| # |  | ||||||
| # SYNOPSIS |  | ||||||
| # |  | ||||||
| #   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) |  | ||||||
| # |  | ||||||
| # DESCRIPTION |  | ||||||
| # |  | ||||||
| #   This macro figures out how to build C programs using POSIX threads. It |  | ||||||
| #   sets the PTHREAD_LIBS output variable to the threads library and linker |  | ||||||
| #   flags, and the PTHREAD_CFLAGS output variable to any special C compiler |  | ||||||
| #   flags that are needed. (The user can also force certain compiler |  | ||||||
| #   flags/libs to be tested by setting these environment variables.) |  | ||||||
| # |  | ||||||
| #   Also sets PTHREAD_CC to any special C compiler that is needed for |  | ||||||
| #   multi-threaded programs (defaults to the value of CC otherwise). (This |  | ||||||
| #   is necessary on AIX to use the special cc_r compiler alias.) |  | ||||||
| # |  | ||||||
| #   NOTE: You are assumed to not only compile your program with these flags, |  | ||||||
| #   but also link it with them as well. e.g. you should link with |  | ||||||
| #   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS |  | ||||||
| # |  | ||||||
| #   If you are only building threads programs, you may wish to use these |  | ||||||
| #   variables in your default LIBS, CFLAGS, and CC: |  | ||||||
| # |  | ||||||
| #     LIBS="$PTHREAD_LIBS $LIBS" |  | ||||||
| #     CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |  | ||||||
| #     CC="$PTHREAD_CC" |  | ||||||
| # |  | ||||||
| #   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant |  | ||||||
| #   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name |  | ||||||
| #   (e.g. PTHREAD_CREATE_UNDETACHED on AIX). |  | ||||||
| # |  | ||||||
| #   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the |  | ||||||
| #   PTHREAD_PRIO_INHERIT symbol is defined when compiling with |  | ||||||
| #   PTHREAD_CFLAGS. |  | ||||||
| # |  | ||||||
| #   ACTION-IF-FOUND is a list of shell commands to run if a threads library |  | ||||||
| #   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it |  | ||||||
| #   is not found. If ACTION-IF-FOUND is not specified, the default action |  | ||||||
| #   will define HAVE_PTHREAD. |  | ||||||
| # |  | ||||||
| #   Please let the authors know if this macro fails on any platform, or if |  | ||||||
| #   you have any other suggestions or comments. This macro was based on work |  | ||||||
| #   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help |  | ||||||
| #   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by |  | ||||||
| #   Alejandro Forero Cuervo to the autoconf macro repository. We are also |  | ||||||
| #   grateful for the helpful feedback of numerous users. |  | ||||||
| # |  | ||||||
| #   Updated for Autoconf 2.68 by Daniel Richard G. |  | ||||||
| # |  | ||||||
| # LICENSE |  | ||||||
| # |  | ||||||
| #   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> |  | ||||||
| #   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG> |  | ||||||
| # |  | ||||||
| #   This program is free software: you can redistribute it and/or modify it |  | ||||||
| #   under the terms of the GNU General Public License as published by the |  | ||||||
| #   Free Software Foundation, either version 3 of the License, or (at your |  | ||||||
| #   option) any later version. |  | ||||||
| # |  | ||||||
| #   This program is distributed in the hope that it will be useful, but |  | ||||||
| #   WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |  | ||||||
| #   Public License for more details. |  | ||||||
| # |  | ||||||
| #   You should have received a copy of the GNU General Public License along |  | ||||||
| #   with this program. If not, see <http://www.gnu.org/licenses/>. |  | ||||||
| # |  | ||||||
| #   As a special exception, the respective Autoconf Macro's copyright owner |  | ||||||
| #   gives unlimited permission to copy, distribute and modify the configure |  | ||||||
| #   scripts that are the output of Autoconf when processing the Macro. You |  | ||||||
| #   need not follow the terms of the GNU General Public License when using |  | ||||||
| #   or distributing such scripts, even though portions of the text of the |  | ||||||
| #   Macro appear in them. The GNU General Public License (GPL) does govern |  | ||||||
| #   all other use of the material that constitutes the Autoconf Macro. |  | ||||||
| # |  | ||||||
| #   This special exception to the GPL applies to versions of the Autoconf |  | ||||||
| #   Macro released by the Autoconf Archive. When you make and distribute a |  | ||||||
| #   modified version of the Autoconf Macro, you may extend this special |  | ||||||
| #   exception to the GPL to apply to your modified version as well. |  | ||||||
|  |  | ||||||
| #serial 20 |  | ||||||
|  |  | ||||||
| AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) |  | ||||||
| AC_DEFUN([AX_PTHREAD], [ |  | ||||||
| AC_REQUIRE([AC_CANONICAL_HOST]) |  | ||||||
| AC_LANG_PUSH([C]) |  | ||||||
| ax_pthread_ok=no |  | ||||||
|  |  | ||||||
| # We used to check for pthread.h first, but this fails if pthread.h |  | ||||||
| # requires special compiler flags (e.g. on True64 or Sequent). |  | ||||||
| # It gets checked for in the link test anyway. |  | ||||||
|  |  | ||||||
| # First of all, check if the user has set any of the PTHREAD_LIBS, |  | ||||||
| # etcetera environment variables, and if threads linking works using |  | ||||||
| # them: |  | ||||||
| if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then |  | ||||||
|         save_CFLAGS="$CFLAGS" |  | ||||||
|         CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |  | ||||||
|         save_LIBS="$LIBS" |  | ||||||
|         LIBS="$PTHREAD_LIBS $LIBS" |  | ||||||
|         AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) |  | ||||||
|         AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) |  | ||||||
|         AC_MSG_RESULT($ax_pthread_ok) |  | ||||||
|         if test x"$ax_pthread_ok" = xno; then |  | ||||||
|                 PTHREAD_LIBS="" |  | ||||||
|                 PTHREAD_CFLAGS="" |  | ||||||
|         fi |  | ||||||
|         LIBS="$save_LIBS" |  | ||||||
|         CFLAGS="$save_CFLAGS" |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| # We must check for the threads library under a number of different |  | ||||||
| # names; the ordering is very important because some systems |  | ||||||
| # (e.g. DEC) have both -lpthread and -lpthreads, where one of the |  | ||||||
| # libraries is broken (non-POSIX). |  | ||||||
|  |  | ||||||
| # Create a list of thread flags to try.  Items starting with a "-" are |  | ||||||
| # C compiler flags, and other items are library names, except for "none" |  | ||||||
| # which indicates that we try without any flags at all, and "pthread-config" |  | ||||||
| # which is a program returning the flags for the Pth emulation library. |  | ||||||
|  |  | ||||||
| ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" |  | ||||||
|  |  | ||||||
| # The ordering *is* (sometimes) important.  Some notes on the |  | ||||||
| # individual items follow: |  | ||||||
|  |  | ||||||
| # pthreads: AIX (must check this before -lpthread) |  | ||||||
| # none: in case threads are in libc; should be tried before -Kthread and |  | ||||||
| #       other compiler flags to prevent continual compiler warnings |  | ||||||
| # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) |  | ||||||
| # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) |  | ||||||
| # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) |  | ||||||
| # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) |  | ||||||
| # -pthreads: Solaris/gcc |  | ||||||
| # -mthreads: Mingw32/gcc, Lynx/gcc |  | ||||||
| # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it |  | ||||||
| #      doesn't hurt to check since this sometimes defines pthreads too; |  | ||||||
| #      also defines -D_REENTRANT) |  | ||||||
| #      ... -mt is also the pthreads flag for HP/aCC |  | ||||||
| # pthread: Linux, etcetera |  | ||||||
| # --thread-safe: KAI C++ |  | ||||||
| # pthread-config: use pthread-config program (for GNU Pth library) |  | ||||||
|  |  | ||||||
| case ${host_os} in |  | ||||||
|         solaris*) |  | ||||||
|  |  | ||||||
|         # On Solaris (at least, for some versions), libc contains stubbed |  | ||||||
|         # (non-functional) versions of the pthreads routines, so link-based |  | ||||||
|         # tests will erroneously succeed.  (We need to link with -pthreads/-mt/ |  | ||||||
|         # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather |  | ||||||
|         # a function called by this macro, so we could check for that, but |  | ||||||
|         # who knows whether they'll stub that too in a future libc.)  So, |  | ||||||
|         # we'll just look for -pthreads and -lpthread first: |  | ||||||
|  |  | ||||||
|         ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" |  | ||||||
|         ;; |  | ||||||
|  |  | ||||||
|         darwin*) |  | ||||||
|         ax_pthread_flags="-pthread $ax_pthread_flags" |  | ||||||
|         ;; |  | ||||||
| esac |  | ||||||
|  |  | ||||||
| if test x"$ax_pthread_ok" = xno; then |  | ||||||
| for flag in $ax_pthread_flags; do |  | ||||||
|  |  | ||||||
|         case $flag in |  | ||||||
|                 none) |  | ||||||
|                 AC_MSG_CHECKING([whether pthreads work without any flags]) |  | ||||||
|                 ;; |  | ||||||
|  |  | ||||||
|                 -*) |  | ||||||
|                 AC_MSG_CHECKING([whether pthreads work with $flag]) |  | ||||||
|                 PTHREAD_CFLAGS="$flag" |  | ||||||
|                 ;; |  | ||||||
|  |  | ||||||
|                 pthread-config) |  | ||||||
|                 AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) |  | ||||||
|                 if test x"$ax_pthread_config" = xno; then continue; fi |  | ||||||
|                 PTHREAD_CFLAGS="`pthread-config --cflags`" |  | ||||||
|                 PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" |  | ||||||
|                 ;; |  | ||||||
|  |  | ||||||
|                 *) |  | ||||||
|                 AC_MSG_CHECKING([for the pthreads library -l$flag]) |  | ||||||
|                 PTHREAD_LIBS="-l$flag" |  | ||||||
|                 ;; |  | ||||||
|         esac |  | ||||||
|  |  | ||||||
|         save_LIBS="$LIBS" |  | ||||||
|         save_CFLAGS="$CFLAGS" |  | ||||||
|         LIBS="$PTHREAD_LIBS $LIBS" |  | ||||||
|         CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |  | ||||||
|  |  | ||||||
|         # Check for various functions.  We must include pthread.h, |  | ||||||
|         # since some functions may be macros.  (On the Sequent, we |  | ||||||
|         # need a special flag -Kthread to make this header compile.) |  | ||||||
|         # We check for pthread_join because it is in -lpthread on IRIX |  | ||||||
|         # while pthread_create is in libc.  We check for pthread_attr_init |  | ||||||
|         # due to DEC craziness with -lpthreads.  We check for |  | ||||||
|         # pthread_cleanup_push because it is one of the few pthread |  | ||||||
|         # functions on Solaris that doesn't have a non-functional libc stub. |  | ||||||
|         # We try pthread_create on general principles. |  | ||||||
|         AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h> |  | ||||||
|                         static void routine(void *a) { a = 0; } |  | ||||||
|                         static void *start_routine(void *a) { return a; }], |  | ||||||
|                        [pthread_t th; pthread_attr_t attr; |  | ||||||
|                         pthread_create(&th, 0, start_routine, 0); |  | ||||||
|                         pthread_join(th, 0); |  | ||||||
|                         pthread_attr_init(&attr); |  | ||||||
|                         pthread_cleanup_push(routine, 0); |  | ||||||
|                         pthread_cleanup_pop(0) /* ; */])], |  | ||||||
|                 [ax_pthread_ok=yes], |  | ||||||
|                 []) |  | ||||||
|  |  | ||||||
|         LIBS="$save_LIBS" |  | ||||||
|         CFLAGS="$save_CFLAGS" |  | ||||||
|  |  | ||||||
|         AC_MSG_RESULT($ax_pthread_ok) |  | ||||||
|         if test "x$ax_pthread_ok" = xyes; then |  | ||||||
|                 break; |  | ||||||
|         fi |  | ||||||
|  |  | ||||||
|         PTHREAD_LIBS="" |  | ||||||
|         PTHREAD_CFLAGS="" |  | ||||||
| done |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| # Various other checks: |  | ||||||
| if test "x$ax_pthread_ok" = xyes; then |  | ||||||
|         save_LIBS="$LIBS" |  | ||||||
|         LIBS="$PTHREAD_LIBS $LIBS" |  | ||||||
|         save_CFLAGS="$CFLAGS" |  | ||||||
|         CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |  | ||||||
|  |  | ||||||
|         # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. |  | ||||||
|         AC_MSG_CHECKING([for joinable pthread attribute]) |  | ||||||
|         attr_name=unknown |  | ||||||
|         for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do |  | ||||||
|             AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>], |  | ||||||
|                            [int attr = $attr; return attr /* ; */])], |  | ||||||
|                 [attr_name=$attr; break], |  | ||||||
|                 []) |  | ||||||
|         done |  | ||||||
|         AC_MSG_RESULT($attr_name) |  | ||||||
|         if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then |  | ||||||
|             AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, |  | ||||||
|                                [Define to necessary symbol if this constant |  | ||||||
|                                 uses a non-standard name on your system.]) |  | ||||||
|         fi |  | ||||||
|  |  | ||||||
|         AC_MSG_CHECKING([if more special flags are required for pthreads]) |  | ||||||
|         flag=no |  | ||||||
|         case ${host_os} in |  | ||||||
|             aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; |  | ||||||
|             osf* | hpux*) flag="-D_REENTRANT";; |  | ||||||
|             solaris*) |  | ||||||
|             if test "$GCC" = "yes"; then |  | ||||||
|                 flag="-D_REENTRANT" |  | ||||||
|             else |  | ||||||
|                 flag="-mt -D_REENTRANT" |  | ||||||
|             fi |  | ||||||
|             ;; |  | ||||||
|         esac |  | ||||||
|         AC_MSG_RESULT(${flag}) |  | ||||||
|         if test "x$flag" != xno; then |  | ||||||
|             PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" |  | ||||||
|         fi |  | ||||||
|  |  | ||||||
|         AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], |  | ||||||
|             ax_cv_PTHREAD_PRIO_INHERIT, [ |  | ||||||
|                 AC_LINK_IFELSE([ |  | ||||||
|                     AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])], |  | ||||||
|                     [ax_cv_PTHREAD_PRIO_INHERIT=yes], |  | ||||||
|                     [ax_cv_PTHREAD_PRIO_INHERIT=no]) |  | ||||||
|             ]) |  | ||||||
|         AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], |  | ||||||
|             AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) |  | ||||||
|  |  | ||||||
|         LIBS="$save_LIBS" |  | ||||||
|         CFLAGS="$save_CFLAGS" |  | ||||||
|  |  | ||||||
|         # More AIX lossage: compile with *_r variant |  | ||||||
|         if test "x$GCC" != xyes; then |  | ||||||
|             case $host_os in |  | ||||||
|                 aix*) |  | ||||||
|                 AS_CASE(["x/$CC"], |  | ||||||
|                   [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], |  | ||||||
|                   [#handle absolute path differently from PATH based program lookup |  | ||||||
|                    AS_CASE(["x$CC"], |  | ||||||
|                      [x/*], |  | ||||||
|                      [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], |  | ||||||
|                      [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) |  | ||||||
|                 ;; |  | ||||||
|             esac |  | ||||||
|         fi |  | ||||||
| fi |  | ||||||
|  |  | ||||||
| test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" |  | ||||||
|  |  | ||||||
| AC_SUBST(PTHREAD_LIBS) |  | ||||||
| AC_SUBST(PTHREAD_CFLAGS) |  | ||||||
| AC_SUBST(PTHREAD_CC) |  | ||||||
|  |  | ||||||
| # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: |  | ||||||
| if test x"$ax_pthread_ok" = xyes; then |  | ||||||
|         ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) |  | ||||||
|         : |  | ||||||
| else |  | ||||||
|         ax_pthread_ok=no |  | ||||||
|         $2 |  | ||||||
| fi |  | ||||||
| AC_LANG_POP |  | ||||||
| ])dnl AX_PTHREAD |  | ||||||
							
								
								
									
										199
									
								
								m4/pkg.m4
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								m4/pkg.m4
									
									
									
									
									
								
							| @@ -1,199 +0,0 @@ | |||||||
| # pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*- |  | ||||||
| # serial 1 (pkg-config-0.24) |  | ||||||
| #  |  | ||||||
| # Copyright © 2004 Scott James Remnant <scott@netsplit.com>. |  | ||||||
| # |  | ||||||
| # This program is free software; you can redistribute it and/or modify |  | ||||||
| # it under the terms of the GNU General Public License as published by |  | ||||||
| # the Free Software Foundation; either version 2 of the License, or |  | ||||||
| # (at your option) any later version. |  | ||||||
| # |  | ||||||
| # This program is distributed in the hope that it will be useful, but |  | ||||||
| # WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU |  | ||||||
| # General Public License for more details. |  | ||||||
| # |  | ||||||
| # You should have received a copy of the GNU General Public License |  | ||||||
| # along with this program; if not, write to the Free Software |  | ||||||
| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |  | ||||||
| # |  | ||||||
| # As a special exception to the GNU General Public License, if you |  | ||||||
| # distribute this file as part of a program that contains a |  | ||||||
| # configuration script generated by Autoconf, you may include it under |  | ||||||
| # the same distribution terms that you use for the rest of that program. |  | ||||||
|  |  | ||||||
| # PKG_PROG_PKG_CONFIG([MIN-VERSION]) |  | ||||||
| # ---------------------------------- |  | ||||||
| AC_DEFUN([PKG_PROG_PKG_CONFIG], |  | ||||||
| [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) |  | ||||||
| m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) |  | ||||||
| m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) |  | ||||||
| AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) |  | ||||||
| AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) |  | ||||||
| AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) |  | ||||||
|  |  | ||||||
| if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then |  | ||||||
| 	AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) |  | ||||||
| fi |  | ||||||
| if test -n "$PKG_CONFIG"; then |  | ||||||
| 	_pkg_min_version=m4_default([$1], [0.9.0]) |  | ||||||
| 	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) |  | ||||||
| 	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then |  | ||||||
| 		AC_MSG_RESULT([yes]) |  | ||||||
| 	else |  | ||||||
| 		AC_MSG_RESULT([no]) |  | ||||||
| 		PKG_CONFIG="" |  | ||||||
| 	fi |  | ||||||
| fi[]dnl |  | ||||||
| ])# PKG_PROG_PKG_CONFIG |  | ||||||
|  |  | ||||||
| # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) |  | ||||||
| # |  | ||||||
| # Check to see whether a particular set of modules exists.  Similar |  | ||||||
| # to PKG_CHECK_MODULES(), but does not set variables or print errors. |  | ||||||
| # |  | ||||||
| # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) |  | ||||||
| # only at the first occurence in configure.ac, so if the first place |  | ||||||
| # it's called might be skipped (such as if it is within an "if", you |  | ||||||
| # have to call PKG_CHECK_EXISTS manually |  | ||||||
| # -------------------------------------------------------------- |  | ||||||
| AC_DEFUN([PKG_CHECK_EXISTS], |  | ||||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl |  | ||||||
| if test -n "$PKG_CONFIG" && \ |  | ||||||
|     AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then |  | ||||||
|   m4_default([$2], [:]) |  | ||||||
| m4_ifvaln([$3], [else |  | ||||||
|   $3])dnl |  | ||||||
| fi]) |  | ||||||
|  |  | ||||||
| # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) |  | ||||||
| # --------------------------------------------- |  | ||||||
| m4_define([_PKG_CONFIG], |  | ||||||
| [if test -n "$$1"; then |  | ||||||
|     pkg_cv_[]$1="$$1" |  | ||||||
|  elif test -n "$PKG_CONFIG"; then |  | ||||||
|     PKG_CHECK_EXISTS([$3], |  | ||||||
|                      [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` |  | ||||||
| 		      test "x$?" != "x0" && pkg_failed=yes ], |  | ||||||
| 		     [pkg_failed=yes]) |  | ||||||
|  else |  | ||||||
|     pkg_failed=untried |  | ||||||
| fi[]dnl |  | ||||||
| ])# _PKG_CONFIG |  | ||||||
|  |  | ||||||
| # _PKG_SHORT_ERRORS_SUPPORTED |  | ||||||
| # ----------------------------- |  | ||||||
| AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], |  | ||||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) |  | ||||||
| if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then |  | ||||||
|         _pkg_short_errors_supported=yes |  | ||||||
| else |  | ||||||
|         _pkg_short_errors_supported=no |  | ||||||
| fi[]dnl |  | ||||||
| ])# _PKG_SHORT_ERRORS_SUPPORTED |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], |  | ||||||
| # [ACTION-IF-NOT-FOUND]) |  | ||||||
| # |  | ||||||
| # |  | ||||||
| # Note that if there is a possibility the first call to |  | ||||||
| # PKG_CHECK_MODULES might not happen, you should be sure to include an |  | ||||||
| # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac |  | ||||||
| # |  | ||||||
| # |  | ||||||
| # -------------------------------------------------------------- |  | ||||||
| AC_DEFUN([PKG_CHECK_MODULES], |  | ||||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl |  | ||||||
| AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl |  | ||||||
| AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl |  | ||||||
|  |  | ||||||
| pkg_failed=no |  | ||||||
| AC_MSG_CHECKING([for $1]) |  | ||||||
|  |  | ||||||
| _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) |  | ||||||
| _PKG_CONFIG([$1][_LIBS], [libs], [$2]) |  | ||||||
|  |  | ||||||
| m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS |  | ||||||
| and $1[]_LIBS to avoid the need to call pkg-config. |  | ||||||
| See the pkg-config man page for more details.]) |  | ||||||
|  |  | ||||||
| if test $pkg_failed = yes; then |  | ||||||
|    	AC_MSG_RESULT([no]) |  | ||||||
|         _PKG_SHORT_ERRORS_SUPPORTED |  | ||||||
|         if test $_pkg_short_errors_supported = yes; then |  | ||||||
| 	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` |  | ||||||
|         else  |  | ||||||
| 	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` |  | ||||||
|         fi |  | ||||||
| 	# Put the nasty error message in config.log where it belongs |  | ||||||
| 	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD |  | ||||||
|  |  | ||||||
| 	m4_default([$4], [AC_MSG_ERROR( |  | ||||||
| [Package requirements ($2) were not met: |  | ||||||
|  |  | ||||||
| $$1_PKG_ERRORS |  | ||||||
|  |  | ||||||
| Consider adjusting the PKG_CONFIG_PATH environment variable if you |  | ||||||
| installed software in a non-standard prefix. |  | ||||||
|  |  | ||||||
| _PKG_TEXT])[]dnl |  | ||||||
|         ]) |  | ||||||
| elif test $pkg_failed = untried; then |  | ||||||
|      	AC_MSG_RESULT([no]) |  | ||||||
| 	m4_default([$4], [AC_MSG_FAILURE( |  | ||||||
| [The pkg-config script could not be found or is too old.  Make sure it |  | ||||||
| is in your PATH or set the PKG_CONFIG environment variable to the full |  | ||||||
| path to pkg-config. |  | ||||||
|  |  | ||||||
| _PKG_TEXT |  | ||||||
|  |  | ||||||
| To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl |  | ||||||
|         ]) |  | ||||||
| else |  | ||||||
| 	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS |  | ||||||
| 	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS |  | ||||||
|         AC_MSG_RESULT([yes]) |  | ||||||
| 	$3 |  | ||||||
| fi[]dnl |  | ||||||
| ])# PKG_CHECK_MODULES |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # PKG_INSTALLDIR(DIRECTORY) |  | ||||||
| # ------------------------- |  | ||||||
| # Substitutes the variable pkgconfigdir as the location where a module |  | ||||||
| # should install pkg-config .pc files. By default the directory is |  | ||||||
| # $libdir/pkgconfig, but the default can be changed by passing |  | ||||||
| # DIRECTORY. The user can override through the --with-pkgconfigdir |  | ||||||
| # parameter. |  | ||||||
| AC_DEFUN([PKG_INSTALLDIR], |  | ||||||
| [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) |  | ||||||
| m4_pushdef([pkg_description], |  | ||||||
|     [pkg-config installation directory @<:@]pkg_default[@:>@]) |  | ||||||
| AC_ARG_WITH([pkgconfigdir], |  | ||||||
|     [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, |  | ||||||
|     [with_pkgconfigdir=]pkg_default) |  | ||||||
| AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) |  | ||||||
| m4_popdef([pkg_default]) |  | ||||||
| m4_popdef([pkg_description]) |  | ||||||
| ]) dnl PKG_INSTALLDIR |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # PKG_NOARCH_INSTALLDIR(DIRECTORY) |  | ||||||
| # ------------------------- |  | ||||||
| # Substitutes the variable noarch_pkgconfigdir as the location where a |  | ||||||
| # module should install arch-independent pkg-config .pc files. By |  | ||||||
| # default the directory is $datadir/pkgconfig, but the default can be |  | ||||||
| # changed by passing DIRECTORY. The user can override through the |  | ||||||
| # --with-noarch-pkgconfigdir parameter. |  | ||||||
| AC_DEFUN([PKG_NOARCH_INSTALLDIR], |  | ||||||
| [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) |  | ||||||
| m4_pushdef([pkg_description], |  | ||||||
|     [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) |  | ||||||
| AC_ARG_WITH([noarch-pkgconfigdir], |  | ||||||
|     [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, |  | ||||||
|     [with_noarch_pkgconfigdir=]pkg_default) |  | ||||||
| AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) |  | ||||||
| m4_popdef([pkg_default]) |  | ||||||
| m4_popdef([pkg_description]) |  | ||||||
| ]) dnl PKG_NOARCH_INSTALLDIR |  | ||||||
| @@ -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 +0,0 @@ | |||||||
| dist_pkgdata_DATA = DHTnodes |  | ||||||
							
								
								
									
										1
									
								
								misc/nameservers
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								misc/nameservers
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25 | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| # 24 or 12 hour time |  | ||||||
| time:24; |  | ||||||
|  |  | ||||||
| # 1 to enable autologging, 0 to disable |  | ||||||
| autolog:0; |  | ||||||
|  |  | ||||||
| # 1 to disbale terminal alerts on messages, 0 to enable |  | ||||||
| disable_alerts:0; |  | ||||||
|  |  | ||||||
| # maximum lines for chat window history |  | ||||||
| history_size:700; |  | ||||||
|  |  | ||||||
| # 1 to use native terminal colours, 0 to use toxic default colour theme |  | ||||||
| colour_theme:0; |  | ||||||
|  |  | ||||||
| # preferred audio input device; numbers correspond to /lsdev in |  | ||||||
| audio_in_dev:0; |  | ||||||
|  |  | ||||||
| # preferred audio output device; numbers correspond to /lsdev out |  | ||||||
| audio_out_dev:0; |  | ||||||
|  |  | ||||||
| # preferred path for downloads |  | ||||||
| download_path:/home/USERNAME/Downloads/; |  | ||||||
							
								
								
									
										124
									
								
								misc/toxic.conf.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								misc/toxic.conf.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | // SAMPLE TOXIC CONFIGURATION | ||||||
|  | // USES LIBCONFIG-ACCEPTED SYNTAX | ||||||
|  |  | ||||||
|  | ui = { | ||||||
|  |   // true to enable timestamps, false to disable | ||||||
|  |   timestamps=true; | ||||||
|  |  | ||||||
|  |   // true to enable acoustic alerts on messages, false to disable | ||||||
|  |   alerts=true; | ||||||
|  |  | ||||||
|  |   // Output a bell when receiving a message (see manpage) | ||||||
|  |   bell_on_message=true | ||||||
|  |  | ||||||
|  |   // Output a bell when receiving a filetransfer (see manpage) | ||||||
|  |   bell_on_filetrans=true | ||||||
|  |  | ||||||
|  |   // Don't output a bell when a filetransfer was accepted (see manpage) | ||||||
|  |   bell_on_filetrans_accept=false | ||||||
|  |  | ||||||
|  |   // Output a bell when receiving a group/call invite (see manpage) | ||||||
|  |   bell_on_invite=true | ||||||
|  |  | ||||||
|  |   // true to use native terminal colours, false to use toxic default colour theme | ||||||
|  |   native_colors=false; | ||||||
|  |  | ||||||
|  |   // 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:%S"; | ||||||
|  |  | ||||||
|  |   // true to show you when others are typing a message in 1-on-1 chats | ||||||
|  |   show_typing_other=true; | ||||||
|  |  | ||||||
|  |   // true to show others when you're typing a message in 1-on-1 chats | ||||||
|  |   show_typing_self=true; | ||||||
|  |  | ||||||
|  |   // true to show the welcome message on startup | ||||||
|  |   show_welcome_msg=true; | ||||||
|  |  | ||||||
|  |   // true to show friend connection change messages on the home screen | ||||||
|  |   show_connection_msg=true; | ||||||
|  |  | ||||||
|  |   // How often in days to update the DHT nodes list. (0 to disable updates) | ||||||
|  |   nodeslist_update_freq=7; | ||||||
|  |  | ||||||
|  |   // maximum lines for chat window history | ||||||
|  |   history_size=700; | ||||||
|  |  | ||||||
|  |   // Indicator for display when someone connects or joins a group. | ||||||
|  |   line_join="-->"; | ||||||
|  |  | ||||||
|  |   // Indicator for display when someone disconnects or leaves a group. | ||||||
|  |   line_quit="<--"; | ||||||
|  |  | ||||||
|  |   // Indicator for alert messages. | ||||||
|  |   line_alert="-!-"; | ||||||
|  |  | ||||||
|  |   // Indicator for normal messages. | ||||||
|  |   line_normal="---"; | ||||||
|  |  | ||||||
|  |   // Indicator for special messages (currently only used for private group messages) | ||||||
|  |   line_special=">>>"; | ||||||
|  |  | ||||||
|  |   // true to change status based on screen/tmux attach/detach, false to disable | ||||||
|  |   mplex_away=true; | ||||||
|  |  | ||||||
|  |   // Status message to use when status set to away due to screen/tmux detach | ||||||
|  |   mplex_away_note="Away from keyboard, be back soon!" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | audio = { | ||||||
|  |   // preferred audio input device; numbers correspond to /lsdev in | ||||||
|  |   input_device=2; | ||||||
|  |  | ||||||
|  |   // preferred audio output device; numbers correspond to /lsdev out | ||||||
|  |   output_device=0; | ||||||
|  |  | ||||||
|  |   // default VAD treshold; float (recommended values are around 40) | ||||||
|  |   VAD_treshold=40.0; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | tox = { | ||||||
|  |   // Path for downloaded files | ||||||
|  |   // download_path="/home/USERNAME/Downloads/"; | ||||||
|  |  | ||||||
|  |   // Path for your avatar (file must be a .png and cannot exceed 64 KiB) | ||||||
|  |   // avatar_path="/home/USERNAME/Pictures/youravatar.png"; | ||||||
|  |  | ||||||
|  |   // Path for chatlogs | ||||||
|  |   // chatlogs_path="/home/USERNAME/toxic_chatlogs/"; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // To disable a sound set the path to "silent" | ||||||
|  | sounds = { | ||||||
|  |   error="__DATADIR__/sounds/ToxicError.wav"; | ||||||
|  |   user_log_in="__DATADIR__/sounds/ToxicContactOnline.wav"; | ||||||
|  |   user_log_out="__DATADIR__/sounds/ToxicContactOffline.wav"; | ||||||
|  |   call_incoming="__DATADIR__/sounds/ToxicIncomingCall.wav"; | ||||||
|  |   call_outgoing="__DATADIR__/sounds/ToxicOutgoingCall.wav"; | ||||||
|  |   generic_message="__DATADIR__/sounds/ToxicRecvMessage.wav"; | ||||||
|  |   transfer_pending="__DATADIR__/sounds/ToxicTransferStart.wav"; | ||||||
|  |   transfer_completed="__DATADIR__/sounds/ToxicTransferComplete.wav"; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive) | ||||||
|  | // Note: Ctrl+M does not work | ||||||
|  | keys = { | ||||||
|  |   next_tab="Ctrl+P"; | ||||||
|  |   prev_tab="Ctrl+O"; | ||||||
|  |   scroll_line_up="PAGEUP"; | ||||||
|  |   scroll_line_down="PAGEDOWN"; | ||||||
|  |   half_page_up="Ctrl+F"; | ||||||
|  |   half_page_down="Ctrl+V"; | ||||||
|  |   page_bottom="Ctrl+H"; | ||||||
|  |   peer_list_up="Ctrl+["; | ||||||
|  |   peer_list_down="Ctrl+]"; | ||||||
|  |   toggle_peerlist="Ctrl+b"; | ||||||
|  |   toggle_paste_mode="Ctrl+T"; | ||||||
|  | }; | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								misc/toxic.desktop
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								misc/toxic.desktop
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | [Desktop Entry] | ||||||
|  | Version=1.0 | ||||||
|  | Type=Application | ||||||
|  | Name=Toxic | ||||||
|  | Comment=A CLI based Tox client | ||||||
|  | TryExec=toxic | ||||||
|  | Exec=toxic | ||||||
|  | Icon=utilities-terminal | ||||||
|  | Categories=InstantMessaging;AudioVideo;Network; | ||||||
|  | Terminal=true | ||||||
|  | MimeType=x-scheme-handler/tox; | ||||||
							
								
								
									
										
											BIN
										
									
								
								sounds/ToxicContactOffline.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/ToxicContactOffline.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/ToxicContactOnline.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/ToxicContactOnline.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/ToxicError.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/ToxicError.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/ToxicIncomingCall.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/ToxicIncomingCall.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/ToxicOutgoingCall.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/ToxicOutgoingCall.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/ToxicRecvMessage.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/ToxicRecvMessage.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/ToxicTransferComplete.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/ToxicTransferComplete.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/ToxicTransferStart.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/ToxicTransferStart.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								sounds/license
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								sounds/license
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | ToxicError.wav, ToxicRecvMessage.wav, ToxicContactOffline.wav, ToxicIncomingCall.wav, ToxicTransferComplete.wav, ToxicContactOnline.wav, ToxicOutgoingCall.wav and ToxicTransferStart.wav | ||||||
|  | are licensed under the "Creative Commons Attribution 3.0 Unported". All credit attributed to Jfreegman. | ||||||
							
								
								
									
										1137
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										1137
									
								
								src/audio_call.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -20,37 +20,74 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef _audio_h | #ifndef AUDIO_CALL_H | ||||||
| #define _audio_h | #define AUDIO_CALL_H | ||||||
|  |  | ||||||
| #include <tox/toxav.h> | #include <tox/toxav.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "audio_device.h" | ||||||
| #include "windows.h" |  | ||||||
|  |  | ||||||
| #define MAX_DEVICES 32 | #define MAX_CALLS 10 | ||||||
|  |  | ||||||
| typedef enum _AudioError { | typedef enum _AudioError { | ||||||
|     NoError = 0, |     ae_None = 0, | ||||||
|     ErrorStartingCaptureDevice = 1 << 0, |     ae_StartingCaptureDevice = 1 << 0, | ||||||
|     ErrorStartingOutputDevice = 1 << 1, |     ae_StartingOutputDevice = 1 << 1, | ||||||
|     ErrorStartingCoreAudio = 1 << 2 |     ae_StartingCoreAudio = 1 << 2 | ||||||
| } AudioError; | } AudioError; | ||||||
|  |  | ||||||
| typedef enum _Devices { | #ifdef VIDEO | ||||||
|     input, | typedef enum _VideoError { | ||||||
|     output, |     ve_None = 0, | ||||||
| } _Devices; |     ve_StartingCaptureDevice = 1 << 0, | ||||||
|  |     ve_StartingOutputDevice = 1 << 1, | ||||||
|  |     ve_StartingCoreVideo = 1 << 2 | ||||||
|  | } VideoError; | ||||||
|  |  | ||||||
| /* You will have to pass pointer to first member of 'windows' | #endif /* VIDEO */ | ||||||
|  * declared in windows.c otherwise undefined behaviour will |  | ||||||
|  */ | typedef struct Call { | ||||||
| ToxAv *init_audio(ToxWindow *self, Tox *tox); |     pthread_t ttid; /* Transmission thread id */ | ||||||
|  |     bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */ | ||||||
|  |     uint32_t in_idx, out_idx; /* Audio Index */ | ||||||
|  | #ifdef VIDEO | ||||||
|  |     uint32_t vin_idx, vout_idx; /* Video Index */ | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |     pthread_mutex_t mutex; | ||||||
|  | } Call; | ||||||
|  |  | ||||||
|  | struct CallControl { | ||||||
|  |     AudioError audio_errors; | ||||||
|  | #ifdef VIDEO | ||||||
|  |     VideoError video_errors; | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  |     ToxAV *av; | ||||||
|  |     ToxWindow *prompt; | ||||||
|  |  | ||||||
|  |     Call calls[MAX_CALLS]; | ||||||
|  |     uint32_t call_state; | ||||||
|  |     bool pending_call; | ||||||
|  |     bool audio_enabled; | ||||||
|  |     bool video_enabled; | ||||||
|  |  | ||||||
|  |     uint32_t audio_bit_rate; | ||||||
|  |     int32_t audio_frame_duration; | ||||||
|  |     uint32_t audio_sample_rate; | ||||||
|  |     uint8_t audio_channels; | ||||||
|  |  | ||||||
|  |     uint32_t video_bit_rate; | ||||||
|  |     int32_t video_frame_duration; | ||||||
|  |  | ||||||
|  | } CallControl; | ||||||
|  |  | ||||||
|  | struct CallControl CallControl; | ||||||
|  |  | ||||||
|  | /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | ||||||
|  | ToxAV *init_audio(ToxWindow *self, Tox *tox); | ||||||
| void terminate_audio(); | void terminate_audio(); | ||||||
|  | int start_transmission(ToxWindow *self, Call *call); | ||||||
|  | int stop_transmission(Call *call, uint32_t friend_number); | ||||||
|  | void stop_current_call(ToxWindow *self); | ||||||
|  |  | ||||||
| int errors(); | #endif /* AUDIO_CALL_H */ | ||||||
|  |  | ||||||
| int start_transmission(ToxWindow *self); |  | ||||||
| int device_set(ToxWindow *self, _Devices type, long int selection); |  | ||||||
|  |  | ||||||
| #endif /* _audio_h */ |  | ||||||
|   | |||||||
							
								
								
									
										501
									
								
								src/audio_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										501
									
								
								src/audio_device.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,501 @@ | |||||||
|  | /*  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" | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  | #include "audio_call.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "settings.h" | ||||||
|  |  | ||||||
|  | #ifdef __APPLE__ | ||||||
|  | #include <OpenAL/al.h> | ||||||
|  | #include <OpenAL/alc.h> | ||||||
|  | #else | ||||||
|  | #include <AL/al.h> | ||||||
|  | #include <AL/alc.h> | ||||||
|  | /* compatibility with older versions of OpenAL */ | ||||||
|  | #ifndef ALC_ALL_DEVICES_SPECIFIER | ||||||
|  | #include <AL/alext.h> | ||||||
|  | #endif  /* ALC_ALL_DEVICES_SPECIFIER */ | ||||||
|  | #endif  /* __APPLE__ */ | ||||||
|  |  | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <assert.h> | ||||||
|  |  | ||||||
|  | #define inline__ inline __attribute__((always_inline)) | ||||||
|  |  | ||||||
|  | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
|  | typedef struct Device { | ||||||
|  |     ALCdevice  *dhndl;                     /* Handle of device selected/opened */ | ||||||
|  |     ALCcontext *ctx;                       /* Device context */ | ||||||
|  |     DataHandleCallback cb;                 /* Use this to handle data from input device usually */ | ||||||
|  |     void *cb_data;                         /* Data to be passed to callback */ | ||||||
|  |     int32_t friend_number;                      /* ToxAV friend number */ | ||||||
|  |  | ||||||
|  |     uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */ | ||||||
|  |     uint32_t ref_count; | ||||||
|  |     int32_t selection; | ||||||
|  |     bool enable_VAD; | ||||||
|  |     bool muted; | ||||||
|  |     pthread_mutex_t mutex[1]; | ||||||
|  |     uint32_t sample_rate; | ||||||
|  |     uint32_t frame_duration; | ||||||
|  |     int32_t sound_mode; | ||||||
|  | #ifdef AUDIO | ||||||
|  |     float VAD_treshold;                    /* 40 is usually recommended value */ | ||||||
|  | #endif | ||||||
|  | } Device; | ||||||
|  |  | ||||||
|  | const char *ddevice_names[2];              /* Default device */ | ||||||
|  | const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */ | ||||||
|  | static int size[2];                        /* Size of above containers */ | ||||||
|  | Device *running[2][MAX_DEVICES] = {{NULL}};     /* Running devices */ | ||||||
|  | uint32_t primary_device[2];          /* Primary device */ | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  | static ToxAV *av = NULL; | ||||||
|  | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  | /* q_mutex */ | ||||||
|  | #define lock pthread_mutex_lock(&mutex) | ||||||
|  | #define unlock pthread_mutex_unlock(&mutex) | ||||||
|  | pthread_mutex_t mutex; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | bool thread_running = true, | ||||||
|  |      thread_paused = true;               /* Thread control */ | ||||||
|  |  | ||||||
|  | void *thread_poll(void *); | ||||||
|  | /* Meet devices */ | ||||||
|  | #ifdef AUDIO | ||||||
|  | DeviceError init_devices(ToxAV *av_) | ||||||
|  | #else | ||||||
|  | DeviceError init_devices() | ||||||
|  | #endif /* AUDIO */ | ||||||
|  | { | ||||||
|  |     const char *stringed_device_list; | ||||||
|  |  | ||||||
|  |     size[input] = 0; | ||||||
|  |  | ||||||
|  |     if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) { | ||||||
|  |         ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); | ||||||
|  |  | ||||||
|  |         for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) { | ||||||
|  |             devices_names[input][size[input]] = stringed_device_list; | ||||||
|  |             stringed_device_list += strlen( stringed_device_list ) + 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size[output] = 0; | ||||||
|  |  | ||||||
|  |     if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) | ||||||
|  |         stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); | ||||||
|  |     else | ||||||
|  |         stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER); | ||||||
|  |  | ||||||
|  |     if (stringed_device_list) { | ||||||
|  |         ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); | ||||||
|  |  | ||||||
|  |         for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) { | ||||||
|  |             devices_names[output][size[output]] = stringed_device_list; | ||||||
|  |             stringed_device_list += strlen( stringed_device_list ) + 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Start poll thread | ||||||
|  |     if (pthread_mutex_init(&mutex, NULL) != 0) | ||||||
|  |         return de_InternalError; | ||||||
|  |  | ||||||
|  |     pthread_t thread_id; | ||||||
|  |  | ||||||
|  |     if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) | ||||||
|  |         return de_InternalError; | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  |     av = av_; | ||||||
|  | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  |     return (DeviceError) de_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DeviceError terminate_devices() | ||||||
|  | { | ||||||
|  |     /* Cleanup if needed */ | ||||||
|  |     lock; | ||||||
|  |     thread_running = false; | ||||||
|  |     unlock; | ||||||
|  |  | ||||||
|  |     usleep(20000); | ||||||
|  |  | ||||||
|  |     if (pthread_mutex_destroy(&mutex) != 0) | ||||||
|  |         return (DeviceError) de_InternalError; | ||||||
|  |  | ||||||
|  |     return (DeviceError) de_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DeviceError device_mute(DeviceType type, uint32_t device_idx) | ||||||
|  | { | ||||||
|  |     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||||
|  |  | ||||||
|  |     lock; | ||||||
|  |  | ||||||
|  |     Device *device = running[type][device_idx]; | ||||||
|  |  | ||||||
|  |     if (!device) { | ||||||
|  |         unlock; | ||||||
|  |         return de_DeviceNotActive; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     device->muted = !device->muted; | ||||||
|  |  | ||||||
|  |     unlock; | ||||||
|  |     return de_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  | DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) | ||||||
|  | { | ||||||
|  |     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||||
|  |  | ||||||
|  |     lock; | ||||||
|  |  | ||||||
|  |     Device *device = running[input][device_idx]; | ||||||
|  |  | ||||||
|  |     if (!device) { | ||||||
|  |         unlock; | ||||||
|  |         return de_DeviceNotActive; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     device->VAD_treshold = value; | ||||||
|  |  | ||||||
|  |     unlock; | ||||||
|  |     return de_None; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | DeviceError set_primary_device(DeviceType type, int32_t selection) | ||||||
|  | { | ||||||
|  |     if (size[type] <= selection || selection < 0) return de_InvalidSelection; | ||||||
|  |  | ||||||
|  |     primary_device[type] = selection; | ||||||
|  |  | ||||||
|  |     return de_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration, | ||||||
|  |                                 uint8_t channels) | ||||||
|  | { | ||||||
|  |     return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void get_primary_device_name(DeviceType type, char *buf, int size) | ||||||
|  | { | ||||||
|  |     memcpy(buf, ddevice_names[type], size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TODO: generate buffers separately | ||||||
|  | DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate, | ||||||
|  |                         uint32_t frame_duration, uint8_t channels) | ||||||
|  | { | ||||||
|  |     if (size[type] <= selection || selection < 0) return de_InvalidSelection; | ||||||
|  |  | ||||||
|  |     if (channels != 1 && channels != 2) return de_UnsupportedMode; | ||||||
|  |  | ||||||
|  |     lock; | ||||||
|  |  | ||||||
|  |     const uint32_t frame_size = (sample_rate * frame_duration / 1000); | ||||||
|  |  | ||||||
|  |     uint32_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i); | ||||||
|  |  | ||||||
|  |     if (i == MAX_DEVICES) { | ||||||
|  |         unlock; | ||||||
|  |         return de_AllDevicesBusy; | ||||||
|  |     } else *device_idx = i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */ | ||||||
|  |         if ( running[type][i] && running[type][i]->selection == selection ) { | ||||||
|  | //             printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl); | ||||||
|  |  | ||||||
|  |             running[type][*device_idx] = running[type][i]; | ||||||
|  |             running[type][i]->ref_count ++; | ||||||
|  |  | ||||||
|  |             unlock; | ||||||
|  |             return de_None; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Device *device = running[type][*device_idx] = calloc(1, sizeof(Device)); | ||||||
|  |     device->selection = selection; | ||||||
|  |  | ||||||
|  |     device->sample_rate = sample_rate; | ||||||
|  |     device->frame_duration = frame_duration; | ||||||
|  |     device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; | ||||||
|  |  | ||||||
|  |     if (pthread_mutex_init(device->mutex, NULL) != 0) { | ||||||
|  |         free(device); | ||||||
|  |         unlock; | ||||||
|  |         return de_InternalError; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (type == input) { | ||||||
|  |         device->dhndl = alcCaptureOpenDevice(devices_names[type][selection], | ||||||
|  |                                              sample_rate, device->sound_mode, frame_size * 2); | ||||||
|  | #ifdef AUDIO | ||||||
|  |         device->VAD_treshold = user_settings->VAD_treshold; | ||||||
|  | #endif | ||||||
|  |     } else { | ||||||
|  |         device->dhndl = alcOpenDevice(devices_names[type][selection]); | ||||||
|  |  | ||||||
|  |         if ( !device->dhndl ) { | ||||||
|  |             free(device); | ||||||
|  |             running[type][*device_idx] = NULL; | ||||||
|  |             unlock; | ||||||
|  |             return de_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         device->ctx = alcCreateContext(device->dhndl, NULL); | ||||||
|  |         alcMakeContextCurrent(device->ctx); | ||||||
|  |  | ||||||
|  |         alGenBuffers(OPENAL_BUFS, device->buffers); | ||||||
|  |         alGenSources((uint32_t)1, &device->source); | ||||||
|  |         alSourcei(device->source, AL_LOOPING, AL_FALSE); | ||||||
|  |  | ||||||
|  |         uint16_t zeros[frame_size]; | ||||||
|  |         memset(zeros, 0, frame_size * 2); | ||||||
|  |  | ||||||
|  |         for ( i = 0; i < OPENAL_BUFS; ++i ) { | ||||||
|  |             alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers); | ||||||
|  |         alSourcePlay(device->source); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (alcGetError(device->dhndl) != AL_NO_ERROR) { | ||||||
|  |         free(device); | ||||||
|  |         running[type][*device_idx] = NULL; | ||||||
|  |         unlock; | ||||||
|  |         return de_FailedStart; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (type == input) { | ||||||
|  |         alcCaptureStart(device->dhndl); | ||||||
|  |         thread_paused = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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]; | ||||||
|  |     DeviceError rc = de_None; | ||||||
|  |  | ||||||
|  |     if (!device) { | ||||||
|  |         unlock; | ||||||
|  |         return de_DeviceNotActive; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     running[type][device_idx] = NULL; | ||||||
|  |  | ||||||
|  |     if ( !device->ref_count ) { | ||||||
|  |         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); | ||||||
|  |  | ||||||
|  |             alcMakeContextCurrent(NULL); | ||||||
|  |  | ||||||
|  |             if ( device->ctx ) alcDestroyContext(device->ctx); | ||||||
|  |  | ||||||
|  |             if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         free(device); | ||||||
|  |     } else device->ref_count--; | ||||||
|  |  | ||||||
|  |     unlock; | ||||||
|  |     return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, | ||||||
|  |                                       void *data, bool enable_VAD) | ||||||
|  | { | ||||||
|  |     if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) | ||||||
|  |         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]->friend_number = friend_number; | ||||||
|  |     unlock; | ||||||
|  |  | ||||||
|  |     return de_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels, | ||||||
|  |                                uint32_t sample_rate) | ||||||
|  | { | ||||||
|  |     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||||
|  |  | ||||||
|  |     Device *device = running[output][device_idx]; | ||||||
|  |  | ||||||
|  |     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, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels, | ||||||
|  |                  sample_rate); | ||||||
|  |     alSourceQueueBuffers(device->source, 1, &bufid); | ||||||
|  |  | ||||||
|  |     ALint state; | ||||||
|  |     alGetSourcei(device->source, AL_SOURCE_STATE, &state); | ||||||
|  |  | ||||||
|  |     if (state != AL_PLAYING) alSourcePlay(device->source); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     pthread_mutex_unlock(device->mutex); | ||||||
|  |     return de_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void *thread_poll (void *arg) // TODO: maybe use thread for every input source | ||||||
|  | { | ||||||
|  |     /* | ||||||
|  |      * NOTE: We only need to poll input devices for data. | ||||||
|  |      */ | ||||||
|  |     (void)arg; | ||||||
|  |     uint32_t i; | ||||||
|  |     int32_t sample = 0; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     while (1) { | ||||||
|  |         lock; | ||||||
|  |  | ||||||
|  |         if (!thread_running) { | ||||||
|  |             unlock; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool paused = thread_paused; | ||||||
|  |         unlock; | ||||||
|  |  | ||||||
|  |         /* Wait for unpause. */ | ||||||
|  |         if (paused) { | ||||||
|  |             usleep(10000); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         else { | ||||||
|  |             for (i = 0; i < size[input]; ++i) { | ||||||
|  |                 lock; | ||||||
|  |  | ||||||
|  |                 if (running[input][i] != NULL) { | ||||||
|  |                     alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample); | ||||||
|  |  | ||||||
|  |                     int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000); | ||||||
|  |  | ||||||
|  |                     if (sample < f_size) { | ||||||
|  |                         unlock; | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     Device *device = running[input][i]; | ||||||
|  |  | ||||||
|  |                     int16_t frame[16000]; | ||||||
|  |                     alcCaptureSamples(device->dhndl, frame, f_size); | ||||||
|  |  | ||||||
|  |                     if (device->muted) { | ||||||
|  |                         unlock; | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if ( device->cb ) device->cb(frame, f_size, device->cb_data); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 unlock; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             usleep(5000); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pthread_exit(NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void print_devices(ToxWindow *self, DeviceType type) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < size[type]; ++i) | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										95
									
								
								src/audio_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								src/audio_device.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | |||||||
|  | /*  audio_device.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * You can have multiple sources (Input devices) but only one output device. | ||||||
|  |  * Pass buffers to output device via write(); | ||||||
|  |  * Read from running input device(s) via select()/callback combo. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef AUDIO_DEVICE_H | ||||||
|  | #define AUDIO_DEVICE_H | ||||||
|  |  | ||||||
|  | #define OPENAL_BUFS 5 | ||||||
|  | #define MAX_DEVICES 32 | ||||||
|  | #include <inttypes.h> | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
|  | typedef enum DeviceType { | ||||||
|  |     input, | ||||||
|  |     output, | ||||||
|  | } DeviceType; | ||||||
|  |  | ||||||
|  | typedef enum DeviceError { | ||||||
|  |     de_None, | ||||||
|  |     de_InternalError = -1, | ||||||
|  |     de_InvalidSelection = -2, | ||||||
|  |     de_FailedStart = -3, | ||||||
|  |     de_Busy = -4, | ||||||
|  |     de_AllDevicesBusy = -5, | ||||||
|  |     de_DeviceNotActive = -6, | ||||||
|  |     de_BufferError = -7, | ||||||
|  |     de_UnsupportedMode = -8, | ||||||
|  |     de_AlError = -9, | ||||||
|  | } DeviceError; | ||||||
|  |  | ||||||
|  | typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  | DeviceError init_devices(ToxAV *av); | ||||||
|  | #else | ||||||
|  | DeviceError init_devices(); | ||||||
|  | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  | DeviceError terminate_devices(); | ||||||
|  |  | ||||||
|  | /* Callback handles ready data from INPUT device */ | ||||||
|  | DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, | ||||||
|  |                                      void *data, bool enable_VAD); | ||||||
|  | void *get_device_callback_data(uint32_t device_idx); | ||||||
|  |  | ||||||
|  | /* toggle device mute */ | ||||||
|  | DeviceError device_mute(DeviceType type, uint32_t device_idx); | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  | DeviceError device_set_VAD_treshold(uint32_t device_idx, float value); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | DeviceError set_primary_device(DeviceType type, int32_t selection); | ||||||
|  | DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration, | ||||||
|  |                                 uint8_t channels); | ||||||
|  | /* Start device */ | ||||||
|  | DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate, | ||||||
|  |                         uint32_t frame_duration, uint8_t channels); | ||||||
|  | /* Stop device */ | ||||||
|  | DeviceError close_device(DeviceType type, uint32_t device_idx); | ||||||
|  |  | ||||||
|  | /* Write data to device */ | ||||||
|  | DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels, | ||||||
|  |                       uint32_t sample_rate); | ||||||
|  |  | ||||||
|  | void print_devices(ToxWindow *self, DeviceType type); | ||||||
|  | void get_primary_device_name(DeviceType type, char *buf, int size); | ||||||
|  |  | ||||||
|  | DeviceError selection_valid(DeviceType type, int32_t selection); | ||||||
|  | #endif /* AUDIO_DEVICE_H */ | ||||||
							
								
								
									
										309
									
								
								src/autocomplete.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								src/autocomplete.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,309 @@ | |||||||
|  | /*  autocomplete.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <limits.h> | ||||||
|  |  | ||||||
|  | #ifdef __APPLE__ | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/dir.h> | ||||||
|  | #else | ||||||
|  | #include <dirent.h> | ||||||
|  | #endif /* ifdef __APPLE__ */ | ||||||
|  |  | ||||||
|  | #include "windows.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "execute.h" | ||||||
|  | #include "configdir.h" | ||||||
|  |  | ||||||
|  | static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size) | ||||||
|  | { | ||||||
|  |     if (m) | ||||||
|  |         execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE); | ||||||
|  |  | ||||||
|  |     const char *L = (char *) list; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < n_items; ++i) | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]); | ||||||
|  |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");   /* formatting */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* puts match in match buffer. if more than one match, add first n chars that are identical. | ||||||
|  |  * e.g. if matches contains: [foo, foobar, foe] we put fo in match. | ||||||
|  |  * | ||||||
|  |  * Returns the length of the match. | ||||||
|  |  */ | ||||||
|  | static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n) | ||||||
|  | { | ||||||
|  |     if (n == 1) { | ||||||
|  |         return snprintf(match, match_sz, "%s", matches[0]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_STR_SIZE; ++i) { | ||||||
|  |         char ch1 = matches[0][i]; | ||||||
|  |         int j; | ||||||
|  |  | ||||||
|  |         for (j = 0; j < n; ++j) { | ||||||
|  |             char ch2 = matches[j][i]; | ||||||
|  |  | ||||||
|  |             if (ch1 != ch2 || !ch1) { | ||||||
|  |                 snprintf(match, match_sz, "%s", matches[0]); | ||||||
|  |                 match[i] = '\0'; | ||||||
|  |                 return i; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return snprintf(match, match_sz, "%s", matches[0]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* looks for all instances in list that begin with the last entered word in line according to pos, | ||||||
|  |    then fills line with the complete word. e.g. "Hello jo" would complete the line | ||||||
|  |    with "Hello john". If multiple matches, prints out all the matches and semi-completes line. | ||||||
|  |  | ||||||
|  |    list is a pointer to the list of strings being compared, n_items is the number of items | ||||||
|  |    in the list, and size is the size of each item in the list. | ||||||
|  |  | ||||||
|  |    Returns the difference between the old len and new len of line on success, -1 if error */ | ||||||
|  | int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     const char *L = (char *) list; | ||||||
|  |     const char *endchrs = " "; | ||||||
|  |     char ubuf[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |     /* work with multibyte string copy of buf for simplicity */ | ||||||
|  |     if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     /* TODO: generalize this */ | ||||||
|  |     bool dir_search =    !strncmp(ubuf, "/sendfile", strlen("/sendfile")) | ||||||
|  |                          || !strncmp(ubuf, "/avatar", strlen("/avatar")); | ||||||
|  |  | ||||||
|  |     /* isolate substring from space behind pos to pos */ | ||||||
|  |     char tmp[MAX_STR_SIZE]; | ||||||
|  |     snprintf(tmp, sizeof(tmp), "%s", ubuf); | ||||||
|  |     tmp[ctx->pos] = '\0'; | ||||||
|  |  | ||||||
|  |     const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' '); | ||||||
|  |     char *sub = calloc(1, strlen(ubuf) + 1); | ||||||
|  |  | ||||||
|  |     if (sub == NULL) | ||||||
|  |         exit_toxic_err("failed in complete_line", FATALERR_MEMORY); | ||||||
|  |  | ||||||
|  |     if (!s && !dir_search) { | ||||||
|  |         strcpy(sub, tmp); | ||||||
|  |  | ||||||
|  |         if (sub[0] != '/') | ||||||
|  |             endchrs = ": "; | ||||||
|  |     } else if (s) { | ||||||
|  |         strcpy(sub, &s[1]); | ||||||
|  |  | ||||||
|  |         if (dir_search) { | ||||||
|  |             int sub_len = strlen(sub); | ||||||
|  |             int si = char_rfind(sub, '/', sub_len); | ||||||
|  |  | ||||||
|  |             if (si || *sub == '/') | ||||||
|  |                 memmove(sub, &sub[si + 1], sub_len - si); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!sub[0]) { | ||||||
|  |         free(sub); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int s_len = strlen(sub); | ||||||
|  |     int n_matches = 0; | ||||||
|  |     char matches[n_items][MAX_STR_SIZE]; | ||||||
|  |     int i = 0; | ||||||
|  |  | ||||||
|  |     /* put all list matches in matches array */ | ||||||
|  |     for (i = 0; i < n_items; ++i) { | ||||||
|  |         char str[MAX_CMDNAME_SIZE + 1]; | ||||||
|  |         snprintf(str, sizeof(str), "%s", &L[i * size]); | ||||||
|  |  | ||||||
|  |         if (strncasecmp(str, sub, s_len) == 0) | ||||||
|  |             strcpy(matches[n_matches++], str); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     free(sub); | ||||||
|  |  | ||||||
|  |     if (!n_matches) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     if (!dir_search && n_matches > 1) | ||||||
|  |         print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); | ||||||
|  |  | ||||||
|  |     char match[MAX_STR_SIZE]; | ||||||
|  |     size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches); | ||||||
|  |  | ||||||
|  |     if (match_len == 0) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (dir_search) { | ||||||
|  |         if (n_matches == 1) | ||||||
|  |             endchrs = char_rfind(match, '.', match_len) ? "\"" : "/"; | ||||||
|  |         else | ||||||
|  |             endchrs = ""; | ||||||
|  |     } else if (n_matches > 1) { | ||||||
|  |         endchrs = ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* put match in correct spot in buf and append endchars */ | ||||||
|  |     int n_endchrs = strlen(endchrs); | ||||||
|  |     int strt = ctx->pos - s_len; | ||||||
|  |     int diff = match_len - s_len + n_endchrs; | ||||||
|  |  | ||||||
|  |     if (ctx->len + diff >= MAX_STR_SIZE) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     char tmpend[MAX_STR_SIZE]; | ||||||
|  |     snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]); | ||||||
|  |  | ||||||
|  |     if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     strcpy(&ubuf[strt], match); | ||||||
|  |     strcpy(&ubuf[strt + match_len], endchrs); | ||||||
|  |     strcpy(&ubuf[strt + match_len + n_endchrs], tmpend); | ||||||
|  |  | ||||||
|  |     /* convert to widechar and copy back to original buf */ | ||||||
|  |     wchar_t newbuf[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |     if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     wcscpy(ctx->line, newbuf); | ||||||
|  |  | ||||||
|  |     ctx->len += diff; | ||||||
|  |     ctx->pos += diff; | ||||||
|  |  | ||||||
|  |     return diff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* transforms a tab complete starting with the shorthand "~" into the full home directory.*/ | ||||||
|  | static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     char homedir[MAX_STR_SIZE] = {0}; | ||||||
|  |     get_home_dir(homedir, sizeof(homedir)); | ||||||
|  |  | ||||||
|  |     char newline[MAX_STR_SIZE]; | ||||||
|  |     snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1); | ||||||
|  |     snprintf(path, pathsize, "%s", &newline[cmdlen]); | ||||||
|  |  | ||||||
|  |     wchar_t wline[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |     if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     int newlen = wcslen(wline); | ||||||
|  |  | ||||||
|  |     if (ctx->len + newlen >= MAX_STR_SIZE) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     wmemcpy(ctx->line, wline, newlen + 1); | ||||||
|  |     ctx->pos = newlen; | ||||||
|  |     ctx->len = ctx->pos; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*  attempts to match /command "<incomplete-dir>" line to matching directories. | ||||||
|  |  | ||||||
|  |     if only one match, auto-complete line. | ||||||
|  |     return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ | ||||||
|  | #define MAX_DIRS 512 | ||||||
|  |  | ||||||
|  | int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||||
|  | { | ||||||
|  |     char b_path[MAX_STR_SIZE]; | ||||||
|  |     char b_name[MAX_STR_SIZE]; | ||||||
|  |     char b_cmd[MAX_STR_SIZE]; | ||||||
|  |     const wchar_t *tmpline = &line[wcslen(cmd) + 2];   /* start after "/command \"" */ | ||||||
|  |  | ||||||
|  |     if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     if (b_path[0] == '~') | ||||||
|  |         complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2); | ||||||
|  |  | ||||||
|  |     int si = char_rfind(b_path, '/', strlen(b_path)); | ||||||
|  |  | ||||||
|  |     if (!b_path[0]) {    /* list everything in pwd */ | ||||||
|  |         b_path[0] = '.'; | ||||||
|  |         b_path[1] = '\0'; | ||||||
|  |     } else if (!si && b_path[0] != '/') {    /* look for matches in pwd */ | ||||||
|  |         char tmp[MAX_STR_SIZE]; | ||||||
|  |         snprintf(tmp, sizeof(tmp), ".%s", b_path); | ||||||
|  |         snprintf(b_path, sizeof(b_path), "%s", tmp); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); | ||||||
|  |     b_path[si + 1] = '\0'; | ||||||
|  |     int b_name_len = strlen(b_name); | ||||||
|  |     DIR *dp = opendir(b_path); | ||||||
|  |  | ||||||
|  |     if (dp == NULL) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     char dirnames[MAX_DIRS][NAME_MAX]; | ||||||
|  |     struct dirent *entry; | ||||||
|  |     int dircount = 0; | ||||||
|  |  | ||||||
|  |     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { | ||||||
|  |         if (strncmp(entry->d_name, b_name, b_name_len) == 0 | ||||||
|  |                 && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { | ||||||
|  |             snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name); | ||||||
|  |             ++dircount; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     closedir(dp); | ||||||
|  |  | ||||||
|  |     if (dircount == 0) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     if (dircount > 1) { | ||||||
|  |         qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr); | ||||||
|  |         print_matches(self, m, dirnames, dircount, NAME_MAX); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return complete_line(self, dirnames, dircount, NAME_MAX); | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								src/autocomplete.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/autocomplete.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | /*  autocomplete.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef AUTOCOMPLETE_H | ||||||
|  | #define AUTOCOMPLETE_H | ||||||
|  |  | ||||||
|  | /* looks for all instances in list that begin with the last entered word in line according to pos, | ||||||
|  |    then fills line with the complete word. e.g. "Hello jo" would complete the line | ||||||
|  |    with "Hello john". If multiple matches, prints out all the matches and semi-completes line. | ||||||
|  |  | ||||||
|  |    list is a pointer to the list of strings being compared, n_items is the number of items | ||||||
|  |    in the list, and size is the size of each item in the list. | ||||||
|  |  | ||||||
|  |    Returns the difference between the old len and new len of line on success, -1 if error */ | ||||||
|  | int complete_line(ToxWindow *self, const void *list, int n_items, int size); | ||||||
|  |  | ||||||
|  | /*  attempts to match /command "<incomplete-dir>" line to matching directories. | ||||||
|  |  | ||||||
|  |     if only one match, auto-complete line. | ||||||
|  |     return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ | ||||||
|  | int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd); | ||||||
|  |  | ||||||
|  | #endif  /* #define AUTOCOMPLETE_H */ | ||||||
							
								
								
									
										207
									
								
								src/avatars.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/avatars.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | |||||||
|  | /*  avatars.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2015 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "file_transfers.h" | ||||||
|  | #include "friendlist.h" | ||||||
|  | #include "avatars.h" | ||||||
|  |  | ||||||
|  | extern FriendsList Friends; | ||||||
|  |  | ||||||
|  | static struct Avatar { | ||||||
|  |     char name[TOX_MAX_FILENAME_LENGTH + 1]; | ||||||
|  |     size_t name_len; | ||||||
|  |     char path[PATH_MAX + 1]; | ||||||
|  |     size_t path_len; | ||||||
|  |     off_t size; | ||||||
|  | } Avatar; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void avatar_clear(void) | ||||||
|  | { | ||||||
|  |     memset(&Avatar, 0, sizeof(struct Avatar)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Sends avatar to friendnum. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int avatar_send(Tox *m, uint32_t friendnum) | ||||||
|  | { | ||||||
|  |     TOX_ERR_FILE_SEND err; | ||||||
|  |     uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, | ||||||
|  |                                      NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); | ||||||
|  |  | ||||||
|  |     if (Avatar.size == 0) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     if (err != TOX_ERR_FILE_SEND_OK) { | ||||||
|  |         fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR); | ||||||
|  |  | ||||||
|  |     if (!ft) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     ft->file = fopen(Avatar.path, "r"); | ||||||
|  |  | ||||||
|  |     if (ft->file == NULL) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name); | ||||||
|  |     ft->file_size = Avatar.size; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Sends avatar to all friends */ | ||||||
|  | static void avatar_send_all(Tox *m) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < Friends.max_idx; ++i) { | ||||||
|  |         if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) | ||||||
|  |             avatar_send(m, Friends.list[i].num); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Sets avatar to path and sends it to all online contacts. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int avatar_set(Tox *m, const char *path, size_t path_len) | ||||||
|  | { | ||||||
|  |     if (path_len == 0 || path_len >= sizeof(Avatar.path)) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     FILE *fp = fopen(path, "rb"); | ||||||
|  |  | ||||||
|  |     if (fp == NULL) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; | ||||||
|  |  | ||||||
|  |     if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) { | ||||||
|  |         fclose(fp); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fclose(fp); | ||||||
|  |  | ||||||
|  |     off_t size = file_size(path); | ||||||
|  |  | ||||||
|  |     if (size == 0 || size > MAX_AVATAR_FILE_SIZE) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     get_file_name(Avatar.name, sizeof(Avatar.name), path); | ||||||
|  |     Avatar.name_len = strlen(Avatar.name); | ||||||
|  |     snprintf(Avatar.path, sizeof(Avatar.path), "%s", path); | ||||||
|  |     Avatar.path_len = path_len; | ||||||
|  |     Avatar.size = size; | ||||||
|  |  | ||||||
|  |     avatar_send_all(m); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Unsets avatar and sends to all online contacts. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | void avatar_unset(Tox *m) | ||||||
|  | { | ||||||
|  |     avatar_clear(); | ||||||
|  |     avatar_send_all(m); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control) | ||||||
|  | { | ||||||
|  |     switch (control) { | ||||||
|  |         case TOX_FILE_CONTROL_RESUME: | ||||||
|  |             if (ft->state == FILE_TRANSFER_PENDING) { | ||||||
|  |                 ft->state = FILE_TRANSFER_STARTED; | ||||||
|  |             } else if (ft->state == FILE_TRANSFER_PAUSED) { | ||||||
|  |                 ft->state = FILE_TRANSFER_STARTED; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case TOX_FILE_CONTROL_PAUSE: | ||||||
|  |             ft->state = FILE_TRANSFER_PAUSED; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case TOX_FILE_CONTROL_CANCEL: | ||||||
|  |             close_file_transfer(NULL, m, ft, -1, NULL, silent); | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length) | ||||||
|  | { | ||||||
|  |     if (ft->state != FILE_TRANSFER_STARTED) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     if (length == 0) { | ||||||
|  |         close_file_transfer(NULL, m, ft, -1, NULL, silent); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ft->file == NULL) { | ||||||
|  |         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ft->position != position) { | ||||||
|  |         if (fseek(ft->file, position, SEEK_SET) == -1) { | ||||||
|  |             close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ft->position = position; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     uint8_t send_data[length]; | ||||||
|  |     size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file); | ||||||
|  |  | ||||||
|  |     if (send_length != length) { | ||||||
|  |         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_FILE_SEND_CHUNK err; | ||||||
|  |     tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err); | ||||||
|  |  | ||||||
|  |     if (err != TOX_ERR_FILE_SEND_CHUNK_OK) | ||||||
|  |         fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err); | ||||||
|  |  | ||||||
|  |     ft->position += send_length; | ||||||
|  |     ft->last_keep_alive = get_unix_time(); | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								src/avatars.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/avatars.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | /*  avatars.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2015 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef AVATARS_H | ||||||
|  | #define AVATARS_H | ||||||
|  |  | ||||||
|  | #define MAX_AVATAR_FILE_SIZE 65536 | ||||||
|  |  | ||||||
|  | /* Sends avatar to friendnum. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int avatar_send(Tox *m, uint32_t friendnum); | ||||||
|  |  | ||||||
|  | /* Sets avatar to path and sends it to all online contacts. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int avatar_set(Tox *m, const char *path, size_t length); | ||||||
|  |  | ||||||
|  | /* Unsets avatar and sends to all online contacts. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | void avatar_unset(Tox *m); | ||||||
|  |  | ||||||
|  | void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length); | ||||||
|  | void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control); | ||||||
|  |  | ||||||
|  | #endif /* AVATARS_H */ | ||||||
							
								
								
									
										586
									
								
								src/bootstrap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										586
									
								
								src/bootstrap.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,586 @@ | |||||||
|  | /*  bootstrap.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2016 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <limits.h> | ||||||
|  |  | ||||||
|  | #include <curl/curl.h> | ||||||
|  | #include <tox/tox.h> | ||||||
|  |  | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "windows.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "configdir.h" | ||||||
|  | #include "curl_util.h" | ||||||
|  | #include "settings.h" | ||||||
|  |  | ||||||
|  | extern struct arg_opts arg_opts; | ||||||
|  | extern struct user_settings *user_settings; | ||||||
|  | extern struct Winthread Winthread; | ||||||
|  |  | ||||||
|  | /* URL that we get the JSON encoded nodes list from. */ | ||||||
|  | #define NODES_LIST_URL "https://nodes.tox.chat/json" | ||||||
|  |  | ||||||
|  | #define DEFAULT_NODES_FILENAME "DHTnodes.json" | ||||||
|  |  | ||||||
|  | /* Time to wait between bootstrap attempts */ | ||||||
|  | #define TRY_BOOTSTRAP_INTERVAL 5 | ||||||
|  |  | ||||||
|  | /* Number of nodes to bootstrap to per try */ | ||||||
|  | #define NUM_BOOTSTRAP_NODES 5 | ||||||
|  |  | ||||||
|  | /* Number of seconds since last successful ping before we consider a node offline */ | ||||||
|  | #define NODE_OFFLINE_TIMOUT (60*60*24*2) | ||||||
|  |  | ||||||
|  | #define IP_MAX_SIZE 45 | ||||||
|  | #define IP_MIN_SIZE 7 | ||||||
|  | #define PORT_MAX_SIZE 5 | ||||||
|  |  | ||||||
|  | #define LAST_SCAN_JSON_KEY "\"last_scan\":" | ||||||
|  | #define LAST_SCAN_JSON_KEY_LEN (sizeof(LAST_SCAN_JSON_KEY) - 1) | ||||||
|  |  | ||||||
|  | #define IPV4_JSON_KEY "\"ipv4\":\"" | ||||||
|  | #define IPV4_JSON_KEY_LEN (sizeof(IPV4_JSON_KEY) - 1) | ||||||
|  |  | ||||||
|  | #define IPV6_JSON_KEY "\"ipv6\":\"" | ||||||
|  | #define IPV6_JSON_KEY_LEN (sizeof(IPV6_JSON_KEY) - 1) | ||||||
|  |  | ||||||
|  | #define PORT_JSON_KEY "\"port\":" | ||||||
|  | #define PORT_JSON_KEY_LEN (sizeof(PORT_JSON_KEY) - 1) | ||||||
|  |  | ||||||
|  | #define PK_JSON_KEY "\"public_key\":\"" | ||||||
|  | #define PK_JSON_KEY_LEN (sizeof(PK_JSON_KEY) - 1) | ||||||
|  |  | ||||||
|  | #define LAST_PING_JSON_KEY "\"last_ping\":" | ||||||
|  | #define LAST_PING_JSON_KEY_LEN (sizeof(LAST_PING_JSON_KEY) - 1) | ||||||
|  |  | ||||||
|  | /* Maximum allowable size of the nodes list */ | ||||||
|  | #define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static struct Thread_Data { | ||||||
|  |     pthread_t tid; | ||||||
|  |     pthread_attr_t attr; | ||||||
|  |     pthread_mutex_t lock; | ||||||
|  |     volatile bool active; | ||||||
|  | } thread_data; | ||||||
|  |  | ||||||
|  | #define MAX_NODES 50 | ||||||
|  | struct Node { | ||||||
|  |     char ip4[IP_MAX_SIZE + 1]; | ||||||
|  |     bool have_ip4; | ||||||
|  |  | ||||||
|  |     char ip6[IP_MAX_SIZE + 1]; | ||||||
|  |     bool have_ip6; | ||||||
|  |  | ||||||
|  |     char key[TOX_PUBLIC_KEY_SIZE]; | ||||||
|  |     uint16_t port; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct DHT_Nodes { | ||||||
|  |     struct Node list[MAX_NODES]; | ||||||
|  |     size_t count; | ||||||
|  |     time_t last_updated; | ||||||
|  | } Nodes; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Determine if a node is offline by comparing the age of the nodeslist | ||||||
|  |  * to the last time the node was successfully pinged. | ||||||
|  |  */ | ||||||
|  | #define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping)) | ||||||
|  |  | ||||||
|  | /* Return true if nodeslist pointed to by fp needs to be updated. | ||||||
|  |  * This will be the case if the file is empty, has an invalid format, | ||||||
|  |  * or if the file is older than the given timeout. | ||||||
|  |  */ | ||||||
|  | static bool nodeslist_needs_update(const char *nodes_path) | ||||||
|  | { | ||||||
|  |     if (user_settings->nodeslist_update_freq <= 0) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     FILE *fp = fopen(nodes_path, "r+"); | ||||||
|  |  | ||||||
|  |     if (fp == NULL) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* last_scan value should be at beginning of file */ | ||||||
|  |     char line[LAST_SCAN_JSON_KEY_LEN + 32]; | ||||||
|  |  | ||||||
|  |     if (fgets(line, sizeof(line), fp) == NULL) { | ||||||
|  |         fclose(fp); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fclose(fp); | ||||||
|  |  | ||||||
|  |     const char *last_scan_val = strstr(line, LAST_SCAN_JSON_KEY); | ||||||
|  |  | ||||||
|  |     if (last_scan_val == NULL) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     long long int last_scan = strtoll(last_scan_val + LAST_SCAN_JSON_KEY_LEN, NULL, 10); | ||||||
|  |  | ||||||
|  |     pthread_mutex_lock(&thread_data.lock); | ||||||
|  |     Nodes.last_updated = last_scan; | ||||||
|  |     pthread_mutex_unlock(&thread_data.lock); | ||||||
|  |  | ||||||
|  |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |     bool is_timeout = timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60); | ||||||
|  |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|  |     if (is_timeout) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Fetches the JSON encoded DHT nodeslist from NODES_LIST_URL. | ||||||
|  |  * | ||||||
|  |  * Return 0 on success. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data) | ||||||
|  | { | ||||||
|  |     CURL *c_handle = curl_easy_init(); | ||||||
|  |  | ||||||
|  |     if (c_handle == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int err = -1; | ||||||
|  |  | ||||||
|  |     struct curl_slist *headers = NULL; | ||||||
|  |     headers = curl_slist_append(headers, "Content-Type: application/json"); | ||||||
|  |     headers = curl_slist_append(headers, "charsets: utf-8"); | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers); | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_URL, NODES_LIST_URL); | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data); | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data); | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L); | ||||||
|  |  | ||||||
|  |     int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type); | ||||||
|  |  | ||||||
|  |     if (proxy_ret != 0) { | ||||||
|  |         fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = curl_easy_perform(c_handle); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         /* If system doesn't support any of the specified ciphers suites, fall back to default */ | ||||||
|  |         if (ret == CURLE_SSL_CIPHER) { | ||||||
|  |             curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL); | ||||||
|  |             ret = curl_easy_perform(c_handle); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (ret != CURLE_OK) { | ||||||
|  |             fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret); | ||||||
|  |             goto on_exit; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     err = 0; | ||||||
|  |  | ||||||
|  | on_exit: | ||||||
|  |     curl_slist_free_all(headers); | ||||||
|  |     curl_easy_cleanup(c_handle); | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Attempts to update the DHT nodeslist. | ||||||
|  |  * | ||||||
|  |  * Return 1 if list was updated successfully. | ||||||
|  |  * Return 0 if list does not need to be updated. | ||||||
|  |  * Return -1 if file cannot be opened. | ||||||
|  |  * Return -2 if http lookup failed. | ||||||
|  |  * Return -3 if http reponse was empty. | ||||||
|  |  * Return -4 if data could not be written to disk. | ||||||
|  |  */ | ||||||
|  | static int update_DHT_nodeslist(const char *nodes_path) | ||||||
|  | { | ||||||
|  |     if (!nodeslist_needs_update(nodes_path)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     FILE *fp = fopen(nodes_path, "r+"); | ||||||
|  |  | ||||||
|  |     if (fp == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     struct Recv_Curl_Data recv_data; | ||||||
|  |  | ||||||
|  |     memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); | ||||||
|  |  | ||||||
|  |     if (curl_fetch_nodes_JSON(&recv_data) == -1) { | ||||||
|  |         fclose(fp); | ||||||
|  |         return -2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (recv_data.length == 0) { | ||||||
|  |         fclose(fp); | ||||||
|  |         return -3; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) { | ||||||
|  |         fclose(fp); | ||||||
|  |         return -4; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fclose(fp); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void get_nodeslist_path(char *buf, size_t buf_size) | ||||||
|  | { | ||||||
|  |     char *config_dir = NULL; | ||||||
|  |  | ||||||
|  |     if (arg_opts.nodes_path[0]) { | ||||||
|  |         snprintf(buf, buf_size, "%s", arg_opts.nodes_path); | ||||||
|  |     } else if ((config_dir = get_user_config_dir()) != NULL) { | ||||||
|  |         snprintf(buf, buf_size, "%s%s%s", config_dir, CONFIGDIR, DEFAULT_NODES_FILENAME); | ||||||
|  |         free(config_dir); | ||||||
|  |     } else { | ||||||
|  |         snprintf(buf, buf_size, "%s", DEFAULT_NODES_FILENAME); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return true if json encoded string s contains a valid IP address and puts address in ip_buf. | ||||||
|  |  * | ||||||
|  |  * ip_type should be set to 1 for ipv4 address, or 0 for ipv6 addresses. | ||||||
|  |  * ip_buf must have room for at least IP_MAX_SIZE + 1 bytes. | ||||||
|  |  */ | ||||||
|  | static bool extract_val_ip(const char *s, char *ip_buf, unsigned short int ip_type) | ||||||
|  | { | ||||||
|  |     int ip_len = char_find(0, s, '"'); | ||||||
|  |  | ||||||
|  |     if (ip_len < IP_MIN_SIZE || ip_len > IP_MAX_SIZE) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(ip_buf, s, ip_len); | ||||||
|  |     ip_buf[ip_len] = 0; | ||||||
|  |  | ||||||
|  |     return (ip_type == 1) ? is_ip4_address(ip_buf) : is_ip6_address(ip_buf); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Extracts the port from json encoded string s. | ||||||
|  |  * | ||||||
|  |  * Return port number on success. | ||||||
|  |  * Return 0 on failure. | ||||||
|  |  */ | ||||||
|  | static uint16_t extract_val_port(const char *s) | ||||||
|  | { | ||||||
|  |     long int port = strtol(s, NULL, 10); | ||||||
|  |     return (port > 0 && port <= MAX_PORT_RANGE) ? port : 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Extracts the last pinged value from json encoded string s. | ||||||
|  |  * | ||||||
|  |  * Return timestamp on success. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | static long long int extract_val_last_pinged(const char *s) | ||||||
|  | { | ||||||
|  |     long long int last_pinged = strtoll(s, NULL, 10); | ||||||
|  |     return (last_pinged <= 0) ? -1 : last_pinged; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Extracts DHT public key from json encoded string s and puts key in key_buf. | ||||||
|  |  * key_buf must have room for at least TOX_PUBLIC_KEY_SIZE * 2 + 1 bytes. | ||||||
|  |  * | ||||||
|  |  * Return number of bytes copied to key_buf on success. | ||||||
|  |  * Return -1 on failure. | ||||||
|  |  */ | ||||||
|  | static int extract_val_pk(const char *s, char *key_buf) | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     int key_len = char_find(0, s, '"'); | ||||||
|  |  | ||||||
|  |     if (key_len != TOX_PUBLIC_KEY_SIZE * 2) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(key_buf, s, key_len); | ||||||
|  |     key_buf[key_len] = 0; | ||||||
|  |  | ||||||
|  |     return key_len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Extracts values from json formatted string, validats them, and puts them in node. | ||||||
|  |  * | ||||||
|  |  * Return 0 on success. | ||||||
|  |  * Return -1 if line is empty. | ||||||
|  |  * Return -2 if line does not appear to be a valid nodes list entry. | ||||||
|  |  * Return -3 if node appears to be offline. | ||||||
|  |  * Return -4 if entry does not contain either a valid ipv4 or ipv6 address. | ||||||
|  |  * Return -5 if port value is invalid. | ||||||
|  |  * Return -6 if public key is invalid. | ||||||
|  |  */ | ||||||
|  | static int extract_node(const char *line, struct Node *node) | ||||||
|  | { | ||||||
|  |     if (!line) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *ip4_start = strstr(line, IPV4_JSON_KEY); | ||||||
|  |     const char *ip6_start = strstr(line, IPV6_JSON_KEY); | ||||||
|  |     const char *port_start = strstr(line, PORT_JSON_KEY); | ||||||
|  |     const char *key_start = strstr(line, PK_JSON_KEY); | ||||||
|  |     const char *last_pinged_str = strstr(line, LAST_PING_JSON_KEY); | ||||||
|  |  | ||||||
|  |     if (!ip4_start || !ip6_start || !port_start || !key_start || !last_pinged_str) { | ||||||
|  |         return -2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN); | ||||||
|  |  | ||||||
|  |     if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) { | ||||||
|  |         return -3; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char ip4_string[IP_MAX_SIZE + 1]; | ||||||
|  |     bool have_ip4 = extract_val_ip(ip4_start + IPV4_JSON_KEY_LEN, ip4_string, 1); | ||||||
|  |  | ||||||
|  |     char ip6_string[IP_MAX_SIZE + 1]; | ||||||
|  |     bool have_ip6 = extract_val_ip(ip6_start + IPV6_JSON_KEY_LEN, ip6_string, 0); | ||||||
|  |  | ||||||
|  |     if (!have_ip6 && !have_ip4) { | ||||||
|  |         return -4; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     uint16_t port = extract_val_port(port_start + PORT_JSON_KEY_LEN); | ||||||
|  |  | ||||||
|  |     if (port == 0) { | ||||||
|  |         return -5; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1]; | ||||||
|  |     int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string); | ||||||
|  |  | ||||||
|  |     if (key_len == -1) { | ||||||
|  |         return -6; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) { | ||||||
|  |         return -6; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (have_ip4) { | ||||||
|  |         snprintf(node->ip4, sizeof(node->ip4), "%s", ip4_string); | ||||||
|  |         node->have_ip4 = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (have_ip6) { | ||||||
|  |         snprintf(node->ip6, sizeof(node->ip6), "%s", ip6_string); | ||||||
|  |         node->have_ip6 = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     node->port = port; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Loads the DHT nodeslist to memory from json encoded nodes file. */ | ||||||
|  | void *load_nodeslist_thread(void *data) | ||||||
|  | { | ||||||
|  |     char nodes_path[PATH_MAX]; | ||||||
|  |     get_nodeslist_path(nodes_path, sizeof(nodes_path)); | ||||||
|  |  | ||||||
|  |     FILE *fp = NULL; | ||||||
|  |  | ||||||
|  |     if (!file_exists(nodes_path)) { | ||||||
|  |         if ((fp = fopen(nodes_path, "w+")) == NULL) { | ||||||
|  |             fprintf(stderr, "nodeslist load error: failed to create file '%s'\n", nodes_path); | ||||||
|  |             goto on_exit; | ||||||
|  |         } | ||||||
|  |     } else if ((fp = fopen(nodes_path, "r+")) == NULL) { | ||||||
|  |         fprintf(stderr, "nodeslist load error: failed to open file '%s'\n", nodes_path); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int update_err = update_DHT_nodeslist(nodes_path); | ||||||
|  |  | ||||||
|  |     if (update_err < 0) { | ||||||
|  |         fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char line[MAX_NODELIST_SIZE + 1]; | ||||||
|  |  | ||||||
|  |     if (fgets(line, sizeof(line), fp) == NULL) { | ||||||
|  |         fclose(fp); | ||||||
|  |         fprintf(stderr, "nodeslist load error: file empty.\n"); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t idx = 0; | ||||||
|  |     const char *line_start = line; | ||||||
|  |  | ||||||
|  |     while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY))) { | ||||||
|  |         pthread_mutex_lock(&thread_data.lock); | ||||||
|  |         idx = Nodes.count; | ||||||
|  |  | ||||||
|  |         if (idx >= MAX_NODES) { | ||||||
|  |             pthread_mutex_unlock(&thread_data.lock); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (extract_node(line_start, &Nodes.list[idx]) == 0) { | ||||||
|  |             ++Nodes.count; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         pthread_mutex_unlock(&thread_data.lock); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* If nodeslist does not contain any valid entries we set the last_scan value | ||||||
|  |      * to 0 so that it will fetch a new list the next time this function is called. | ||||||
|  |      */ | ||||||
|  |     if (idx == 0) { | ||||||
|  |         const char *s = "{\"last_scan\":0}"; | ||||||
|  |         rewind(fp); | ||||||
|  |         fwrite(s, strlen(s), 1, fp);  // Not much we can do if it fails | ||||||
|  |         fclose(fp); | ||||||
|  |         fprintf(stderr, "nodeslist load error: List did not contain any valid entries.\n"); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fclose(fp); | ||||||
|  |  | ||||||
|  | on_exit: | ||||||
|  |     thread_data.active = false; | ||||||
|  |     pthread_attr_destroy(&thread_data.attr); | ||||||
|  |     pthread_exit(0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Creates a new thread that will load the DHT nodeslist to memory | ||||||
|  |  * from json encoded nodes file obtained at NODES_LIST_URL. Only one | ||||||
|  |  * thread may run at a time. | ||||||
|  |  * | ||||||
|  |  * Return 0 on success. | ||||||
|  |  * Return -1 if a thread is already active. | ||||||
|  |  * Return -2 if mutex fails to init. | ||||||
|  |  * Return -3 if pthread attribute fails to init. | ||||||
|  |  * Return -4 if pthread fails to set detached state. | ||||||
|  |  * Return -5 if thread creation fails. | ||||||
|  |  */ | ||||||
|  | int load_DHT_nodeslist(void) | ||||||
|  | { | ||||||
|  |     if (thread_data.active) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (pthread_mutex_init(&thread_data.lock, NULL) != 0) { | ||||||
|  |         return -2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (pthread_attr_init(&thread_data.attr) != 0) { | ||||||
|  |         return -3; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED) != 0) { | ||||||
|  |         return -4; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     thread_data.active = true; | ||||||
|  |  | ||||||
|  |     if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) { | ||||||
|  |         thread_data.active = false; | ||||||
|  |         return -5; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */ | ||||||
|  | static void DHT_bootstrap(Tox *m) | ||||||
|  | { | ||||||
|  |     pthread_mutex_lock(&thread_data.lock); | ||||||
|  |     size_t num_nodes = Nodes.count; | ||||||
|  |     pthread_mutex_unlock(&thread_data.lock); | ||||||
|  |  | ||||||
|  |     if (num_nodes == 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     pthread_mutex_lock(&thread_data.lock); | ||||||
|  |  | ||||||
|  |     for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { | ||||||
|  |         struct Node *node = &Nodes.list[rand() % Nodes.count]; | ||||||
|  |         const char *addr = node->have_ip4 ? node->ip4 : node->ip6; | ||||||
|  |  | ||||||
|  |         if (!addr) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         TOX_ERR_BOOTSTRAP err; | ||||||
|  |         tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_BOOTSTRAP_OK) { | ||||||
|  |             fprintf(stderr, "Failed to bootstrap %s:%d\n", addr, node->port); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         tox_add_tcp_relay(m, addr, node->port, (uint8_t *) node->key, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_BOOTSTRAP_OK) { | ||||||
|  |             fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pthread_mutex_unlock(&thread_data.lock); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Manages connection to the Tox DHT network. */ | ||||||
|  | void do_tox_connection(Tox *m) | ||||||
|  | { | ||||||
|  |     static time_t last_bootstrap_time = 0; | ||||||
|  |     bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE; | ||||||
|  |  | ||||||
|  |     if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) { | ||||||
|  |         DHT_bootstrap(m); | ||||||
|  |         last_bootstrap_time = get_unix_time(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								src/bootstrap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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 */ | ||||||
							
								
								
									
										1300
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										1300
									
								
								src/chat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										11
									
								
								src/chat.h
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/chat.h
									
									
									
									
									
								
							| @@ -20,13 +20,16 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef CHAT_H_6489PZ13 | #ifndef CHAT_H | ||||||
| #define CHAT_H_6489PZ13 | #define CHAT_H | ||||||
|  |  | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  |  | ||||||
| void kill_chat_window(ToxWindow *self); | /* set CTRL to -1 if we don't want to send a control signal. | ||||||
|  |    set msg to NULL if we don't want to display a message */ | ||||||
|  | void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL); | ||||||
|  | void kill_chat_window(ToxWindow *self, Tox *m); | ||||||
| ToxWindow new_chat(Tox *m, int32_t friendnum); | ToxWindow new_chat(Tox *m, int32_t friendnum); | ||||||
|  |  | ||||||
| #endif /* end of include guard: CHAT_H_6489PZ13 */ | #endif /* end of include guard: CHAT_H */ | ||||||
|   | |||||||
| @@ -20,10 +20,6 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| @@ -33,252 +29,289 @@ | |||||||
| #include "friendlist.h" | #include "friendlist.h" | ||||||
| #include "execute.h" | #include "execute.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
|  | #include "groupchat.h" | ||||||
|  | #include "chat.h" | ||||||
|  | #include "file_transfers.h" | ||||||
|  |  | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
|  | extern FriendsList Friends; | ||||||
|  |  | ||||||
| extern ToxicFriend friends[MAX_FRIENDS_NUM]; | void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  |  | ||||||
| extern FileSender file_senders[MAX_FILES]; |  | ||||||
| extern uint8_t max_file_senders_index; |  | ||||||
|  |  | ||||||
| void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) |  | ||||||
| { | { | ||||||
|     struct history *hst = self->chatwin->hst; |     if (argc < 2) { | ||||||
|     line_info_clear(hst); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID."); | ||||||
|     struct line_info *start = hst->line_start; |  | ||||||
|  |  | ||||||
|     if (argc == 1) { |  | ||||||
|         if (!strcmp(argv[1], "global")) { |  | ||||||
|             execute(window, self, m, "/help", GLOBAL_COMMAND_MODE); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     char msg[MAX_STR_SIZE]; | ||||||
|  |     const char *inoutstr = argv[1]; | ||||||
|  |     long int idx = strtol(argv[2], NULL, 10); | ||||||
|  |  | ||||||
|  |     if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||||
|  |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t *msg = "Chat commands:"; |     struct FileTransfer *ft = NULL; | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); |  | ||||||
|  |  | ||||||
| #ifdef _SUPPORT_AUDIO |     /* cancel an incoming file transfer */ | ||||||
| #define NUMLINES 13 |     if (strcasecmp(inoutstr, "in") == 0) { | ||||||
| #else |         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||||
| #define NUMLINES 9 |     } else if (strcasecmp(inoutstr, "out") == 0) { | ||||||
| #endif |         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); | ||||||
|  |     } else { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     uint8_t lines[NUMLINES][MAX_STR_SIZE] = { |     if (!ft) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
| #ifdef _SUPPORT_AUDIO |     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|         { "    /call                      : Audio call"                           }, |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||||
|         { "    /cancel                    : Cancel call"                          }, |         return; | ||||||
|         { "    /answer                    : Answer incomming call"                }, |     } | ||||||
|         { "    /reject                    : Reject incoming call"                 }, |  | ||||||
|         { "    /hangup                    : Hangup active call"                   }, |  | ||||||
| #endif /* _SUPPORT_AUDIO */ |  | ||||||
|         { "    /invite <n>                : Invite friend to a group chat"        }, |  | ||||||
|         { "    /join                      : Join a pending group chat"            }, |  | ||||||
|         { "    /log <on> or <off>         : Enable/disable logging"               }, |  | ||||||
|         { "    /sendfile <filepath>       : Send a file"                          }, |  | ||||||
|         { "    /savefile <n>              : Receive a file"                       }, |  | ||||||
|         { "    /close                     : Close the current chat window"        }, |  | ||||||
|         { "    /help                      : Print this message again"             }, |  | ||||||
|         { "    /help global               : Show a list of global commands"       }, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     int i; |     snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name); | ||||||
|  |     close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent); | ||||||
|  | } | ||||||
|  |  | ||||||
|     for (i = 0; i < NUMLINES; ++i) | void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|         line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0); | { | ||||||
|  |     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     msg = " * Use Page Up/Page Down to scroll chat history\n"; |     if (Friends.list[self->num].group_invite.length == 0) { | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group invite"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     hst->line_start = start; |     const char *passwd = NULL; | ||||||
|  |     uint16_t passwd_len = 0; | ||||||
|  |  | ||||||
|  |     if (argc > 0) { | ||||||
|  |         passwd = argv[1]; | ||||||
|  |         passwd_len = strlen(passwd); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_INVITE_ACCEPT err; | ||||||
|  |     uint32_t groupnumber = tox_group_invite_accept(m, Friends.list[self->num].group_invite.data, | ||||||
|  |                            Friends.list[self->num].group_invite.length, | ||||||
|  |                            (uint8_t *) passwd, passwd_len, &err); | ||||||
|  |  | ||||||
|  |     if (err != TOX_ERR_GROUP_INVITE_ACCEPT_OK) { | ||||||
|  |         if (err == TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG) | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group: Password too long."); | ||||||
|  |         else | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group (error %d).", err); | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (init_groupchat_win(m, groupnumber, NULL, 0) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||||
|  |         tox_group_leave(m, groupnumber, NULL, 0, NULL); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |  | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         errmsg = "Invalid syntax"; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int groupnum = atoi(argv[1]); |     int groupnum = atoi(argv[1]); | ||||||
|  |  | ||||||
|  |  | ||||||
|     if (groupnum == 0 && strcmp(argv[1], "0")) {    /* atoi returns 0 value on invalid input */ |     if (groupnum == 0 && strcmp(argv[1], "0")) {    /* atoi returns 0 value on invalid input */ | ||||||
|         errmsg = "Invalid syntax."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (tox_invite_friend(m, self->num, groupnum) == -1) { |     TOX_ERR_GROUP_INVITE_FRIEND err; | ||||||
|         errmsg = "Failed to invite friend."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |     if (!tox_group_invite_friend(m, groupnum, self->num, &err)) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t msg[MAX_STR_SIZE]; |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum); | ||||||
|     snprintf(msg, sizeof(msg), "Invited friend to Room #%d.", groupnum); |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) |  | ||||||
| { |  | ||||||
|     uint8_t *errmsg; |  | ||||||
|  |  | ||||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { |  | ||||||
|         errmsg = " * Warning: Too many windows are open."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint8_t *groupkey = friends[self->num].pending_groupchat; |  | ||||||
|  |  | ||||||
|     if (groupkey[0] == '\0') { |  | ||||||
|         errmsg = "No pending group chat invite."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int groupnum = tox_join_groupchat(m, self->num, groupkey); |  | ||||||
|  |  | ||||||
|     if (groupnum == -1) { |  | ||||||
|         errmsg = "Group chat instance failed to initialize."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (init_groupchat_win(prompt, m, groupnum) == -1) { |  | ||||||
|         errmsg = "Group chat window failed to initialize."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         tox_del_groupchat(m, groupnum); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required."); | ||||||
|     if (argc != 1) { |  | ||||||
|         errmsg = "Invalid syntax."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t filenum = atoi(argv[1]); |     long int idx = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) { |     if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { | ||||||
|         errmsg = "No pending file transfers with that number."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!friends[self->num].file_receiver.pending[filenum]) { |     struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||||
|         errmsg = "No pending file transfers with that number."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |     if (!ft) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t *filename = friends[self->num].file_receiver.filenames[filenum]; |     if (ft->state != FILE_TRANSFER_PENDING) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||||
|     if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) { |         return; | ||||||
|         uint8_t msg[MAX_STR_SIZE]; |  | ||||||
|         snprintf(msg, sizeof(msg), "Saving file as: '%s' (%.1f%%)", filename, 0.0); |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |  | ||||||
|         friends[self->num].file_receiver.line_id[filenum] = self->chatwin->hst->line_end->id + 1; |  | ||||||
|  |  | ||||||
|         if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) { |  | ||||||
|             errmsg = "* Error writing to file."; |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); |  | ||||||
|             tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         errmsg = "File transfer failed."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     friends[self->num].file_receiver.pending[filenum] = false; |     if ((ft->file = fopen(ft->file_path, "a")) == NULL) { | ||||||
|  |         const char *msg =  "File transfer failed: Invalid file path."; | ||||||
|  |         close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_FILE_CONTROL err; | ||||||
|  |     tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err); | ||||||
|  |  | ||||||
|  |     if (err != TOX_ERR_FILE_CONTROL_OK) | ||||||
|  |         goto on_recv_error; | ||||||
|  |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path); | ||||||
|  |  | ||||||
|  |     /* prep progress bar line */ | ||||||
|  |     char progline[MAX_STR_SIZE]; | ||||||
|  |     init_progress_bar(progline); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||||
|  |  | ||||||
|  |     ft->line_id = self->chatwin->hst->line_end->id + 2; | ||||||
|  |     ft->state = FILE_TRANSFER_STARTED; | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  | on_recv_error: | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED: | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online."); | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_FILE_CONTROL_NOT_FOUND: | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber."); | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_FILE_CONTROL_SENDQ: | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error."); | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         default: | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err); | ||||||
|  |             return; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |     const char *errmsg = NULL; | ||||||
|  |  | ||||||
|     if (max_file_senders_index >= (MAX_FILES - 1)) { |  | ||||||
|         errmsg = "Please wait for some of your outgoing file transfers to complete."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         errmsg = "Invalid syntax."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t *path = argv[1]; |     if (argv[1][0] != '\"') { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes."); | ||||||
|     if (path[0] != '\"') { |  | ||||||
|         errmsg = "File path must be enclosed in quotes."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     path[strlen(++path) - 1] = L'\0'; |     /* remove opening and closing quotes */ | ||||||
|     int path_len = strlen(path); |     char path[MAX_STR_SIZE]; | ||||||
|  |     snprintf(path, sizeof(path), "%s", &argv[1][1]); | ||||||
|  |     int path_len = strlen(path) - 1; | ||||||
|  |     path[path_len] = '\0'; | ||||||
|  |  | ||||||
|     if (path_len > MAX_STR_SIZE) { |     if (path_len >= MAX_STR_SIZE) { | ||||||
|         errmsg = "File path exceeds character limit."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     FILE *file_to_send = fopen(path, "r"); |     FILE *file_to_send = fopen(path, "r"); | ||||||
|  |  | ||||||
|     if (file_to_send == NULL) { |     if (file_to_send == NULL) { | ||||||
|         errmsg = "File not found."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fseek(file_to_send, 0, SEEK_END); |     off_t filesize = file_size(path); | ||||||
|     uint64_t filesize = ftell(file_to_send); |  | ||||||
|     fseek(file_to_send, 0, SEEK_SET); |  | ||||||
|  |  | ||||||
|     uint8_t filename[MAX_STR_SIZE]; |     if (filesize == 0) { | ||||||
|     get_file_name(filename, path); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file."); | ||||||
|     int filenum = tox_new_file_sender(m, self->num, filesize, 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); |  | ||||||
|  |  | ||||||
|             uint8_t msg[MAX_STR_SIZE]; |     if (err != TOX_ERR_FILE_SEND_OK) | ||||||
|             snprintf(msg, sizeof(msg), "Sending file: '%s'", path); |         goto on_send_error; | ||||||
|             line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |  | ||||||
|  |  | ||||||
|             if (i == max_file_senders_index) |     struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA); | ||||||
|                 ++max_file_senders_index; |  | ||||||
|  |     if (!ft) { | ||||||
|  |         err = TOX_ERR_FILE_SEND_TOO_MANY; | ||||||
|  |         goto on_send_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(ft->file_name, file_name, namelen + 1); | ||||||
|  |     ft->file = file_to_send; | ||||||
|  |     ft->file_size = filesize; | ||||||
|  |     tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL); | ||||||
|  |  | ||||||
|  |     char sizestr[32]; | ||||||
|  |     bytes_convert_str(sizestr, sizeof(sizestr), filesize); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr); | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
|  | on_send_error: | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: | ||||||
|  |             errmsg = "File transfer failed: Invalid friend."; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED: | ||||||
|  |             errmsg = "File transfer failed: Friend is offline."; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_FILE_SEND_NAME_TOO_LONG: | ||||||
|  |             errmsg = "File transfer failed: Filename is too long."; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_FILE_SEND_TOO_MANY: | ||||||
|  |             errmsg = "File transfer failed: Too many concurrent file transfers."; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         default: | ||||||
|  |             errmsg = "File transfer failed."; | ||||||
|  |             break; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg); | ||||||
|  |     tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); | ||||||
|  |     fclose(file_to_send); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,25 +20,32 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef _chat_commands_h | #ifndef CHAT_COMMANDS_H | ||||||
| #define _chat_commands_h | #define CHAT_COMMANDS_H | ||||||
|  |  | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  |  | ||||||
| void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_groupinvite(WINDOW *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_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, 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 AUDIO | ||||||
| #ifdef _SUPPORT_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]); | ||||||
| void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| #endif /* _SUPPORT_AUDIO */ | void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #endif /* #define _chat_commands_h */ | #ifdef VIDEO | ||||||
|  | void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  | #endif /* #define CHAT_COMMANDS_H */ | ||||||
|   | |||||||
							
								
								
									
										110
									
								
								src/configdir.c
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								src/configdir.c
									
									
									
									
									
								
							| @@ -20,10 +20,6 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| @@ -33,66 +29,68 @@ | |||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <pwd.h> | #include <pwd.h> | ||||||
|  |  | ||||||
|  | #include "toxic.h" | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  |  | ||||||
|  | /* get the user's home directory. */ | ||||||
|  | void get_home_dir(char *home, int size) | ||||||
|  | { | ||||||
|  |     struct passwd pwd; | ||||||
|  |     struct passwd *pwdbuf; | ||||||
|  |     const char *hmstr; | ||||||
|  |     char buf[NSS_BUFLEN_PASSWD]; | ||||||
|  |  | ||||||
|  |     int rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); | ||||||
|  |  | ||||||
|  |     if (rc == 0) { | ||||||
|  |         hmstr = pwd.pw_dir; | ||||||
|  |     } else { | ||||||
|  |         hmstr = getenv("HOME"); | ||||||
|  |  | ||||||
|  |         if (hmstr == NULL) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         snprintf(buf, sizeof(buf), "%s", hmstr); | ||||||
|  |         hmstr = buf; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(home, size, "%s", hmstr); | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @brief Get the users config directory. |  * @brief Get the user's config directory. | ||||||
|  * |  * | ||||||
|  * This is without a trailing slash. |  * This is without a trailing slash. Resulting string must be freed. | ||||||
|  * |  * | ||||||
|  * @return The users config dir or NULL on error. |  * @return The users config dir or NULL on error. | ||||||
|  */ |  */ | ||||||
| char *get_user_config_dir(void) | char *get_user_config_dir(void) | ||||||
| { | { | ||||||
|     char *user_config_dir; |     char home[NSS_BUFLEN_PASSWD] = {0}; | ||||||
|  |     get_home_dir(home, sizeof(home)); | ||||||
|  |  | ||||||
| #ifndef NSS_BUFLEN_PASSWD |     char *user_config_dir = NULL; | ||||||
| #define NSS_BUFLEN_PASSWD 4096 |     size_t len = 0; | ||||||
| #endif /* NSS_BUFLEN_PASSWD */ |  | ||||||
|  |  | ||||||
|     struct passwd pwd; |  | ||||||
|     struct passwd *pwdbuf; |  | ||||||
|     const char *home; |  | ||||||
|     char buf[NSS_BUFLEN_PASSWD]; |  | ||||||
|     size_t len; |  | ||||||
|     int rc; |  | ||||||
|  |  | ||||||
|     rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); |  | ||||||
|  |  | ||||||
|     if (rc == 0) { |  | ||||||
|         home = pwd.pw_dir; |  | ||||||
|     } else { |  | ||||||
|         home = getenv("HOME"); |  | ||||||
|  |  | ||||||
|         if (home == NULL) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* env variables can be tainted */ |  | ||||||
|         snprintf(buf, sizeof(buf), "%s", home); |  | ||||||
|         home = buf; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| # if defined(__APPLE__) | # if defined(__APPLE__) | ||||||
|     len = strlen(home) + strlen("/Library/Application Support") + 1; |     len = strlen(home) + strlen("/Library/Application Support") + 1; | ||||||
|     user_config_dir = malloc(len); |     user_config_dir = malloc(len); | ||||||
|  |  | ||||||
|     if (user_config_dir == NULL) { |     if (user_config_dir == NULL) | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     snprintf(user_config_dir, len, "%s/Library/Application Support", home); |     snprintf(user_config_dir, len, "%s/Library/Application Support", home); | ||||||
| # else /* __APPLE__ */ | # else /* __APPLE__ */ | ||||||
|  |  | ||||||
|     const char *tmp; |     const char *tmp = getenv("XDG_CONFIG_HOME"); | ||||||
|  |  | ||||||
|     if (!(tmp = getenv("XDG_CONFIG_HOME"))) { |     if (tmp == NULL) { | ||||||
|         len = strlen(home) + strlen("/.config") + 1; |         len = strlen(home) + strlen("/.config") + 1; | ||||||
|         user_config_dir = malloc(len); |         user_config_dir = malloc(len); | ||||||
|  |  | ||||||
|         if (user_config_dir == NULL) { |         if (user_config_dir == NULL) | ||||||
|             return NULL; |             return NULL; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         snprintf(user_config_dir, len, "%s/.config", home); |         snprintf(user_config_dir, len, "%s/.config", home); | ||||||
|     } else { |     } else { | ||||||
| @@ -102,34 +100,50 @@ char *get_user_config_dir(void) | |||||||
| # endif /* __APPLE__ */ | # endif /* __APPLE__ */ | ||||||
|  |  | ||||||
|     return user_config_dir; |     return user_config_dir; | ||||||
| #undef NSS_BUFLEN_PASSWD |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* Creates the config and chatlog directories. | ||||||
|  * Creates the config directory. |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  */ |  */ | ||||||
| int create_user_config_dir(char *path) | int create_user_config_dirs(char *path) | ||||||
| { | { | ||||||
|     int mkdir_err; |  | ||||||
|  |  | ||||||
|     mkdir_err = mkdir(path, 0700); |  | ||||||
|     struct stat buf; |     struct stat buf; | ||||||
|  |     int mkdir_err = mkdir(path, 0700); | ||||||
|  |  | ||||||
|     if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) { |     if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) | ||||||
|         return -1; |         return -1; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1); |     char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1); | ||||||
|  |     char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1); | ||||||
|  |  | ||||||
|  |     if (fullpath == NULL || logpath == NULL) | ||||||
|  |         exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY); | ||||||
|  |  | ||||||
|     strcpy(fullpath, path); |     strcpy(fullpath, path); | ||||||
|     strcat(fullpath, CONFIGDIR); |     strcat(fullpath, CONFIGDIR); | ||||||
|  |  | ||||||
|  |     strcpy(logpath, path); | ||||||
|  |     strcat(logpath, LOGDIR); | ||||||
|  |  | ||||||
|     mkdir_err = mkdir(fullpath, 0700); |     mkdir_err = mkdir(fullpath, 0700); | ||||||
|  |  | ||||||
|     if (mkdir_err && (errno != EEXIST || stat(fullpath, &buf) || !S_ISDIR(buf.st_mode))) { |     if (mkdir_err && (errno != EEXIST || stat(fullpath, &buf) || !S_ISDIR(buf.st_mode))) { | ||||||
|         free(fullpath); |         free(fullpath); | ||||||
|  |         free(logpath); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     mkdir_err = mkdir(logpath, 0700); | ||||||
|  |  | ||||||
|  |     if (mkdir_err && (errno != EEXIST || stat(logpath, &buf) || !S_ISDIR(buf.st_mode))) { | ||||||
|  |         free(fullpath); | ||||||
|  |         free(logpath); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     free(logpath); | ||||||
|     free(fullpath); |     free(fullpath); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,17 +20,37 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef _configdir_h | #ifndef CONFIGDIR_H | ||||||
| #define _configdir_h | #define CONFIGDIR_H | ||||||
|  |  | ||||||
|  | #ifndef NSS_BUFLEN_PASSWD | ||||||
|  | #define NSS_BUFLEN_PASSWD 4096 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define CONFIGDIR "/tox/" | #define CONFIGDIR "/tox/" | ||||||
|  | #define LOGDIR "/tox/chatlogs/" | ||||||
|  |  | ||||||
| #ifndef S_ISDIR | #ifndef S_ISDIR | ||||||
| #define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR) | #define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Get the user's config directory. | ||||||
|  |  * | ||||||
|  |  * This is without a trailing slash. Resulting string must be freed. | ||||||
|  |  * | ||||||
|  |  * @return The users config dir or NULL on error. | ||||||
|  |  */ | ||||||
| char *get_user_config_dir(void); | char *get_user_config_dir(void); | ||||||
|  |  | ||||||
| int create_user_config_dir(char *path); | /* get the user's home directory. */ | ||||||
|  | void get_home_dir(char *home, int size); | ||||||
|  |  | ||||||
| #endif /* #define _configdir_h */ | /* Creates the config and chatlog directories. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int create_user_config_dirs(char *path); | ||||||
|  |  | ||||||
|  | #endif /* #define CONFIGDIR_H */ | ||||||
|   | |||||||
							
								
								
									
										93
									
								
								src/curl_util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/curl_util.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | /*  curl_util.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2016 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #include <curl/curl.h> | ||||||
|  | #include <tox/tox.h> | ||||||
|  |  | ||||||
|  | #include "curl_util.h" | ||||||
|  |  | ||||||
|  | /* Sets proxy info for given CURL handler. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success or if no proxy is set by the client. | ||||||
|  |  * Returns -1 if proxy info is invalid. | ||||||
|  |  * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html) | ||||||
|  |  */ | ||||||
|  | int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type) | ||||||
|  | { | ||||||
|  |     if (proxy_type == TOX_PROXY_TYPE_NONE) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     if (proxy_address == NULL || port == 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP; | ||||||
|  |  | ||||||
|  |     ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Callback function for CURL to write received data. | ||||||
|  |  * | ||||||
|  |  * This function will append data from an http request to the data buffer | ||||||
|  |  * until the request is complete or the buffer is full. Buffer will be null terminated. | ||||||
|  |  * | ||||||
|  |  * Returns number of bytes received from http request on success (don't change this). | ||||||
|  |  * Returns 0 if data exceeds buffer size. | ||||||
|  |  */ | ||||||
|  | size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer) | ||||||
|  | { | ||||||
|  |     struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer; | ||||||
|  |  | ||||||
|  |     size_t length = size * nmemb; | ||||||
|  |     size_t total_size = length + recv_data->length; | ||||||
|  |  | ||||||
|  |     if (total_size > MAX_RECV_CURL_DATA_SIZE) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(recv_data->data + recv_data->length, data, length); | ||||||
|  |     recv_data->data[total_size] = '\0'; | ||||||
|  |     recv_data->length += length; | ||||||
|  |  | ||||||
|  |     return length; | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								src/curl_util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/curl_util.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | /*  curl_util.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2016 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef CURL_UTIL_H | ||||||
|  | #define CURL_UTIL_H | ||||||
|  |  | ||||||
|  | /* List based on Mozilla's recommended configurations for modern browsers */ | ||||||
|  | #define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" | ||||||
|  |  | ||||||
|  | /* Max size of an http response that we can store in Recv_Data */ | ||||||
|  | #define MAX_RECV_CURL_DATA_SIZE 32767 | ||||||
|  |  | ||||||
|  | /* Holds data received from curl lookup */ | ||||||
|  | struct Recv_Curl_Data { | ||||||
|  |     char data[MAX_RECV_CURL_DATA_SIZE + 1];   /* Data received from curl write data callback */ | ||||||
|  |     size_t length;  /* Total number of bytes written to data buffer (doesn't include null) */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Sets proxy info for given CURL handler. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success or if no proxy is set by the client. | ||||||
|  |  * Returns -1 if proxy info is invalid. | ||||||
|  |  * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html) | ||||||
|  |  */ | ||||||
|  | int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type); | ||||||
|  |  | ||||||
|  | /* Callback function for CURL to write received data. | ||||||
|  |  * | ||||||
|  |  * This function will append data from an http request to the data buffer | ||||||
|  |  * until the request is complete or the buffer is full. Buffer will be null terminated. | ||||||
|  |  * | ||||||
|  |  * Returns size of bytes written to the data buffer. | ||||||
|  |  */ | ||||||
|  | size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer); | ||||||
|  |  | ||||||
|  | #endif  /* CURL_UTIL_H */ | ||||||
							
								
								
									
										304
									
								
								src/dns.c
									
									
									
									
									
								
							
							
						
						
									
										304
									
								
								src/dns.c
									
									
									
									
									
								
							| @@ -1,304 +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/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <netinet/in.h> |  | ||||||
| #include <arpa/nameser.h> |  | ||||||
| #include <resolv.h> |  | ||||||
|  |  | ||||||
| #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 |  | ||||||
|  |  | ||||||
| /* TODO: process keys from key file instead of hard-coding like a noob */ |  | ||||||
| static struct dns3_server { |  | ||||||
|     uint8_t *name; |  | ||||||
|     uint8_t 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; |  | ||||||
|     uint8_t id_bin[TOX_FRIEND_ADDRESS_SIZE]; |  | ||||||
|     uint8_t addr[MAX_STR_SIZE]; |  | ||||||
|     uint8_t msg[MAX_STR_SIZE]; |  | ||||||
|     uint8_t busy; |  | ||||||
|     Tox *m; |  | ||||||
| } t_data; |  | ||||||
|  |  | ||||||
| static struct _dns_thread { |  | ||||||
|     pthread_t tid; |  | ||||||
|     pthread_mutex_t lock; |  | ||||||
| } dns_thread; |  | ||||||
|  |  | ||||||
| static int dns_error(ToxWindow *self, uint8_t *errmsg) |  | ||||||
| { |  | ||||||
|     uint8_t msg[MAX_STR_SIZE]; |  | ||||||
|     snprintf(msg, sizeof(msg), "DNS lookup failed: %s", errmsg); |  | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&dns_thread.lock); |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |  | ||||||
|     pthread_mutex_unlock(&dns_thread.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_exit(0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* 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, uint8_t *buf) |  | ||||||
| { |  | ||||||
|     uint8_t *ans_pt = answer + sizeof(HEADER); |  | ||||||
|     uint8_t *ans_end = answer + ans_len; |  | ||||||
|     uint8_t 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, "Reply was too short.");  |  | ||||||
|  |  | ||||||
|     int type; |  | ||||||
|     GETSHORT(type, ans_pt); |  | ||||||
|  |  | ||||||
|     if (type != T_TXT) |  | ||||||
|         return dns_error(self, "Broken 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, "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, "Not a TXT record.");  |  | ||||||
|  |  | ||||||
|     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(uint8_t *addr, uint8_t *namebuf, uint8_t *dombuf) |  | ||||||
| { |  | ||||||
|     uint8_t tmpaddr[MAX_STR_SIZE]; |  | ||||||
|     uint8_t *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; |  | ||||||
|  |  | ||||||
|     uint8_t domain[MAX_STR_SIZE]; |  | ||||||
|     uint8_t 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 */ |  | ||||||
|     uint8_t *DNS_pubkey, *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(DNS_pubkey); |  | ||||||
|  |  | ||||||
|     if (dns_obj == NULL) { |  | ||||||
|         dns_error(self, "Core failed to create DNS object."); |  | ||||||
|         kill_dns_thread(NULL); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint8_t string[MAX_DNS_REQST_SIZE]; |  | ||||||
|     uint32_t request_id; |  | ||||||
|  |  | ||||||
|     int str_len = tox_generate_dns3_string(dns_obj, string, sizeof(string), &request_id, 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]; |  | ||||||
|     uint8_t 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, "Query failed."); |  | ||||||
|         kill_dns_thread(dns_obj); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint8_t 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); |  | ||||||
|  |  | ||||||
|     uint8_t 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, t_data.id_bin, encrypted_id, strlen(encrypted_id), request_id) == -1) { |  | ||||||
|         dns_error(self, "Core failed to decrypt response."); |  | ||||||
|         kill_dns_thread(dns_obj); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&dns_thread.lock); |  | ||||||
|     cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg); |  | ||||||
|     pthread_mutex_unlock(&dns_thread.lock); |  | ||||||
|  |  | ||||||
|     kill_dns_thread(dns_obj); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* creates new thread for dns3 lookup. Only allows one lookup at a time. */ |  | ||||||
| void dns3_lookup(ToxWindow *self, Tox *m, uint8_t *id_bin, uint8_t *addr, uint8_t *msg) |  | ||||||
| { |  | ||||||
|     if (t_data.busy) { |  | ||||||
|         uint8_t *err = "Please wait for previous user lookup to finish."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, err, SYS_MSG, 0, 0); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     t_data.self = self; |  | ||||||
|     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.m = m; |  | ||||||
|     t_data.busy = 1; |  | ||||||
|  |  | ||||||
|     if (pthread_create(&dns_thread.tid, NULL, dns3_lookup_thread, NULL) != 0) |  | ||||||
|         exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_CREATE); |  | ||||||
|  |  | ||||||
|     if (pthread_mutex_init(&dns_thread.lock, NULL) != 0) |  | ||||||
|         exit_toxic_err("failed in dns3_lookup", FATALERR_MUTEX_INIT); |  | ||||||
| } |  | ||||||
							
								
								
									
										218
									
								
								src/execute.c
									
									
									
									
									
								
							
							
						
						
									
										218
									
								
								src/execute.c
									
									
									
									
									
								
							| @@ -20,10 +20,6 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
| @@ -33,7 +29,12 @@ | |||||||
| #include "execute.h" | #include "execute.h" | ||||||
| #include "chat_commands.h" | #include "chat_commands.h" | ||||||
| #include "global_commands.h" | #include "global_commands.h" | ||||||
|  | #include "group_commands.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "notify.h" | ||||||
|  |  | ||||||
|  | #define MAX_NUM_ARGS 10     /* Includes command */ | ||||||
|  |  | ||||||
| struct cmd_func { | struct cmd_func { | ||||||
|     const char *name; |     const char *name; | ||||||
| @@ -43,100 +44,221 @@ struct cmd_func { | |||||||
| static struct cmd_func global_commands[] = { | static struct cmd_func global_commands[] = { | ||||||
|     { "/accept",    cmd_accept        }, |     { "/accept",    cmd_accept        }, | ||||||
|     { "/add",       cmd_add           }, |     { "/add",       cmd_add           }, | ||||||
|  |     { "/avatar",    cmd_avatar        }, | ||||||
|     { "/clear",     cmd_clear         }, |     { "/clear",     cmd_clear         }, | ||||||
|     { "/connect",   cmd_connect       }, |     { "/connect",   cmd_connect       }, | ||||||
|  |     { "/decline",   cmd_decline       }, | ||||||
|     { "/exit",      cmd_quit          }, |     { "/exit",      cmd_quit          }, | ||||||
|     { "/groupchat", cmd_groupchat     }, |     { "/group",     cmd_groupchat     }, | ||||||
|     { "/help",      cmd_prompt_help   }, |     { "/help",      cmd_prompt_help   }, | ||||||
|  |     { "/join",      cmd_join          }, | ||||||
|     { "/log",       cmd_log           }, |     { "/log",       cmd_log           }, | ||||||
|     { "/myid",      cmd_myid          }, |     { "/myid",      cmd_myid          }, | ||||||
|  |     { "/myqr",      cmd_myqr          }, | ||||||
|     { "/nick",      cmd_nick          }, |     { "/nick",      cmd_nick          }, | ||||||
|     { "/note",      cmd_note          }, |     { "/note",      cmd_note          }, | ||||||
|  |     { "/nospam",    cmd_nospam        }, | ||||||
|     { "/q",         cmd_quit          }, |     { "/q",         cmd_quit          }, | ||||||
|     { "/quit",      cmd_quit          }, |     { "/quit",      cmd_quit          }, | ||||||
|  |     { "/requests",  cmd_requests      }, | ||||||
|     { "/status",    cmd_status        }, |     { "/status",    cmd_status        }, | ||||||
|  | #ifdef AUDIO | ||||||
| #ifdef _SUPPORT_AUDIO |  | ||||||
|     { "/lsdev",     cmd_list_devices  }, |     { "/lsdev",     cmd_list_devices  }, | ||||||
|     { "/sdev",      cmd_change_device }, |     { "/sdev",      cmd_change_device }, | ||||||
| #endif /* _SUPPORT_AUDIO */ | #endif /* AUDIO */ | ||||||
|  | #ifdef VIDEO | ||||||
|  |     { "/lsvdev",    cmd_list_video_devices }, | ||||||
|  |     { "/svdev" ,    cmd_change_video_device }, | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |     { NULL,         NULL              }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct cmd_func chat_commands[] = { | static struct cmd_func chat_commands[] = { | ||||||
|     { "/help",      cmd_chat_help   }, |     { "/cancel",    cmd_cancelfile  }, | ||||||
|  |     { "/gaccept",   cmd_groupaccept }, | ||||||
|     { "/invite",    cmd_groupinvite }, |     { "/invite",    cmd_groupinvite }, | ||||||
|     { "/join",      cmd_join_group  }, |  | ||||||
|     { "/savefile",  cmd_savefile    }, |     { "/savefile",  cmd_savefile    }, | ||||||
|     { "/sendfile",  cmd_sendfile    }, |     { "/sendfile",  cmd_sendfile    }, | ||||||
|  | #ifdef AUDIO | ||||||
| #ifdef _SUPPORT_AUDIO |  | ||||||
|     { "/call",      cmd_call        }, |     { "/call",      cmd_call        }, | ||||||
|     { "/cancel",    cmd_cancel      }, |  | ||||||
|     { "/answer",    cmd_answer      }, |     { "/answer",    cmd_answer      }, | ||||||
|     { "/reject",    cmd_reject      }, |     { "/reject",    cmd_reject      }, | ||||||
|     { "/hangup",    cmd_hangup      }, |     { "/hangup",    cmd_hangup      }, | ||||||
| #endif /* _SUPPORT_AUDIO */ |     { "/mute",      cmd_mute        }, | ||||||
|  |     { "/sense",     cmd_sense       }, | ||||||
|  | #endif /* AUDIO */ | ||||||
|  | #ifdef VIDEO | ||||||
|  |     { "/video",     cmd_video       }, | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |     { NULL,         NULL            }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Parses input command and puts args into arg array. | static struct cmd_func group_commands[] = { | ||||||
|    Returns number of arguments on success, -1 on failure. */ |     { "/ban",       cmd_ban            }, | ||||||
| static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE]) |     { "/chatid",    cmd_chatid         }, | ||||||
|  |     { "/ignore",    cmd_ignore         }, | ||||||
|  |     { "/kick",      cmd_kick           }, | ||||||
|  |     { "/mod",       cmd_mod            }, | ||||||
|  |     { "/mykey",     cmd_mykey          }, | ||||||
|  |     { "/passwd",    cmd_set_passwd     }, | ||||||
|  |     { "/peerlimit", cmd_set_peerlimit  }, | ||||||
|  |     { "/privacy",   cmd_set_privacy    }, | ||||||
|  |     { "/rejoin",    cmd_rejoin         }, | ||||||
|  |     { "/silence",   cmd_silence        }, | ||||||
|  |     { "/topic",     cmd_set_topic      }, | ||||||
|  |     { "/unban",     cmd_unban          }, | ||||||
|  |     { "/unignore",  cmd_unignore       }, | ||||||
|  |     { "/unmod",     cmd_unmod          }, | ||||||
|  |     { "/unsilence", cmd_unsilence      }, | ||||||
|  |     { "/whois",     cmd_whois          }, | ||||||
|  | #ifdef AUDIO | ||||||
|  |     { "/mute",      cmd_mute           }, | ||||||
|  |     { "/sense",     cmd_sense          }, | ||||||
|  | #endif /* AUDIO */ | ||||||
|  |     { NULL,         NULL               }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define NUM_SPECIAL_COMMANDS 15 | ||||||
|  | static const char special_commands[NUM_SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = { | ||||||
|  |     "/ban", | ||||||
|  |     "/gaccept", | ||||||
|  |     "/group", | ||||||
|  |     "/ignore", | ||||||
|  |     "/kick", | ||||||
|  |     "/mod", | ||||||
|  |     "/nick", | ||||||
|  |     "/note", | ||||||
|  |     "/passwd", | ||||||
|  |     "/silence", | ||||||
|  |     "/topic", | ||||||
|  |     "/unignore", | ||||||
|  |     "/unmod", | ||||||
|  |     "/unsilence", | ||||||
|  |     "/whois", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* return true if input command is in the special_commands array. False otherwise.*/ | ||||||
|  | static bool is_special_command(const char *input) | ||||||
| { | { | ||||||
|  |     int s = char_find(0, input, ' '); | ||||||
|  |  | ||||||
|  |     if (s == strlen(input)) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < NUM_SPECIAL_COMMANDS; ++i) { | ||||||
|  |         if (strncmp(input, special_commands[i], s) == 0) | ||||||
|  |             return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Parses commands in the special_commands array which take exactly one argument that may contain spaces. | ||||||
|  |  * Unlike parse_command, this function does not split the input string at spaces. | ||||||
|  |  * | ||||||
|  |  * Returns number of arguments on success | ||||||
|  |  * Returns -1 on failure | ||||||
|  |  */ | ||||||
|  | static int parse_special_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     int len = strlen(input); | ||||||
|  |     int s = char_find(0, input, ' '); | ||||||
|  |  | ||||||
|  |     if (s + 1 >= len) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     memcpy(args[0], input, s); | ||||||
|  |     args[0][s++] = '\0';    /* increment to remove space after /command */ | ||||||
|  |     memcpy(args[1], input + s, len - s); | ||||||
|  |     args[1][len - s] = '\0'; | ||||||
|  |  | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Parses input command and puts args (split by spaces) into args array. | ||||||
|  |  * | ||||||
|  |  * Returns number of arguments on success | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (is_special_command(input)) | ||||||
|  |         return parse_special_command(w, self, input, args); | ||||||
|  |  | ||||||
|  |     char *cmd = strdup(input); | ||||||
|  |  | ||||||
|  |     if (cmd == NULL) | ||||||
|  |         exit_toxic_err("failed in parse_command", FATALERR_MEMORY); | ||||||
|  |  | ||||||
|     int num_args = 0; |     int num_args = 0; | ||||||
|     bool cmd_end = false;    /* flags when we get to the end of cmd */ |     int i = 0;    /* index of last char in an argument */ | ||||||
|     char *end;               /* points to the end of the current arg */ |  | ||||||
|  |  | ||||||
|     /* characters wrapped in double quotes count as one arg */ |     /* characters wrapped in double quotes count as one arg */ | ||||||
|     while (!cmd_end && num_args < MAX_NUM_ARGS) { |     while (num_args < MAX_NUM_ARGS) { | ||||||
|         if (*cmd == '\"') { |         int qt_ofst = 0;    /* set to 1 to offset index for quote char at end of arg */ | ||||||
|             end = strchr(cmd + 1, '\"'); |  | ||||||
|  |  | ||||||
|             if (end++ == NULL) {    /* Increment past the end quote */ |         if (*cmd == '\"') { | ||||||
|                 uint8_t *errmsg = "Invalid argument. Did you forget a closing \"?"; |             qt_ofst = 1; | ||||||
|                 line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |             i = char_find(1, cmd, '\"'); | ||||||
|  |  | ||||||
|  |             if (cmd[i] == '\0') { | ||||||
|  |                 const char *errmsg = "Invalid argument. Did you forget a closing \"?"; | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||||
|  |                 free(cmd); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             cmd_end = *end == '\0'; |  | ||||||
|         } else { |         } else { | ||||||
|             end = strchr(cmd, ' '); |             i = char_find(0, cmd, ' '); | ||||||
|             cmd_end = end == NULL; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!cmd_end) |         memcpy(args[num_args], cmd, i + qt_ofst); | ||||||
|             *end++ = '\0';    /* mark end of current argument */ |         args[num_args++][i + qt_ofst] = '\0'; | ||||||
|  |  | ||||||
|         /* Copy from start of current arg to where we just inserted the null byte */ |         if (cmd[i] == '\0')    /* no more args */ | ||||||
|         strcpy(args[num_args++], cmd); |             break; | ||||||
|         cmd = end; |  | ||||||
|  |         char tmp[MAX_STR_SIZE]; | ||||||
|  |         snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]); | ||||||
|  |         strcpy(cmd, tmp);    /* tmp will always fit inside cmd */ | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /* Ugly special case concatinates all args after arg1 for multi-word group passwords */ | ||||||
|  |     if (num_args > 2 && strcmp(args[0], "/join") == 0) | ||||||
|  |         strcpy(args[2], input + strlen(args[0]) + 1 + strlen(args[1]) + 1); | ||||||
|  |  | ||||||
|  |     free(cmd); | ||||||
|     return num_args; |     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; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     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] = {0}; |     char args[MAX_NUM_ARGS][MAX_STR_SIZE]; | ||||||
|     int num_args = parse_command(w, self, cmd, args); |     int num_args = parse_command(w, self, input, args); | ||||||
|  |  | ||||||
|     if (num_args == -1) |     if (num_args == -1) | ||||||
|         return; |         return; | ||||||
| @@ -147,18 +269,20 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode) | |||||||
|        Note: Global commands must come last in case of duplicate command names */ |        Note: Global commands must come last in case of duplicate command names */ | ||||||
|     switch (mode) { |     switch (mode) { | ||||||
|         case CHAT_COMMAND_MODE: |         case CHAT_COMMAND_MODE: | ||||||
|             if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0) |             if (do_command(w, self, m, num_args, chat_commands, args) == 0) | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case GROUPCHAT_COMMAND_MODE: |         case GROUPCHAT_COMMAND_MODE: | ||||||
|  |             if (do_command(w, self, m, num_args, group_commands, args) == 0) | ||||||
|  |                 return; | ||||||
|  |  | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0) |     if (do_command(w, self, m, num_args, global_commands, args) == 0) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     uint8_t *errmsg = "Invalid command."; |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command."); | ||||||
|     line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,28 +20,18 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #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 */ |  | ||||||
|  |  | ||||||
| #ifdef _SUPPORT_AUDIO |  | ||||||
| #define GLOBAL_NUM_COMMANDS 16 |  | ||||||
| #define CHAT_NUM_COMMANDS 10 |  | ||||||
| #else |  | ||||||
| #define GLOBAL_NUM_COMMANDS 14 |  | ||||||
| #define CHAT_NUM_COMMANDS 5 |  | ||||||
| #endif /* _SUPPORT_AUDIO */ |  | ||||||
|  |  | ||||||
| enum { | enum { | ||||||
|     GLOBAL_COMMAND_MODE, |     GLOBAL_COMMAND_MODE, | ||||||
|     CHAT_COMMAND_MODE, |     CHAT_COMMAND_MODE, | ||||||
|     GROUPCHAT_COMMAND_MODE, |     GROUPCHAT_COMMAND_MODE, | ||||||
| }; | } COMMAND_MODE; | ||||||
|  |  | ||||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode); | void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | ||||||
|  |  | ||||||
| #endif /* #define _execute_h */ | #endif /* #define EXECUTE_H */ | ||||||
|   | |||||||
| @@ -1,130 +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/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <time.h> |  | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "file_senders.h" |  | ||||||
| #include "line_info.h" |  | ||||||
|  |  | ||||||
| FileSender file_senders[MAX_FILES]; |  | ||||||
| uint8_t max_file_senders_index; |  | ||||||
|  |  | ||||||
| static void close_file_sender(int i) |  | ||||||
| { |  | ||||||
|     fclose(file_senders[i].file); |  | ||||||
|     memset(&file_senders[i], 0, sizeof(FileSender)); |  | ||||||
|  |  | ||||||
|     int j; |  | ||||||
|  |  | ||||||
|     for (j = max_file_senders_index; j > 0; --j) { |  | ||||||
|         if (file_senders[j - 1].active) |  | ||||||
|             break; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     max_file_senders_index = j; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Should only be called on exit */ |  | ||||||
| void close_all_file_senders(void) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < max_file_senders_index; ++i) { |  | ||||||
|         if (file_senders[i].active) |  | ||||||
|             fclose(file_senders[i].file); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void do_file_senders(Tox *m) |  | ||||||
| { |  | ||||||
|     uint8_t 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; |  | ||||||
|         uint8_t *pathname = file_senders[i].pathname; |  | ||||||
|         int filenum = file_senders[i].filenum; |  | ||||||
|         int32_t friendnum = file_senders[i].friendnum; |  | ||||||
|         FILE *fp = file_senders[i].file; |  | ||||||
|         uint64_t current_time = get_unix_time(); |  | ||||||
|  |  | ||||||
|         /* If file transfer has timed out kill transfer and send kill control */ |  | ||||||
|         if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) { |  | ||||||
|             if (self->chatwin != NULL) { |  | ||||||
|                 snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", pathname); |  | ||||||
|                 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, TOX_FILECONTROL_KILL, 0, 0); |  | ||||||
|             close_file_sender(i); |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         while (true) { |  | ||||||
|             if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece, |  | ||||||
|                                    file_senders[i].piecelen) == -1) |  | ||||||
|                 break; |  | ||||||
|  |  | ||||||
|             file_senders[i].timestamp = current_time; |  | ||||||
|             file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, |  | ||||||
|                                              tox_file_data_size(m, friendnum), fp); |  | ||||||
|  |  | ||||||
|             /* refresh line with percentage complete */ |  | ||||||
|             if (self->chatwin != NULL) { |  | ||||||
|                 uint64_t size = file_senders[i].size; |  | ||||||
|                 long double remain = (long double) tox_file_data_remaining(m, friendnum, filenum, 0); |  | ||||||
|                 long double pct_remain = 100; |  | ||||||
|  |  | ||||||
|                 if (remain) |  | ||||||
|                     pct_remain = (1 - (remain / size)) * 100; |  | ||||||
|  |  | ||||||
|                 const uint8_t *name = file_senders[i].pathname; |  | ||||||
|                 snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1Lf%%)", name, pct_remain); |  | ||||||
|                 line_info_set(self, file_senders[i].line_id, msg); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (file_senders[i].piecelen == 0) { |  | ||||||
|                 if (self->chatwin != NULL) { |  | ||||||
|                     snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname); |  | ||||||
|                     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, TOX_FILECONTROL_FINISHED, 0, 0); |  | ||||||
|                 close_file_sender(i); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										277
									
								
								src/file_transfers.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								src/file_transfers.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,277 @@ | |||||||
|  | /*  file_transfers.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <time.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  | #include "friendlist.h" | ||||||
|  | #include "file_transfers.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "notify.h" | ||||||
|  |  | ||||||
|  | extern FriendsList Friends; | ||||||
|  |  | ||||||
|  | /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | ||||||
|  | #define NUM_PROG_MARKS 50 | ||||||
|  |  | ||||||
|  | /* creates initial progress line that will be updated during file transfer. | ||||||
|  |    Assumes progline has room for at least MAX_STR_SIZE bytes */ | ||||||
|  | void init_progress_bar(char *progline) | ||||||
|  | { | ||||||
|  |     strcpy(progline, "0% ["); | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < NUM_PROG_MARKS; ++i) | ||||||
|  |         strcat(progline, "-"); | ||||||
|  |  | ||||||
|  |     strcat(progline, "] 0.0 B/s"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* prints a progress bar for file transfers. */ | ||||||
|  | void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id) | ||||||
|  | { | ||||||
|  |     if (bps < 0 || pct_done < 0 || pct_done > 100) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     char pct_str[24]; | ||||||
|  |     snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done); | ||||||
|  |  | ||||||
|  |     char bps_str[24]; | ||||||
|  |     bytes_convert_str(bps_str, sizeof(bps_str), bps); | ||||||
|  |  | ||||||
|  |     char prog_line[NUM_PROG_MARKS + 1] = {0}; | ||||||
|  |     int n = pct_done / (100 / NUM_PROG_MARKS); | ||||||
|  |     int i, j; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < n; ++i) | ||||||
|  |         strcat(prog_line, "="); | ||||||
|  |  | ||||||
|  |     if (pct_done < 100) | ||||||
|  |         strcpy(prog_line + n, ">"); | ||||||
|  |  | ||||||
|  |     for (j = i; j < NUM_PROG_MARKS - 1; ++j) | ||||||
|  |         strcat(prog_line, "-"); | ||||||
|  |  | ||||||
|  |     char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7]; | ||||||
|  |     snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str); | ||||||
|  |  | ||||||
|  |     line_info_set(self, line_id, full_line); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft) | ||||||
|  | { | ||||||
|  |     if (ft->state == FILE_TRANSFER_INACTIVE) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     /* Timeout must be set to 1 second to show correct bytes per second */ | ||||||
|  |     if (!timed_out(ft->last_line_progress, 1)) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     double remain = ft->file_size - ft->position; | ||||||
|  |     double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100; | ||||||
|  |     print_progress_bar(self, ft->bps, pct_done, ft->line_id); | ||||||
|  |  | ||||||
|  |     ft->bps = 0; | ||||||
|  |     ft->last_line_progress = get_unix_time(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* refreshes active file transfer status bars. */ | ||||||
|  | void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]); | ||||||
|  |         refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns a pointer to friendnum's FileTransfer struct associated with filenum. | ||||||
|  |  * Returns NULL if filenum is invalid. | ||||||
|  |  */ | ||||||
|  | struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i]; | ||||||
|  |  | ||||||
|  |         if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) | ||||||
|  |             return ft_send; | ||||||
|  |  | ||||||
|  |         struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i]; | ||||||
|  |  | ||||||
|  |         if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum) | ||||||
|  |             return ft_recv; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns a pointer to the FileTransfer struct associated with index with the direction specified. | ||||||
|  |  * Returns NULL on failure. | ||||||
|  |  */ | ||||||
|  | struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | ||||||
|  |         FILE_TRANSFER_DIRECTION direction) | ||||||
|  | { | ||||||
|  |     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) | ||||||
|  |         return NULL; | ||||||
|  |  | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? | ||||||
|  |                                       &Friends.list[friendnum].file_sender[i] : | ||||||
|  |                                       &Friends.list[friendnum].file_receiver[i]; | ||||||
|  |  | ||||||
|  |         if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) | ||||||
|  |             return ft; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns a pointer to an unused file sender. | ||||||
|  |  * Returns NULL if all file senders are in use. | ||||||
|  |  */ | ||||||
|  | static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i]; | ||||||
|  |  | ||||||
|  |         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|  |             memset(ft, 0, sizeof(struct FileTransfer)); | ||||||
|  |             ft->window = window; | ||||||
|  |             ft->index = i; | ||||||
|  |             ft->friendnum = friendnum; | ||||||
|  |             ft->filenum = filenum; | ||||||
|  |             ft->file_type = type; | ||||||
|  |             ft->last_keep_alive = get_unix_time(); | ||||||
|  |             ft->state = FILE_TRANSFER_PENDING; | ||||||
|  |             ft->direction = FILE_TRANSFER_SEND; | ||||||
|  |             return ft; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns a pointer to an unused file receiver. | ||||||
|  |  * Returns NULL if all file receivers are in use. | ||||||
|  |  */ | ||||||
|  | static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i]; | ||||||
|  |  | ||||||
|  |         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|  |             memset(ft, 0, sizeof(struct FileTransfer)); | ||||||
|  |             ft->window = window; | ||||||
|  |             ft->index = i; | ||||||
|  |             ft->friendnum = friendnum; | ||||||
|  |             ft->filenum = filenum; | ||||||
|  |             ft->file_type = type; | ||||||
|  |             ft->last_keep_alive = get_unix_time(); | ||||||
|  |             ft->state = FILE_TRANSFER_PENDING; | ||||||
|  |             ft->direction = FILE_TRANSFER_RECV; | ||||||
|  |             return ft; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Initializes an unused file transfer and returns its pointer. | ||||||
|  |  * Returns NULL on failure. | ||||||
|  |  */ | ||||||
|  | struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, | ||||||
|  |                                        FILE_TRANSFER_DIRECTION direction, uint8_t type) | ||||||
|  | { | ||||||
|  |     if (direction == FILE_TRANSFER_RECV) | ||||||
|  |         return new_file_receiver(window, friendnum, filenum, type); | ||||||
|  |  | ||||||
|  |     if (direction == FILE_TRANSFER_SEND) | ||||||
|  |         return new_file_sender(window, friendnum, filenum, type); | ||||||
|  |  | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Closes file transfer ft. | ||||||
|  |  * | ||||||
|  |  * Set CTRL to -1 if we don't want to send a control signal. | ||||||
|  |  * Set message or self to NULL if we don't want to display a message. | ||||||
|  |  */ | ||||||
|  | void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, | ||||||
|  |                          Notification sound_type) | ||||||
|  | { | ||||||
|  |     if (!ft) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     if (ft->state == FILE_TRANSFER_INACTIVE) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     if (ft->file) | ||||||
|  |         fclose(ft->file); | ||||||
|  |  | ||||||
|  |     if (CTRL >= 0) | ||||||
|  |         tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL); | ||||||
|  |  | ||||||
|  |     if (message && self) { | ||||||
|  |         if (self->active_box != -1 && sound_type != silent) | ||||||
|  |             box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message); | ||||||
|  |         else | ||||||
|  |             box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message); | ||||||
|  |  | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memset(ft, 0, sizeof(struct FileTransfer)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Kills all active file transfers for friendnum */ | ||||||
|  | void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|  |         close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void kill_all_file_transfers(Tox *m) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < Friends.max_idx; ++i) | ||||||
|  |         kill_all_file_transfers_friend(m, Friends.list[i].num); | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								src/file_transfers.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/file_transfers.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | /*  file_transfers.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef FILE_TRANSFERS_H | ||||||
|  | #define FILE_TRANSFERS_H | ||||||
|  |  | ||||||
|  | #include <limits.h> | ||||||
|  |  | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  | #include "notify.h" | ||||||
|  |  | ||||||
|  | #define KiB 1024 | ||||||
|  | #define MiB 1048576       /* 1024^2 */ | ||||||
|  | #define GiB 1073741824    /* 1024^3 */ | ||||||
|  |  | ||||||
|  | #define MAX_FILES 32 | ||||||
|  |  | ||||||
|  | typedef enum FILE_TRANSFER_STATE { | ||||||
|  |     FILE_TRANSFER_INACTIVE, | ||||||
|  |     FILE_TRANSFER_PAUSED, | ||||||
|  |     FILE_TRANSFER_PENDING, | ||||||
|  |     FILE_TRANSFER_STARTED, | ||||||
|  | } FILE_TRANSFER_STATE; | ||||||
|  |  | ||||||
|  | typedef enum FILE_TRANSFER_DIRECTION { | ||||||
|  |     FILE_TRANSFER_SEND, | ||||||
|  |     FILE_TRANSFER_RECV | ||||||
|  | } FILE_TRANSFER_DIRECTION; | ||||||
|  |  | ||||||
|  | struct FileTransfer { | ||||||
|  |     ToxWindow *window; | ||||||
|  |     FILE *file; | ||||||
|  |     FILE_TRANSFER_STATE state; | ||||||
|  |     FILE_TRANSFER_DIRECTION direction; | ||||||
|  |     uint8_t file_type; | ||||||
|  |     char file_name[TOX_MAX_FILENAME_LENGTH + 1]; | ||||||
|  |     char file_path[PATH_MAX + 1];    /* Not used by senders */ | ||||||
|  |     double   bps; | ||||||
|  |     uint32_t filenum; | ||||||
|  |     uint32_t friendnum; | ||||||
|  |     size_t   index; | ||||||
|  |     uint64_t file_size; | ||||||
|  |     uint64_t position; | ||||||
|  |     time_t   last_line_progress;   /* The last time we updated the progress bar */ | ||||||
|  |     time_t   last_keep_alive;  /* The last time we sent or received data */ | ||||||
|  |     uint32_t line_id; | ||||||
|  |     uint8_t  file_id[TOX_FILE_ID_LENGTH]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* creates initial progress line that will be updated during file transfer. | ||||||
|  |    progline must be at lesat MAX_STR_SIZE bytes */ | ||||||
|  | void init_progress_bar(char *progline); | ||||||
|  |  | ||||||
|  | /* prints a progress bar for file transfers */ | ||||||
|  | void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id); | ||||||
|  |  | ||||||
|  | /* refreshes active file transfer status bars. */ | ||||||
|  | void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum); | ||||||
|  |  | ||||||
|  | /* Returns a pointer to friendnum's FileTransfer struct associated with filenum. | ||||||
|  |  * Returns NULL if filenum is invalid. | ||||||
|  |  */ | ||||||
|  | struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Returns a pointer to the FileTransfer struct associated with index with the direction specified. | ||||||
|  |  * Returns NULL on failure. | ||||||
|  |  */ | ||||||
|  | struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | ||||||
|  |         FILE_TRANSFER_DIRECTION direction); | ||||||
|  |  | ||||||
|  | /* Initializes an unused file transfer and returns its pointer. | ||||||
|  |  * Returns NULL on failure. | ||||||
|  |  */ | ||||||
|  | struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, | ||||||
|  |                                        FILE_TRANSFER_DIRECTION direction, uint8_t type); | ||||||
|  |  | ||||||
|  | /* Closes file transfer ft. | ||||||
|  |  * | ||||||
|  |  * Set CTRL to -1 if we don't want to send a control signal. | ||||||
|  |  * Set message or self to NULL if we don't want to display a message. | ||||||
|  |  */ | ||||||
|  | void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, | ||||||
|  |                          Notification sound_type); | ||||||
|  |  | ||||||
|  | /* Kills all active file transfers for friendnum */ | ||||||
|  | void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum); | ||||||
|  |  | ||||||
|  | void kill_all_file_transfers(Tox *m); | ||||||
|  |  | ||||||
|  | #endif  /* #define FILE_TRANSFERS_H */ | ||||||
							
								
								
									
										1171
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										1171
									
								
								src/friendlist.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -20,56 +20,73 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef FRIENDLIST_H_53I41IM | #ifndef FRIENDLIST_H | ||||||
| #define FRIENDLIST_H_53I41IM | #define FRIENDLIST_H | ||||||
|  |  | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "file_senders.h" | #include "file_transfers.h" | ||||||
|  |  | ||||||
| #define TIME_STR_SIZE 16 |  | ||||||
|  |  | ||||||
| struct FileReceiver { |  | ||||||
|     uint8_t filenames[MAX_FILES][MAX_STR_SIZE]; |  | ||||||
|     FILE *files[MAX_FILES]; |  | ||||||
|     bool pending[MAX_FILES]; |  | ||||||
|     uint64_t size[MAX_FILES]; |  | ||||||
|     uint32_t line_id[MAX_FILES]; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct LastOnline { | struct LastOnline { | ||||||
|     uint64_t last_on; |     uint64_t last_on; | ||||||
|     struct tm tm; |     struct tm tm; | ||||||
|     uint8_t hour_min_str[TIME_STR_SIZE];    /* holds 12-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 GroupInvite { | ||||||
|  |     uint8_t *data; | ||||||
|  |     uint16_t length; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint8_t name[TOX_MAX_NAME_LENGTH]; |     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     uint16_t namelength; |     int namelength; | ||||||
|     uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH]; |     char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; | ||||||
|     uint16_t statusmsg_len; |     size_t statusmsg_len; | ||||||
|     uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE]; |     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||||
|     uint8_t pub_key[TOX_CLIENT_ID_SIZE]; |     uint32_t num; | ||||||
|     int32_t num; |  | ||||||
|     int chatwin; |     int chatwin; | ||||||
|     bool active; |     bool active; | ||||||
|     bool online; |     TOX_CONNECTION connection_status; | ||||||
|     uint8_t is_typing; |     bool is_typing; | ||||||
|     bool logging_on;    /* saves preference for friend irrespective of chat windows */ |     bool logging_on;    /* saves preference for friend irrespective of global settings */ | ||||||
|     uint8_t status; |     uint8_t status; | ||||||
|  |  | ||||||
|     struct LastOnline last_online; |     struct LastOnline last_online; | ||||||
|     struct FileReceiver file_receiver; |     struct GroupInvite group_invite; | ||||||
|  |  | ||||||
|  |     struct FileTransfer file_receiver[MAX_FILES]; | ||||||
|  |     struct FileTransfer file_sender[MAX_FILES]; | ||||||
| } ToxicFriend; | } ToxicFriend; | ||||||
|  |  | ||||||
| ToxWindow new_friendlist(void); | typedef struct { | ||||||
| void disable_chatwin(int32_t f_num); |     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
| int get_friendnum(uint8_t *name); |     int namelength; | ||||||
|  |     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||||
|  |     uint32_t num; | ||||||
|  |     bool active; | ||||||
|  |     uint64_t last_on; | ||||||
|  | } BlockedFriend; | ||||||
|  |  | ||||||
| void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort); | typedef struct { | ||||||
|  |     int num_selected; | ||||||
|  |     size_t num_friends; | ||||||
|  |     size_t num_online; | ||||||
|  |     size_t max_idx;    /* 1 + the index of the last friend in list */ | ||||||
|  |     uint32_t *index; | ||||||
|  |     ToxicFriend *list; | ||||||
|  | } FriendsList; | ||||||
|  |  | ||||||
|  | ToxWindow new_friendlist(void); | ||||||
|  | void disable_chatwin(uint32_t f_num); | ||||||
|  | int get_friendnum(uint8_t *name); | ||||||
|  | int load_blocklist(char *data); | ||||||
|  | void kill_friendlist(void); | ||||||
|  | void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort); | ||||||
|  |  | ||||||
| /* sorts friendlist_index first by connection status then alphabetically */ | /* sorts friendlist_index first by connection status then alphabetically */ | ||||||
| void sort_friendlist_index(void); | void sort_friendlist_index(void); | ||||||
|  |  | ||||||
| #endif /* end of include guard: FRIENDLIST_H_53I41IM */ | #endif /* end of include guard: FRIENDLIST_H */ | ||||||
|   | |||||||
| @@ -20,10 +20,6 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| @@ -33,154 +29,164 @@ | |||||||
| #include "friendlist.h" | #include "friendlist.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "dns.h" | #include "groupchat.h" | ||||||
|  | #include "prompt.h" | ||||||
|  | #include "help.h" | ||||||
|  | #include "term_mplex.h" | ||||||
|  | #include "avatars.h" | ||||||
|  | #include "name_lookup.h" | ||||||
|  | #include "qr_code.h" | ||||||
|  | #include "toxic_strings.h" | ||||||
|  |  | ||||||
| extern char *DATA_FILE; | extern char *DATA_FILE; | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
|  | extern FriendsList Friends; | ||||||
| extern ToxicFriend friends[MAX_FRIENDS_NUM]; | extern FriendRequests FrndRequests; | ||||||
|  |  | ||||||
| extern uint8_t 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]) | ||||||
| { | { | ||||||
|     uint8_t *msg; |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||||
|     if (argc != 1) { |  | ||||||
|         msg = "Invalid syntax."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int req = atoi(argv[1]); |     long int req = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req >= MAX_FRIENDS_NUM) { |     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||||
|         msg = "No pending friend request with that number."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!strlen(pending_frnd_requests[req])) { |     if (!FrndRequests.request[req].active) { | ||||||
|         msg = "No pending friend request with that number."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int32_t friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]); |     TOX_ERR_FRIEND_ADD err; | ||||||
|  |     uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err); | ||||||
|  |  | ||||||
|     if (friendnum == -1) |     if (err != TOX_ERR_FRIEND_ADD_OK) { | ||||||
|         msg = "Failed to add friend."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err); | ||||||
|     else { |         return; | ||||||
|         msg = "Friend request accepted."; |     } else { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); | ||||||
|         on_friendadded(m, friendnum, true); |         on_friendadded(m, friendnum, true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE); |     memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = num_frnd_requests; i > 0; --i) { |     for (i = FrndRequests.max_idx; i > 0; --i) { | ||||||
|         if (!strlen(pending_frnd_requests[i - 1])) |         if (FrndRequests.request[i - 1].active) | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     num_frnd_requests = i; |     FrndRequests.max_idx = i; | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |     --FrndRequests.num_requests; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_add_helper(ToxWindow *self, Tox *m, uint8_t *id_bin, uint8_t *msg) | void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |     const char *errmsg; | ||||||
|     int32_t f_num = tox_add_friend(m, id_bin, msg, strlen(msg)); |  | ||||||
|  |  | ||||||
|     switch (f_num) { |     TOX_ERR_FRIEND_ADD err; | ||||||
|         case TOX_FAERR_TOOLONG: |     uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_FRIEND_ADD_TOO_LONG: | ||||||
|             errmsg = "Message is too long."; |             errmsg = "Message is too long."; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_FAERR_NOMESSAGE: |         case TOX_ERR_FRIEND_ADD_NO_MESSAGE: | ||||||
|             errmsg = "Please add a message to your request."; |             errmsg = "Please add a message to your request."; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_FAERR_OWNKEY: |         case TOX_ERR_FRIEND_ADD_OWN_KEY: | ||||||
|             errmsg = "That appears to be your own ID."; |             errmsg = "That appears to be your own ID."; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_FAERR_ALREADYSENT: |         case TOX_ERR_FRIEND_ADD_ALREADY_SENT: | ||||||
|             errmsg = "Friend request has already been sent."; |             errmsg = "Friend request has already been sent."; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_FAERR_UNKNOWN: |         case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM: | ||||||
|             errmsg = "Undefined error when adding friend."; |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case TOX_FAERR_BADCHECKSUM: |  | ||||||
|             errmsg = "Bad checksum in address."; |             errmsg = "Bad checksum in address."; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_FAERR_SETNEWNOSPAM: |         case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM: | ||||||
|             errmsg = "Nospam was different."; |             errmsg = "Nospam was different."; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         default: |         case TOX_ERR_FRIEND_ADD_MALLOC: | ||||||
|  |             errmsg = "Core memory allocation failed."; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_FRIEND_ADD_OK: | ||||||
|             errmsg = "Friend request sent."; |             errmsg = "Friend request sent."; | ||||||
|             on_friendadded(m, f_num, true); |             on_friendadded(m, f_num, true); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_FRIEND_ADD_NULL: | ||||||
|  |  | ||||||
|  |         /* fallthrough */ | ||||||
|  |         default: | ||||||
|  |             errmsg = "Faile to add friend: Unknown error."; | ||||||
|  |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |  | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         errmsg = "Invalid syntax."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char *id = argv[1]; |     const char *id = argv[1]; | ||||||
|     uint8_t msg[MAX_STR_SIZE]; |     char msg[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|     if (argc > 1) { |     if (argc > 1) { | ||||||
|         uint8_t *temp = argv[2]; |         if (argv[2][0] != '\"') { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes."); | ||||||
|         if (temp[0] != '\"') { |  | ||||||
|             errmsg = "Message must be enclosed in quotes."; |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         temp[strlen(++temp) - 1] = L'\0'; |         /* remove opening and closing quotes */ | ||||||
|         snprintf(msg, sizeof(msg), "%s", temp); |         char tmp[MAX_STR_SIZE]; | ||||||
|  |         snprintf(tmp, sizeof(tmp), "%s", &argv[2][1]); | ||||||
|  |         int len = strlen(tmp) - 1; | ||||||
|  |         tmp[len] = '\0'; | ||||||
|  |         snprintf(msg, sizeof(msg), "%s", tmp); | ||||||
|     } else { |     } else { | ||||||
|         uint8_t selfname[TOX_MAX_NAME_LENGTH]; |         char selfname[TOX_MAX_NAME_LENGTH]; | ||||||
|         uint16_t n_len = tox_get_self_name(m, 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); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0}; |     char id_bin[TOX_ADDRESS_SIZE] = {0}; | ||||||
|     uint16_t id_len = strlen(id); |     uint16_t id_len = (uint16_t) strlen(id); | ||||||
|  |  | ||||||
|     /* try to add tox ID */ |     /* try to add tox ID */ | ||||||
|     if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) { |     if (id_len == 2 * TOX_ADDRESS_SIZE) { | ||||||
|         size_t i; |         size_t i; | ||||||
|         char xx[3]; |         char xx[3]; | ||||||
|         uint32_t x; |         uint32_t x; | ||||||
|  |  | ||||||
|         for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) { |         for (i = 0; i < TOX_ADDRESS_SIZE; ++i) { | ||||||
|             xx[0] = id[2 * i]; |             xx[0] = id[2 * i]; | ||||||
|             xx[1] = id[2 * i + 1]; |             xx[1] = id[2 * i + 1]; | ||||||
|             xx[2] = '\0'; |             xx[2] = '\0'; | ||||||
|  |  | ||||||
|             if (sscanf(xx, "%02x", &x) != 1) { |             if (sscanf(xx, "%02x", &x) != 1) { | ||||||
|                 errmsg = "Invalid ID."; |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID."); | ||||||
|                 line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -188,252 +194,464 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         cmd_add_helper(self, m, id_bin, msg); |         cmd_add_helper(self, m, id_bin, msg); | ||||||
|     } else {    /* assume id is a username@domain address and do DNS lookup */ |     } else {    /* assume id is a username@domain address and do http name server lookup */ | ||||||
|         dns3_lookup(self, m, id_bin, id, msg); |         name_lookup(self, m, id_bin, id, msg); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 2 || strlen(argv[1]) < 3) { | ||||||
|  |         avatar_unset(m); | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (argv[1][0] != '\"') { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* remove opening and closing quotes */ | ||||||
|  |     char path[MAX_STR_SIZE]; | ||||||
|  |     snprintf(path, sizeof(path), "%s", &argv[1][1]); | ||||||
|  |     int len = strlen(path) - 1; | ||||||
|  |  | ||||||
|  |     if (len <= 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     path[len] = '\0'; | ||||||
|  |     char filename[MAX_STR_SIZE]; | ||||||
|  |     get_file_name(filename, sizeof(filename), path); | ||||||
|  |  | ||||||
|  |     if (avatar_set(m, path, len) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|  |                       "Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.", | ||||||
|  |                       MAX_AVATAR_FILE_SIZE); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename); | ||||||
|  | } | ||||||
|  |  | ||||||
| void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     line_info_clear(self->chatwin->hst); |     line_info_clear(self->chatwin->hst); | ||||||
|     wclear(window); |     force_refresh(window); | ||||||
|  |  | ||||||
|     if (self->is_prompt) { |  | ||||||
|         int y2, x2; |  | ||||||
|         getmaxyx(window, y2, x2); |  | ||||||
|         wmove(self->chatwin->history, y2 - 1, 2); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| 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]) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |  | ||||||
|  |  | ||||||
|     /* check arguments */ |  | ||||||
|     if (argc != 3) { |     if (argc != 3) { | ||||||
|         errmsg = "Invalid syntax."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>"); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char *ip = argv[1]; |     const char *ip = argv[1]; | ||||||
|     char *port = argv[2]; |     const char *port_str = argv[2]; | ||||||
|     char *key = argv[3]; |     const char *ascii_key = argv[3]; | ||||||
|  |  | ||||||
|     if (atoi(port) == 0) { |     long int port = strtol(port_str, NULL, 10); | ||||||
|         errmsg = "Invalid syntax."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |     if (port <= 0 || port > MAX_PORT_RANGE) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t *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)), binary_string); |     if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { | ||||||
|     free(binary_string); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_BOOTSTRAP err; | ||||||
|  |     tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err); | ||||||
|  |     tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_BOOTSTRAP_BAD_HOST: | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP."); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_BOOTSTRAP_BAD_PORT: | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port."); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case TOX_ERR_BOOTSTRAP_NULL: | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     long int req = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|  |     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!FrndRequests.request[req].active) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); | ||||||
|  |  | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = FrndRequests.max_idx; i > 0; --i) { | ||||||
|  |         if (FrndRequests.request[i - 1].active) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     FrndRequests.max_idx = i; | ||||||
|  |     --FrndRequests.num_requests; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |  | ||||||
|  |  | ||||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { |     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||||
|         errmsg = " * Warning: Too many windows are open."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int groupnum = tox_add_groupchat(m); |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name required"); | ||||||
|     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) { |     const char *tmp_name = argv[1]; | ||||||
|         errmsg = "Group chat window failed to initialize."; |     int len = strlen(tmp_name); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         tox_del_groupchat(m, groupnum); |     if (len == 0 || len > TOX_GROUP_MAX_GROUP_NAME_LENGTH) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group name."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t msg[MAX_STR_SIZE]; |     char name[TOX_GROUP_MAX_GROUP_NAME_LENGTH]; | ||||||
|     snprintf(msg, sizeof(msg), "Group chat created as %d.", groupnum); |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |     if (argv[1][0] == '\"') {    /* remove opening and closing quotes */ | ||||||
|  |         snprintf(name, sizeof(name), "%s", &argv[1][1]); | ||||||
|  |         len -= 2; | ||||||
|  |         name[len] = '\0'; | ||||||
|  |     } else { | ||||||
|  |         snprintf(name, sizeof(name), "%s", argv[1]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_NEW err; | ||||||
|  |     uint32_t groupnum = tox_group_new(m, TOX_GROUP_PRIVACY_STATE_PUBLIC, (uint8_t *) name, len, &err); | ||||||
|  |  | ||||||
|  |     if (err != TOX_ERR_GROUP_NEW_OK) { | ||||||
|  |         switch (err) { | ||||||
|  |             case TOX_ERR_GROUP_NEW_TOO_LONG: { | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name length cannot exceed %d.", | ||||||
|  |                               TOX_GROUP_MAX_GROUP_NAME_LENGTH); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             case TOX_ERR_GROUP_NEW_EMPTY: { | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name cannot be empty."); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             default: { | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d).", err); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int init = init_groupchat_win(m, groupnum, name, len); | ||||||
|  |  | ||||||
|  |     if (init == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||||
|  |         tox_group_leave(m, groupnum, NULL, 0, NULL); | ||||||
|  |     } else if (init == -2) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|  |                       "You have been kicked from a group. Close the window and try again."); | ||||||
|  |         tox_group_leave(m, groupnum, NULL, 0, NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Chat ID is required."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *chat_id = argv[1]; | ||||||
|  |  | ||||||
|  |     if (strlen(chat_id) != TOX_GROUP_CHAT_ID_SIZE * 2) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char id_bin[TOX_GROUP_CHAT_ID_SIZE] = {0}; | ||||||
|  |  | ||||||
|  |     size_t i; | ||||||
|  |     char xch[3]; | ||||||
|  |     uint32_t x; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) { | ||||||
|  |         xch[0] = chat_id[2 * i]; | ||||||
|  |         xch[1] = chat_id[2 * i + 1]; | ||||||
|  |         xch[2] = '\0'; | ||||||
|  |  | ||||||
|  |         if (sscanf(xch, "%02x", &x) != 1) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         id_bin[i] = x; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *passwd = NULL; | ||||||
|  |     uint16_t passwd_len = 0; | ||||||
|  |  | ||||||
|  |     if (argc > 1) { | ||||||
|  |         passwd = argv[2]; | ||||||
|  |         passwd_len = strlen(passwd); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_JOIN err; | ||||||
|  |     uint32_t groupnum = tox_group_join(m, (uint8_t *) id_bin, (uint8_t *) passwd, passwd_len, &err); | ||||||
|  |  | ||||||
|  |     if (err != TOX_ERR_GROUP_JOIN_OK) { | ||||||
|  |         if (err == TOX_ERR_GROUP_JOIN_TOO_LONG) | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length cannot exceed %d.", TOX_GROUP_MAX_PASSWORD_SIZE); | ||||||
|  |         else | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err); | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int init = init_groupchat_win(m, groupnum, NULL, 0); | ||||||
|  |  | ||||||
|  |     if (init == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||||
|  |         tox_group_leave(m, groupnum, NULL, 0, NULL); | ||||||
|  |     } else if (init == -2) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|  |                       "You have been kicked from a group. Close the window and try again."); | ||||||
|  |         tox_group_leave(m, groupnum, NULL, 0, NULL); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint8_t *msg; |     const char *msg; | ||||||
|     struct chatlog *log = self->chatwin->log; |     struct chatlog *log = self->chatwin->log; | ||||||
|  |  | ||||||
|     if (argc == 0) { |     if (argc == 0) { | ||||||
|         if (log->log_on) |         if (log->log_on) | ||||||
|             msg = "Logging for this window is ON. Type \"/log off\" to disable."; |             msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)"; | ||||||
|         else |         else | ||||||
|             msg = "Logging for this window is OFF. Type \"/log on\" to enable."; |             msg = "Logging for this window is OFF; type \"/log on\" to enable."; | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t *swch = argv[1]; |     const char *swch = argv[1]; | ||||||
|  |  | ||||||
|     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { |     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { | ||||||
|  |         char myid[TOX_ADDRESS_SIZE]; | ||||||
|  |         tox_self_get_address(m, (uint8_t *) myid); | ||||||
|  |  | ||||||
|  |         int log_ret = -1; | ||||||
|  |  | ||||||
|         if (self->is_chat) { |         if (self->is_chat) { | ||||||
|             friends[self->num].logging_on = true; |             Friends.list[self->num].logging_on = true; | ||||||
|             log_enable(self->name, friends[self->num].pub_key, log); |             log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT); | ||||||
|         } else if (self->is_prompt) { |         } else if (self->is_prompt) { | ||||||
|             uint8_t myid[TOX_FRIEND_ADDRESS_SIZE]; |             log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT); | ||||||
|             tox_get_address(m, myid); |  | ||||||
|             log_enable(self->name, myid, log); |  | ||||||
|         } else if (self->is_groupchat) { |         } else if (self->is_groupchat) { | ||||||
|             log_enable(self->name, NULL, log); |             log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         msg = "Logging enabled"; |         msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize."; | ||||||
|         line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|         return; |         return; | ||||||
|     } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { |     } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { | ||||||
|         if (self->is_chat) |         if (self->is_chat) | ||||||
|             friends[self->num].logging_on = false; |             Friends.list[self->num].logging_on = false; | ||||||
|  |  | ||||||
|         log_disable(log); |         log_disable(log); | ||||||
|  |  | ||||||
|         msg = "Logging disabled"; |         msg = "Logging disabled."; | ||||||
|         line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; |     msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0}; |     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||||
|     uint8_t address[TOX_FRIEND_ADDRESS_SIZE]; |     char bin_id[TOX_ADDRESS_SIZE]; | ||||||
|     tox_get_address(m, address); |     tox_self_get_address(m, (uint8_t *) bin_id); | ||||||
|  |  | ||||||
|     size_t i; |     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID."); | ||||||
|     for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) { |         return; | ||||||
|         char xx[3]; |  | ||||||
|         snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff); |  | ||||||
|         strcat(id, xx); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||||
|  |     char bin_id[TOX_ADDRESS_SIZE]; | ||||||
|  |     tox_self_get_address(m, (uint8_t *) bin_id); | ||||||
|  |  | ||||||
|  |     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|  |     tox_self_get_name(m, (uint8_t *) nick); | ||||||
|  |     size_t nick_len = tox_self_get_name_size(m); | ||||||
|  |     nick[nick_len] = '\0'; | ||||||
|  |  | ||||||
|  |     size_t data_file_len = strlen(DATA_FILE); | ||||||
|  |     char dir[data_file_len + 1]; | ||||||
|  |     size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir); | ||||||
|  |  | ||||||
|  | #ifdef QRPNG | ||||||
|  |  | ||||||
|  |     if (argc == 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'"); | ||||||
|  |         return; | ||||||
|  |     } else if (!strcmp(argv[1], "txt")) { | ||||||
|  |  | ||||||
|  | #endif /* QRPNG */ | ||||||
|  |         char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1]; | ||||||
|  |         snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); | ||||||
|  |  | ||||||
|  |         if (ID_to_QRcode_txt(id_string, qr_path) == -1) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||||
|  |  | ||||||
|  | #ifdef QRPNG | ||||||
|  |     } else if (!strcmp(argv[1], "png")) { | ||||||
|  |         char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1]; | ||||||
|  |         snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); | ||||||
|  |  | ||||||
|  |         if (ID_to_QRcode_png(id_string, qr_path) == -1) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||||
|  |  | ||||||
|  |     } else { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #endif /* QRPNG */ | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |  | ||||||
|  |  | ||||||
|     /* check arguments */ |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         errmsg = "Invalid name."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t *nick = argv[1]; |     char nick[MAX_STR_SIZE]; | ||||||
|     int len = strlen(nick); |     size_t len = 0; | ||||||
|  |  | ||||||
|     if (nick[0] == '\"') { |     if (argv[1][0] == '\"') {    /* remove opening and closing quotes */ | ||||||
|         ++nick; |         snprintf(nick, sizeof(nick), "%s", &argv[1][1]); | ||||||
|         len -= 2; |         len = strlen(nick) - 1; | ||||||
|         nick[len] = L'\0'; |         nick[len] = '\0'; | ||||||
|  |     } else { | ||||||
|  |         snprintf(nick, sizeof(nick), "%s", argv[1]); | ||||||
|  |         len = strlen(nick); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!valid_nick(nick)) { |     if (!valid_nick(nick)) { | ||||||
|         errmsg = "Invalid name."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); |     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||||
|  |     nick[len] = '\0'; | ||||||
|  |  | ||||||
|     nick[len] = L'\0'; |     tox_self_set_name(m, (uint8_t *) nick, len, NULL); | ||||||
|  |     prompt_update_nick(prompt, nick); | ||||||
|     tox_set_name(m, nick, len); |     set_nick_all_groups(m, nick, len); | ||||||
|     prompt_update_nick(prompt, nick, len); |  | ||||||
|  |  | ||||||
|     store_data(m, DATA_FILE); |     store_data(m, DATA_FILE); | ||||||
| } | } | ||||||
|  |  | ||||||
| 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]) | ||||||
| { | { | ||||||
|     uint8_t *errmsg; |  | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         errmsg = "Wrong number of arguments."; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t *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(); | ||||||
|  |  | ||||||
|  |     if (argc > 0) { | ||||||
|  |         nospam = strtol(argv[1], NULL, 16); | ||||||
|  |  | ||||||
|  |         if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value."); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     msg[strlen(++msg) - 1] = L'\0'; |     uint32_t old_nospam = tox_self_get_nospam(m); | ||||||
|     uint16_t len = strlen(msg); |     tox_self_set_nospam(m, (uint32_t) nospam); | ||||||
|     tox_set_status_message(m, msg, len); |  | ||||||
|     prompt_update_statusmessage(prompt, msg, len); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:"); | ||||||
|  |     cmd_myid(window, self, m, 0, NULL); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||||
|  |                   "Any services that relied on your old ID will need to be updated manually."); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'", | ||||||
|  |                   old_nospam); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     struct history *hst = self->chatwin->hst; |     help_init_menu(self); | ||||||
|     line_info_clear(hst); |  | ||||||
|     struct line_info *start = hst->line_start; |  | ||||||
|  |  | ||||||
|     uint8_t *msg = "Global commands:"; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); |  | ||||||
|  |  | ||||||
| #ifdef _SUPPORT_AUDIO |  | ||||||
| #define NUMLINES 14 |  | ||||||
| #else |  | ||||||
| #define NUMLINES 12 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     uint8_t lines[NUMLINES][MAX_STR_SIZE] = { |  | ||||||
|  |  | ||||||
|         { "    /add <id> <msg>            : Add friend with optional message"               }, |  | ||||||
|         { "    /accept <n>                : Accept friend request"                          }, |  | ||||||
|         { "    /connect <ip> <port> <key> : Manually connect to a DHT node"                 }, |  | ||||||
|         { "    /status <type> <msg>       : Set status with optional note"                  }, |  | ||||||
|         { "    /note <msg>                : Set a personal note"                            }, |  | ||||||
|         { "    /nick <nick>               : Set your nickname"                              }, |  | ||||||
|         { "    /log <on> or <off>         : Enable/disable logging"                         }, |  | ||||||
|         { "    /groupchat                 : Create a group chat"                            }, |  | ||||||
|         { "    /myid                      : Print your ID"                                  }, |  | ||||||
|         { "    /help                      : Print this message again"                       }, |  | ||||||
|         { "    /clear                     : Clear window history"                           }, |  | ||||||
|         { "    /quit or /exit             : Exit Toxic"                                     }, |  | ||||||
| #ifdef _SUPPORT_AUDIO |  | ||||||
|         { "    /lsdev <type>              : List devices where type: in|out"                }, |  | ||||||
|         { "    /sdev <type> <id>          : Set active device"                              }, |  | ||||||
| #endif /* _SUPPORT_AUDIO */ |  | ||||||
|  |  | ||||||
|     }; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < NUMLINES; ++i) |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0); |  | ||||||
|  |  | ||||||
|     msg = " * Argument messages must be enclosed in quotation marks."; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); |  | ||||||
|     msg = " * Use ctrl-o and ctrl-p to navigate through the tabs."; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, "", SYS_MSG, 0, 0); |  | ||||||
|  |  | ||||||
|     hst->line_start = start; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| 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]) | ||||||
| @@ -441,50 +659,85 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     exit_toxic_success(m); |     exit_toxic_success(m); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint8_t *msg = NULL; |     if (FrndRequests.num_requests == 0) { | ||||||
|     uint8_t *errmsg; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests."); | ||||||
|  |  | ||||||
|     if (argc >= 2) { |  | ||||||
|         msg = argv[2]; |  | ||||||
|  |  | ||||||
|         if (msg[0] != '\"') { |  | ||||||
|             errmsg = "Note must be enclosed in quotes."; |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } else if (argc != 1) { |  | ||||||
|         errmsg = "Wrong number of arguments."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char *status = argv[1]; |     int i, j; | ||||||
|     str_to_lower(status); |     int count = 0; | ||||||
|     int len = strlen(status); |  | ||||||
|  |  | ||||||
|     TOX_USERSTATUS status_kind; |     for (i = 0; i < FrndRequests.max_idx; ++i) { | ||||||
|  |         if (!FrndRequests.request[i].active) | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|     if (!strcmp(status, "online")) |         char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0}; | ||||||
|         status_kind = TOX_USERSTATUS_NONE; |  | ||||||
|     else if (!strcmp(status, "away")) |         for (j = 0; j < TOX_PUBLIC_KEY_SIZE; ++j) { | ||||||
|         status_kind = TOX_USERSTATUS_AWAY; |             char d[3]; | ||||||
|     else if (!strcmp(status, "busy")) |             snprintf(d, sizeof(d), "%02X", FrndRequests.request[i].key[j] & 0xff); | ||||||
|         status_kind = TOX_USERSTATUS_BUSY; |             strcat(id, d); | ||||||
|     else { |  | ||||||
|         errmsg = "Invalid status. Valid statuses are: online, busy and away."; |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); |  | ||||||
|         return; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     tox_set_user_status(m, status_kind); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); | ||||||
|     prompt_update_status(prompt, status_kind); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg); | ||||||
|  |  | ||||||
|     if (msg != NULL) { |         if (++count < FrndRequests.num_requests) | ||||||
|         msg[strlen(++msg) - 1] = L'\0'; /* remove opening and closing quotes */ |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
|         uint16_t len = strlen(msg); |  | ||||||
|         tox_set_status_message(m, msg, len); |  | ||||||
|         prompt_update_statusmessage(prompt, msg, len); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     bool have_note = false; | ||||||
|  |     const char *errmsg; | ||||||
|  |  | ||||||
|  |     lock_status (); | ||||||
|  |  | ||||||
|  |     if (argc >= 2) { | ||||||
|  |         have_note = true; | ||||||
|  |     } else if (argc < 1) { | ||||||
|  |         errmsg = "Require a status. Statuses are: online, busy and away."; | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||||
|  |         goto finish; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *status_str = argv[1]; | ||||||
|  |     TOX_USER_STATUS status; | ||||||
|  |  | ||||||
|  |     if (!strcasecmp(status_str, "online")) | ||||||
|  |         status = TOX_USER_STATUS_NONE; | ||||||
|  |     else if (!strcasecmp(status_str, "away")) | ||||||
|  |         status = TOX_USER_STATUS_AWAY; | ||||||
|  |     else if (!strcasecmp(status_str, "busy")) | ||||||
|  |         status = TOX_USER_STATUS_BUSY; | ||||||
|  |     else { | ||||||
|  |         errmsg = "Invalid status. Valid statuses are: online, busy and away."; | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||||
|  |         goto finish; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     tox_self_set_status(m, status); | ||||||
|  |     prompt_update_status(prompt, status); | ||||||
|  |     set_status_all_groups(m, status); | ||||||
|  |  | ||||||
|  |     if (have_note) { | ||||||
|  |         if (argv[2][0] != '\"') { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes."); | ||||||
|  |             goto finish; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /* remove opening and closing quotes */ | ||||||
|  |         char msg[MAX_STR_SIZE]; | ||||||
|  |         snprintf(msg, sizeof(msg), "%s", &argv[2][1]); | ||||||
|  |         int len = strlen(msg) - 1; | ||||||
|  |         msg[len] = '\0'; | ||||||
|  |  | ||||||
|  |         prompt_update_statusmessage(prompt, m, msg); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | finish: | ||||||
|  |     unlock_status (); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -20,30 +20,41 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef _global_commands_h | #ifndef GLOBAL_COMMANDS_H | ||||||
| #define _global_commands_h | #define GLOBAL_COMMANDS_H | ||||||
|  |  | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  |  | ||||||
| void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  |  | ||||||
| void cmd_add_helper(ToxWindow *self, Tox *m, uint8_t *id_bin, uint8_t *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 */ | ||||||
|  |  | ||||||
|  | #endif /* #define GLOBAL_COMMANDS_H */ | ||||||
|   | |||||||
							
								
								
									
										727
									
								
								src/group_commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										727
									
								
								src/group_commands.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,727 @@ | |||||||
|  | /*  group_commands.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "groupchat.h" | ||||||
|  |  | ||||||
|  | extern GroupChat groupchats[MAX_GROUPCHAT_NUM]; | ||||||
|  |  | ||||||
|  | void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     char chatid[TOX_GROUP_CHAT_ID_SIZE * 2 + 1] = {0}; | ||||||
|  |     char chat_public_key[TOX_GROUP_CHAT_ID_SIZE]; | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_STATE_QUERIES err; | ||||||
|  |  | ||||||
|  |     if (!tox_group_get_chat_id(m, self->num, (uint8_t *) chat_public_key, &err)) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve the Chat ID (error %d).", err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) { | ||||||
|  |         char xx[3]; | ||||||
|  |         snprintf(xx, sizeof(xx), "%02X", chat_public_key[i] & 0xff); | ||||||
|  |         strcat(chatid, xx); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", chatid); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *nick = argv[1]; | ||||||
|  |     uint32_t peer_id; | ||||||
|  |  | ||||||
|  |     if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_TOGGLE_IGNORE err; | ||||||
|  |  | ||||||
|  |     if (!tox_group_toggle_ignore(m, self->num, peer_id, true, &err)) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to ignore %s (error %d).", nick, err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char timefrmt[TIME_STR_SIZE]; | ||||||
|  |     get_time_str(timefrmt, sizeof(timefrmt)); | ||||||
|  |  | ||||||
|  |     line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- Ignoring %s", nick); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void cmd_kickban_helper(ToxWindow *self, Tox *m, const char *nick, bool set_ban) | ||||||
|  | { | ||||||
|  |     uint32_t peer_id; | ||||||
|  |  | ||||||
|  |     if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *type_str = set_ban ? "ban" : "kick"; | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_MOD_REMOVE_PEER err; | ||||||
|  |     tox_group_mod_remove_peer(m, self->num, peer_id, set_ban, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_MOD_REMOVE_PEER_OK: { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "You do not have permission to %s %s.", type_str, nick); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Failed to %s %s from the group (error %d).", type_str, nick, | ||||||
|  |                           err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cmd_kickban_helper(self, m, argv[1], false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     TOX_ERR_GROUP_BAN_QUERY err; | ||||||
|  |  | ||||||
|  |     if (argc < 1) { | ||||||
|  |         size_t num_banned = tox_group_ban_get_list_size(m, self->num, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_GROUP_BAN_QUERY_OK) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list size (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (num_banned == 0) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list is empty."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         uint32_t ban_list[num_banned]; | ||||||
|  |  | ||||||
|  |         if (!tox_group_ban_get_list(m, self->num, ban_list, &err)) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         uint16_t i; | ||||||
|  |  | ||||||
|  |         for (i = 0; i < num_banned; ++i) { | ||||||
|  |             uint32_t id = ban_list[i]; | ||||||
|  |             size_t len = tox_group_ban_get_name_size(m, self->num, id, &err); | ||||||
|  |  | ||||||
|  |             if (err != TOX_ERR_GROUP_BAN_QUERY_OK) { | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name length for ban %d (error %d).", id, err); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             char tmp_nick[len]; | ||||||
|  |  | ||||||
|  |             if (!tox_group_ban_get_name(m, self->num, id, (uint8_t *) tmp_nick, &err)) { | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name for ban %d (error %d).", id, err); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             char nick[len + 1]; | ||||||
|  |             copy_tox_str(nick, sizeof(nick), tmp_nick, len); | ||||||
|  |  | ||||||
|  |             uint64_t time_set = tox_group_ban_get_time_set(m, self->num, id, &err); | ||||||
|  |  | ||||||
|  |             if (err != TOX_ERR_GROUP_BAN_QUERY_OK) { | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve timestamp for ban %d (error %d).", id, err); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             struct tm tm_set = *localtime((const time_t *) &time_set); | ||||||
|  |  | ||||||
|  |             char time_str[64]; | ||||||
|  |  | ||||||
|  |             strftime(time_str, sizeof(time_str), "%e %b %Y %H:%M:%S%p", &tm_set); | ||||||
|  |  | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "ID %d : %s [Set:%s]", id, nick, time_str); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cmd_kickban_helper(self, m, argv[1], true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int ban_id = atoi(argv[1]); | ||||||
|  |  | ||||||
|  |     if (ban_id == 0 && strcmp(argv[1], "0")) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be a non-negative interger."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_MOD_REMOVE_BAN err; | ||||||
|  |     tox_group_mod_remove_ban(m, self->num, ban_id, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_MOD_REMOVE_BAN_OK: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list entry with id %d has been removed.", ban_id); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unban peers."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID does not exist."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to remove ban list entry (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *nick = argv[1]; | ||||||
|  |     uint32_t peer_id; | ||||||
|  |  | ||||||
|  |     if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_MOD_SET_ROLE err; | ||||||
|  |     tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_MODERATOR, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_OK: { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "You do not have permission to promote moderators."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "This peer is already a moderator."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Failed to promote peer to moderator (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *nick = argv[1]; | ||||||
|  |     uint32_t peer_id; | ||||||
|  |  | ||||||
|  |     if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_MODERATOR) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not a moderator", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_MOD_SET_ROLE err; | ||||||
|  |     tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_OK: { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Nice try."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Failed to revoke moderator powers from %s (error %d).", nick, | ||||||
|  |                           err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0}; | ||||||
|  |     char pk[TOX_GROUP_PEER_PUBLIC_KEY_SIZE]; | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_SELF_QUERY err; | ||||||
|  |  | ||||||
|  |     if (!tox_group_self_get_public_key(m, self->num, (uint8_t *) pk, &err)) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch your public key (error %d)", err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) { | ||||||
|  |         char d[3]; | ||||||
|  |         snprintf(d, sizeof(d), "%02X", pk[i] & 0xff); | ||||||
|  |         strcat(pk_string, d); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", pk_string); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     const char *passwd = NULL; | ||||||
|  |     size_t len = 0; | ||||||
|  |  | ||||||
|  |     if (argc > 0) { | ||||||
|  |         passwd = argv[1]; | ||||||
|  |         len = strlen(passwd); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_FOUNDER_SET_PASSWORD err; | ||||||
|  |     tox_group_founder_set_password(m, self->num, (uint8_t *) passwd, len, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK: { | ||||||
|  |             if (len > 0) | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been set to %s.", passwd); | ||||||
|  |             else | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been unset."); | ||||||
|  |  | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length must not exceed %d.", | ||||||
|  |                           TOX_GROUP_MAX_PASSWORD_SIZE); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the password."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set password (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     int maxpeers = 0; | ||||||
|  |  | ||||||
|  |     if (argc < 1) { | ||||||
|  |         TOX_ERR_GROUP_STATE_QUERIES err; | ||||||
|  |         uint32_t maxpeers = tox_group_get_peer_limit(m, self->num, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve peer limit (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit is set to %d", maxpeers); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     maxpeers = atoi(argv[1]); | ||||||
|  |  | ||||||
|  |     if (maxpeers <= 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit must be a value greater than 0."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT err; | ||||||
|  |     tox_group_founder_set_peer_limit(m, self->num, maxpeers, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit has been set to %d.", maxpeers); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the peer limit."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the peer limit (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     const char *pstate_str = NULL; | ||||||
|  |     TOX_GROUP_PRIVACY_STATE privacy_state; | ||||||
|  |  | ||||||
|  |     if (argc < 1) { | ||||||
|  |         TOX_ERR_GROUP_STATE_QUERIES err; | ||||||
|  |         privacy_state = tox_group_get_privacy_state(m, self->num, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve privacy state (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         pstate_str = privacy_state == TOX_GROUP_PRIVACY_STATE_PRIVATE ? "private" : "public"; | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state is set to %s.", pstate_str); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pstate_str = argv[1]; | ||||||
|  |  | ||||||
|  |     if (strcasecmp(pstate_str, "private") != 0 && strcasecmp(pstate_str, "public") != 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state must be \"private\" or \"public\"."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     privacy_state = strcasecmp(pstate_str, | ||||||
|  |                                "private") == 0 ? TOX_GROUP_PRIVACY_STATE_PRIVATE : TOX_GROUP_PRIVACY_STATE_PUBLIC; | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE err; | ||||||
|  |     tox_group_founder_set_privacy_state(m, self->num, privacy_state, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state has been set to %s.", pstate_str); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the privacy state."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error setting privacy state (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *nick = argv[1]; | ||||||
|  |     uint32_t peer_id; | ||||||
|  |  | ||||||
|  |     if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_MOD_SET_ROLE err; | ||||||
|  |     tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_OBSERVER, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_OK: { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to silence %s.", nick); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to silence %s (error %d).", nick, err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *nick = argv[1]; | ||||||
|  |     uint32_t peer_id; | ||||||
|  |  | ||||||
|  |     if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_OBSERVER) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_MOD_SET_ROLE err; | ||||||
|  |     tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_OK: { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unsilence %s.", nick); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unsilence %s (error %d).", nick, err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     TOX_ERR_GROUP_RECONNECT err; | ||||||
|  |  | ||||||
|  |     if (!tox_group_reconnect(m, self->num, &err)) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to rejoin group (error %d).", err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Reconnecting to group..."); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         TOX_ERR_GROUP_STATE_QUERIES err; | ||||||
|  |         size_t tlen = tox_group_get_topic_size(m, self->num, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic length (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (tlen > 0) { | ||||||
|  |             char cur_topic[tlen]; | ||||||
|  |  | ||||||
|  |             if (!tox_group_get_topic(m, self->num, (uint8_t *) cur_topic, &err)) { | ||||||
|  |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic (error %d).", err); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             cur_topic[tlen] = '\0'; | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is set to: %s", cur_topic); | ||||||
|  |         } else { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is not set."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *topic = argv[1]; | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_TOPIC_SET err; | ||||||
|  |     tox_group_set_topic(m, self->num, (uint8_t *) topic, strlen(topic), &err); | ||||||
|  |  | ||||||
|  |     switch (err) { | ||||||
|  |         case TOX_ERR_GROUP_TOPIC_SET_OK: { | ||||||
|  |             /* handled below switch */ | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_TOPIC_SET_TOO_LONG: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic length must not exceed %d.", TOX_GROUP_MAX_TOPIC_LENGTH); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         case TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic."); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         default: { | ||||||
|  |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the topic (error %d).", err); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char timefrmt[TIME_STR_SIZE]; | ||||||
|  |     get_time_str(timefrmt, sizeof(timefrmt)); | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_SELF_QUERY sn_err; | ||||||
|  |     size_t sn_len = tox_group_self_get_name_size(m, self->num, &sn_err); | ||||||
|  |     char selfnick[sn_len]; | ||||||
|  |  | ||||||
|  |     if (!tox_group_self_get_name(m, self->num, (uint8_t *) selfnick, &sn_err)) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve your own name (error %d).", sn_err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     selfnick[sn_len] = '\0'; | ||||||
|  |  | ||||||
|  |     line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, MAGENTA, "-!- You set the topic to: %s", topic); | ||||||
|  |  | ||||||
|  |     char tmp_event[MAX_STR_SIZE]; | ||||||
|  |     snprintf(tmp_event, sizeof(tmp_event), "set topic to %s", topic); | ||||||
|  |     write_to_log(tmp_event, selfnick, self->chatwin->log, true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *nick = argv[1]; | ||||||
|  |     uint32_t peer_id; | ||||||
|  |  | ||||||
|  |     if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     TOX_ERR_GROUP_TOGGLE_IGNORE err; | ||||||
|  |  | ||||||
|  |     if (!tox_group_toggle_ignore(m, self->num, peer_id, false, &err)) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unignore %s (error %d).", nick, err); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char timefrmt[TIME_STR_SIZE]; | ||||||
|  |     get_time_str(timefrmt, sizeof(timefrmt)); | ||||||
|  |  | ||||||
|  |     line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- You are no longer ignoring %s", nick); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     if (argc < 1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     GroupChat *chat = &groupchats[self->num]; | ||||||
|  |  | ||||||
|  |     if (!chat) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois failed."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *nick = argv[1]; | ||||||
|  |     uint32_t peer_id; | ||||||
|  |  | ||||||
|  |     if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int peer_index = get_peer_index(self->num, peer_id); | ||||||
|  |  | ||||||
|  |     if (peer_index < 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois failed."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *status_str = "Online"; | ||||||
|  |  | ||||||
|  |     if (chat->peer_list[peer_index].status == TOX_USER_STATUS_BUSY) | ||||||
|  |         status_str = "Busy"; | ||||||
|  |     else if (chat->peer_list[peer_index].status == TOX_USER_STATUS_AWAY) | ||||||
|  |         status_str = "Away"; | ||||||
|  |  | ||||||
|  |     const char *role_str = "User"; | ||||||
|  |  | ||||||
|  |     if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_FOUNDER) | ||||||
|  |         role_str = "Founder"; | ||||||
|  |     else if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_MODERATOR) | ||||||
|  |         role_str = "Moderator"; | ||||||
|  |     else if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_OBSERVER) | ||||||
|  |         role_str = "Observer"; | ||||||
|  |  | ||||||
|  |     char last_seen_str[128]; | ||||||
|  |     get_elapsed_time_str_2(last_seen_str, sizeof(last_seen_str), get_unix_time() - chat->peer_list[peer_index].last_active); | ||||||
|  |  | ||||||
|  |     char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0}; | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) { | ||||||
|  |         char d[3]; | ||||||
|  |         snprintf(d, sizeof(d), "%02X", chat->peer_list[peer_index].public_key[i] & 0xff); | ||||||
|  |         strcat(pk_string, d); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois for %s", nick); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Role: %s", role_str); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Status: %s", status_str); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Last active: %s", last_seen_str); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Public key: %s", pk_string); | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								src/group_commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/group_commands.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | /*  group_commands.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef GROUP_COMMANDS_H | ||||||
|  | #define GROUP_COMMANDS_H | ||||||
|  |  | ||||||
|  | #include "windows.h" | ||||||
|  | #include "toxic.h" | ||||||
|  |  | ||||||
|  | void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_prune(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  |  | ||||||
|  | #endif  /* GROUP_COMMANDS_H */ | ||||||
							
								
								
									
										1573
									
								
								src/groupchat.c
									
									
									
									
									
								
							
							
						
						
									
										1573
									
								
								src/groupchat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -20,29 +20,48 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef _groupchat_h | #ifndef GROUPCHAT_H | ||||||
| #define _groupchat_h | #define GROUPCHAT_H | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| #define SIDEBAR_WIDTH 16 | #define SIDEBAR_WIDTH 16 | ||||||
| #define SDBAR_OFST 2    /* Offset for the peer number box at the top of the statusbar */ | #define SDBAR_OFST 2    /* Offset for the peer number box at the top of the statusbar */ | ||||||
| #define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM | #define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2 | ||||||
|  | #define GROUP_EVENT_WAIT 3 | ||||||
|  |  | ||||||
|  | struct GroupPeer { | ||||||
|  |     bool       active; | ||||||
|  |     char       name[TOX_MAX_NAME_LENGTH]; | ||||||
|  |     size_t     name_length; | ||||||
|  |     uint32_t   peer_id; | ||||||
|  |     uint8_t    public_key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE]; | ||||||
|  |     TOX_USER_STATUS status; | ||||||
|  |     TOX_GROUP_ROLE  role; | ||||||
|  |     uint64_t   last_active; | ||||||
|  | }; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|  |     struct GroupPeer *peer_list; | ||||||
|  |     char       *name_list;    /* List of peer names, needed for tab completion */ | ||||||
|  |     uint32_t   num_peers;     /* Number of peers in the chat/name_list array */ | ||||||
|  |     uint32_t   max_idx;       /* Maximum peer list index - 1 */ | ||||||
|  |     uint32_t   groupnumber; | ||||||
|     int        chatwin; |     int        chatwin; | ||||||
|     bool       active; |     bool       active; | ||||||
|     int num_peers; |     uint64_t   time_connected;    /* The time we successfully connected to the group */ | ||||||
|     int        side_pos;     /* current position of the sidebar - used for scrolling up and down */ |     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; | } GroupChat; | ||||||
|  |  | ||||||
| void kill_groupchat_window(ToxWindow *self); | void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum); | ||||||
| int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum); | int init_groupchat_win(Tox *m, uint32_t groupnum, const char *groupname, size_t length); | ||||||
| ToxWindow new_group_chat(Tox *m, int groupnum); | void set_nick_all_groups(Tox *m, const char *nick, size_t length); | ||||||
|  | void set_status_all_groups(Tox *m, uint8_t status); | ||||||
|  | int group_get_nick_peer_id(uint32_t groupnum, const char *nick, uint32_t *peer_id); | ||||||
|  | int get_peer_index(uint32_t groupnum, uint32_t peer_id); | ||||||
|  |  | ||||||
| #endif /* #define _groupchat_h */ | /* destroys and re-creates groupchat window */ | ||||||
|  | void redraw_groupchat_win(ToxWindow *self); | ||||||
|  |  | ||||||
|  | #endif /* #define GROUPCHAT_H */ | ||||||
|   | |||||||
							
								
								
									
										409
									
								
								src/help.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										409
									
								
								src/help.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,409 @@ | |||||||
|  | /*  help.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #include "windows.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "help.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  |  | ||||||
|  | #define HELP_MENU_HEIGHT 9 | ||||||
|  | #define HELP_MENU_WIDTH 26 | ||||||
|  |  | ||||||
|  | void help_init_menu(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     if (self->help->win) | ||||||
|  |         delwin(self->help->win); | ||||||
|  |  | ||||||
|  |     int y2, x2; | ||||||
|  |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3); | ||||||
|  |     self->help->active = true; | ||||||
|  |     self->help->type = HELP_MENU; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_exit(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     delwin(self->help->win); | ||||||
|  |     memset(self->help, 0, sizeof(Help)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_init_window(ToxWindow *self, int height, int width) | ||||||
|  | { | ||||||
|  |     if (self->help->win) | ||||||
|  |         delwin(self->help->win); | ||||||
|  |  | ||||||
|  |     int y2, x2; | ||||||
|  |     getmaxyx(stdscr, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     height = MIN(height, y2); | ||||||
|  |     width = MIN(width, x2); | ||||||
|  |  | ||||||
|  |     self->help->win = newwin(height, width, 0, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_draw_menu(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     WINDOW *win = self->help->win; | ||||||
|  |  | ||||||
|  |     wmove(win, 1, 1); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |     wprintw(win, "       Help Menu\n"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, " g"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "lobal commands\n"); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, " c"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "hat commands\n"); | ||||||
|  |  | ||||||
|  |     wprintw(win, " g"); | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "r"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "oup commands\n"); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, " f"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "riendlist controls\n"); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, " k"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "ey bindings\n"); | ||||||
|  |  | ||||||
|  |     wprintw(win, " e"); | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "x"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "it menu\n"); | ||||||
|  |  | ||||||
|  |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|  |     wrefresh(win); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_draw_bottom_menu(WINDOW *win) | ||||||
|  | { | ||||||
|  |     int y2, x2; | ||||||
|  |     getmaxyx(win, y2, x2); | ||||||
|  |     (void) x2; | ||||||
|  |  | ||||||
|  |     wmove(win, y2 - 2, 1); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, " m"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "ain menu |"); | ||||||
|  |  | ||||||
|  |     wprintw(win, " e"); | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "x"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|  |     wprintw(win, "it"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_draw_global(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     WINDOW *win = self->help->win; | ||||||
|  |  | ||||||
|  |     wmove(win, 1, 1); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |     wprintw(win, "Global Commands:\n"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  /add <addr> <msg>          : Add contact with optional message\n"); | ||||||
|  |     wprintw(win, "  /accept <id>               : Accept friend request\n"); | ||||||
|  |     wprintw(win, "  /avatar <path>             : Set an avatar (leave path empty to unset)\n"); | ||||||
|  |     wprintw(win, "  /decline <id>              : Decline friend request\n"); | ||||||
|  |     wprintw(win, "  /requests                  : List pending friend requests\n"); | ||||||
|  |     wprintw(win, "  /connect <ip> <port> <key> : Manually connect to a DHT node\n"); | ||||||
|  |     wprintw(win, "  /status <type> <msg>       : Set status with optional note\n"); | ||||||
|  |     wprintw(win, "  /note <msg>                : Set a personal note\n"); | ||||||
|  |     wprintw(win, "  /group                     : Create a group chat\n"); | ||||||
|  |     wprintw(win, "  /join <chat id> <passwd>   : Join a group chat with optional password\n"); | ||||||
|  |     wprintw(win, "  /nick <nick>               : Set your nickname\n"); | ||||||
|  |     wprintw(win, "  /nospam <value>            : Change part of your Tox ID to stop spam\n"); | ||||||
|  |     wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n"); | ||||||
|  |     wprintw(win, "  /myid                      : Print your Tox ID\n"); | ||||||
|  | #ifdef QRPNG | ||||||
|  |     wprintw(win, "  /myqr <txt> or <png>       : Print your Tox ID's QR code to a file.\n"); | ||||||
|  | #else | ||||||
|  |     wprintw(win, "  /myqr                      : Print your Tox ID's QR code to a file.\n"); | ||||||
|  | #endif /* QRPNG */ | ||||||
|  |     wprintw(win, "  /clear                     : Clear window history\n"); | ||||||
|  |     wprintw(win, "  /close                     : Close the current chat window\n"); | ||||||
|  |     wprintw(win, "  /quit or /exit             : Exit Toxic\n"); | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, "\n Audio:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  /lsdev <type>              : List devices where type: in|out\n"); | ||||||
|  |     wprintw(win, "  /sdev <type> <id>          : Set active device\n"); | ||||||
|  | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, "\n Video:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  /lsvdev <type>             : List video devices where type: in|out\n"); | ||||||
|  |     wprintw(win, "  /svdev <type> <id>         : Set active video device\n"); | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|  |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|  |     wrefresh(win); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_draw_chat(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     WINDOW *win = self->help->win; | ||||||
|  |  | ||||||
|  |     wmove(win, 1, 1); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |     wprintw(win, "Chat Commands:\n"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  /gaccept <password>        : Accept a group invite with optional password\n"); | ||||||
|  |     wprintw(win, "  /sendfile <path>           : Send a file\n"); | ||||||
|  |     wprintw(win, "  /savefile <id>             : Receive a file\n"); | ||||||
|  |     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, "\n Audio:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  /call                      : Audio call\n"); | ||||||
|  |     wprintw(win, "  /answer                    : Answer incoming call\n"); | ||||||
|  |     wprintw(win, "  /reject                    : Reject incoming call\n"); | ||||||
|  |     wprintw(win, "  /hangup                    : Hangup active call\n"); | ||||||
|  |     wprintw(win, "  /sdev <type> <id>          : Change active device\n"); | ||||||
|  |     wprintw(win, "  /mute <type>               : Mute active device if in call\n"); | ||||||
|  |     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n"); | ||||||
|  | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, "\n Video:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |     wprintw(win, "  /video                     : Toggle video call\n"); | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|  |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|  |     wrefresh(win); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_draw_keys(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     WINDOW *win = self->help->win; | ||||||
|  |  | ||||||
|  |     wmove(win, 1, 1); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |     wprintw(win, "Key bindings:\n"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  Ctrl+O and Ctrl+P         : Navigate through the tabs\n"); | ||||||
|  |     wprintw(win, "  Page Up and Page Down     : Scroll window history one line\n"); | ||||||
|  |     wprintw(win, "  Ctrl+F and Ctrl+V         : Scroll window history half a page\n"); | ||||||
|  |     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); | ||||||
|  |     wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n"); | ||||||
|  |     wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n"); | ||||||
|  |     wprintw(win, "  Ctrl+J                    : Insert new line\n"); | ||||||
|  |     wprintw(win, "  Ctrl+T                    : Toggle paste mode\n\n"); | ||||||
|  |     wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n"); | ||||||
|  |  | ||||||
|  |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|  |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|  |     wrefresh(win); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_draw_group(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     WINDOW *win = self->help->win; | ||||||
|  |  | ||||||
|  |     wmove(win, 1, 1); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |     wprintw(win, "Group commands:\n"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  /chatid                    : Print the group chat id to share with others\n"); | ||||||
|  |     wprintw(win, "  /mykey                     : Print your group public key\n"); | ||||||
|  |     wprintw(win, "  /ignore <nick>             : Ignore peer\n"); | ||||||
|  |     wprintw(win, "  /unignore <nick>           : Unignore peer \n"); | ||||||
|  |     wprintw(win, "  /rejoin                    : Rejoin the group\n"); | ||||||
|  |     wprintw(win, "  /topic <msg>               : Set group topic (show current topic if no msg)\n"); | ||||||
|  |     wprintw(win, "  /whisper <nick> <msg>      : Send private message to nick\n"); | ||||||
|  |     wprintw(win, "  /whois <nick>              : Display info about nick.\n"); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, " Moderator commands:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |     wprintw(win, "  /kick <nick>               : Kick peer\n"); | ||||||
|  |     wprintw(win, "  /ban <nick>                : Ban peer (leave nick blank to see ban list)\n"); | ||||||
|  |     wprintw(win, "  /unban <Ban ID>            : Unban entry\n"); | ||||||
|  |     wprintw(win, "  /silence <nick>            : Silences peer for the entire group\n"); | ||||||
|  |     wprintw(win, "  /unsilence <nick>          : Unsilences peer\n"); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, " Founder commands:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |     wprintw(win, "  /mod <nick>                : Promote peer to moderator\n"); | ||||||
|  |     wprintw(win, "  /unmod <nick>              : Demote moderator to normal user\n"); | ||||||
|  |     wprintw(win, "  /passwd <password>         : Set group password (leave blank to unset)\n"); | ||||||
|  |     wprintw(win, "  /peerlimit <num>           : Set group peer limit\n"); | ||||||
|  |     wprintw(win, "  /privacy <state>           : Set group privacy state: private|public\n"); | ||||||
|  |  | ||||||
|  |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|  |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|  |     wrefresh(win); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void help_draw_contacts(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     WINDOW *win = self->help->win; | ||||||
|  |  | ||||||
|  |     wmove(win, 1, 1); | ||||||
|  |  | ||||||
|  |     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |     wprintw(win, "Friendlist controls:\n"); | ||||||
|  |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  Up and Down arrows            : Scroll through list\n"); | ||||||
|  |     wprintw(win, "  Right and Left arrows         : Switch between friendlist and blocked list\n"); | ||||||
|  |     wprintw(win, "  Enter                         : Open a chat window with selected contact\n"); | ||||||
|  |     wprintw(win, "  Delete                        : Permanently delete a contact\n"); | ||||||
|  |     wprintw(win, "  B                             : Block or unblock a contact\n"); | ||||||
|  |  | ||||||
|  |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|  |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|  |     wrefresh(win); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void help_onKey(ToxWindow *self, wint_t key) | ||||||
|  | { | ||||||
|  |     switch (key) { | ||||||
|  |         case 'x': | ||||||
|  |         case T_KEY_ESC: | ||||||
|  |             help_exit(self); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case 'c': | ||||||
|  | #ifdef VIDEO | ||||||
|  |             help_init_window(self, 21, 80); | ||||||
|  | #elif AUDIO | ||||||
|  |             help_init_window(self, 18, 80); | ||||||
|  | #else | ||||||
|  |             help_init_window(self, 10, 80); | ||||||
|  | #endif | ||||||
|  |             self->help->type = HELP_CHAT; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case 'g': | ||||||
|  | #ifdef VIDEO | ||||||
|  |             help_init_window(self, 30, 80); | ||||||
|  | #elif AUDIO | ||||||
|  |             help_init_window(self, 26, 80); | ||||||
|  | #else | ||||||
|  |             help_init_window(self, 22, 80); | ||||||
|  | #endif | ||||||
|  |             self->help->type = HELP_GLOBAL; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case 'r': | ||||||
|  |             help_init_window(self, 25, 80); | ||||||
|  |             self->help->type = HELP_GROUP; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case 'f': | ||||||
|  |             help_init_window(self, 10, 80); | ||||||
|  |             self->help->type = HELP_CONTACTS; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case 'k': | ||||||
|  |             help_init_window(self, 15, 80); | ||||||
|  |             self->help->type = HELP_KEYS; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case 'm': | ||||||
|  |             help_init_menu(self); | ||||||
|  |             self->help->type = HELP_MENU; | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void help_onDraw(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     curs_set(0); | ||||||
|  |  | ||||||
|  |     switch (self->help->type) { | ||||||
|  |         case HELP_MENU: | ||||||
|  |             help_draw_menu(self); | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         case HELP_CHAT: | ||||||
|  |             help_draw_chat(self); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case HELP_GLOBAL: | ||||||
|  |             help_draw_global(self); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case HELP_KEYS: | ||||||
|  |             help_draw_keys(self); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case HELP_CONTACTS: | ||||||
|  |             help_draw_contacts(self); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case HELP_GROUP: | ||||||
|  |             help_draw_group(self); | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| /*  dns.c
 | /*  help.h
 | ||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
| @@ -20,13 +20,23 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| /* Does DNS lookup for addr and puts resulting tox id in id_bin.
 | #ifndef HELP_H | ||||||
|    Return 0 on success, -1 on failure. */ | #define HELP_H | ||||||
| 
 | 
 | ||||||
| #ifndef _dns_h | #include "toxic.h" | ||||||
| #define _dns_h | #include "windows.h" | ||||||
| 
 | 
 | ||||||
| /* creates new thread for dns3 lookup. Only allows one lookup at a time. */ | typedef enum { | ||||||
| void dns3_lookup(ToxWindow *self, Tox *m, uint8_t *id_bin, uint8_t *addr, uint8_t *msg); |     HELP_MENU, | ||||||
|  |     HELP_GLOBAL, | ||||||
|  |     HELP_CHAT, | ||||||
|  |     HELP_GROUP, | ||||||
|  |     HELP_KEYS, | ||||||
|  |     HELP_CONTACTS, | ||||||
|  | } HELP_TYPES; | ||||||
| 
 | 
 | ||||||
| #endif /* #define _dns_h */ | void help_onDraw(ToxWindow *self); | ||||||
|  | void help_init_menu(ToxWindow *self); | ||||||
|  | void help_onKey(ToxWindow *self, wint_t key); | ||||||
|  | 
 | ||||||
|  | #endif /* #define HELP_H */ | ||||||
							
								
								
									
										287
									
								
								src/input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								src/input.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,287 @@ | |||||||
|  | /*  input.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _GNU_SOURCE | ||||||
|  | #define _GNU_SOURCE    /* needed for wcwidth() */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include <wchar.h> | ||||||
|  |  | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "toxic_strings.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "notify.h" | ||||||
|  | #include "groupchat.h" | ||||||
|  | #include "settings.h" | ||||||
|  |  | ||||||
|  | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
|  | /* add a char to input field and buffer */ | ||||||
|  | void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     /* this is the only place we need to do this check */ | ||||||
|  |     if (key == '\n') | ||||||
|  |         key = L'¶'; | ||||||
|  |  | ||||||
|  |     int cur_len = wcwidth(key); | ||||||
|  |  | ||||||
|  |     if (cur_len == -1) { | ||||||
|  |         sound_notify(self, notif_error, 0, NULL); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (add_char_to_buf(ctx, key) == -1) { | ||||||
|  |         sound_notify(self, notif_error, 0, NULL); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (x + cur_len >= mx_x) { | ||||||
|  |         int s_len = wcwidth(ctx->line[ctx->start]); | ||||||
|  |         ctx->start += 1 + MAX(0, cur_len - s_len); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* delete a char via backspace key from input field and buffer */ | ||||||
|  | static void input_backspace(ToxWindow *self, int x, int mx_x) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     if (del_char_buf_bck(ctx) == -1) { | ||||||
|  |         sound_notify(self, notif_error, 0, NULL); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0; | ||||||
|  |     int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0; | ||||||
|  |  | ||||||
|  |     if (ctx->start && (x >= mx_x - cur_len)) | ||||||
|  |         ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); | ||||||
|  |     else if (ctx->start) | ||||||
|  |         ctx->start = MAX(0, ctx->start - cur_len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* delete a char via delete key from input field and buffer */ | ||||||
|  | static void input_delete(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     if (del_char_buf_frnt(self->chatwin) == -1) | ||||||
|  |         sound_notify(self, notif_error, 0, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* delete last typed word */ | ||||||
|  | static void input_del_word(ToxWindow *self, int x, int mx_x) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     if (del_word_buf(ctx) == -1) { | ||||||
|  |         sound_notify(self, notif_error, 0, NULL); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* deletes entire line before cursor from input field and buffer */ | ||||||
|  | static void input_discard(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     if (discard_buf(self->chatwin) == -1) | ||||||
|  |         sound_notify(self, notif_error, 0, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* deletes entire line after cursor from input field and buffer */ | ||||||
|  | static void input_kill(ChatContext *ctx) | ||||||
|  | { | ||||||
|  |     if (kill_buf(ctx) == -1) | ||||||
|  |         sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void input_yank(ToxWindow *self, int x, int mx_x) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     if (yank_buf(ctx) == -1) { | ||||||
|  |         sound_notify(self, notif_error, 0, NULL); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int yank_cols = MAX(0, wcswidth(ctx->yank, ctx->yank_len)); | ||||||
|  |  | ||||||
|  |     if (x + yank_cols >= mx_x) { | ||||||
|  |         int rmdr = MAX(0, (x + yank_cols) - mx_x); | ||||||
|  |         int s_len = MAX(0, wcswidth(&ctx->line[ctx->start], rmdr)); | ||||||
|  |         ctx->start += s_len + 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* moves cursor/line position to end of line in input field and buffer */ | ||||||
|  | static void input_mv_end(ToxWindow *self, int y, int mx_x) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     ctx->pos = ctx->len; | ||||||
|  |  | ||||||
|  |     int wlen = 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)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* moves cursor/line position to start of line in input field and buffer */ | ||||||
|  | static void input_mv_home(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     if (ctx->pos <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     ctx->pos = 0; | ||||||
|  |     ctx->start = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* moves cursor/line position left in input field and buffer */ | ||||||
|  | static void input_mv_left(ToxWindow *self, int x, int mx_x) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     if (ctx->pos <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0; | ||||||
|  |     int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0; | ||||||
|  |  | ||||||
|  |     --ctx->pos; | ||||||
|  |  | ||||||
|  |     if (ctx->start && (x >= mx_x - cur_len)) | ||||||
|  |         ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); | ||||||
|  |     else if (ctx->start) | ||||||
|  |         ctx->start = MAX(0, ctx->start - cur_len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* moves cursor/line position right in input field and buffer */ | ||||||
|  | static void input_mv_right(ToxWindow *self, int x, int mx_x) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     if (ctx->pos >= ctx->len) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     ++ctx->pos; | ||||||
|  |  | ||||||
|  |     int cur_len = wcwidth(ctx->line[ctx->pos - 1]); | ||||||
|  |  | ||||||
|  |     if (x + cur_len >= mx_x) { | ||||||
|  |         int s_len = wcwidth(ctx->line[ctx->start]); | ||||||
|  |         ctx->start += 1 + MAX(0, cur_len - s_len); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* puts a line history item in input field and buffer */ | ||||||
|  | static void input_history(ToxWindow *self, wint_t key, int mx_x) | ||||||
|  | { | ||||||
|  |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     fetch_hist_item(ctx, key); | ||||||
|  |     int wlen = 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. | ||||||
|  |    return true if key matches a function, false otherwise */ | ||||||
|  | bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||||
|  | { | ||||||
|  |     bool match = true; | ||||||
|  |  | ||||||
|  |     switch (key) { | ||||||
|  |         case 0x7f: | ||||||
|  |         case KEY_BACKSPACE: | ||||||
|  |             input_backspace(self, x, mx_x); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case KEY_DC: | ||||||
|  |             input_delete(self); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case T_KEY_DISCARD: | ||||||
|  |             input_discard(self); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case T_KEY_KILL: | ||||||
|  |             input_kill(self->chatwin); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case T_KEY_C_Y: | ||||||
|  |             input_yank(self, x, mx_x); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case T_KEY_C_W: | ||||||
|  |             input_del_word(self, x, mx_x); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case KEY_HOME: | ||||||
|  |         case T_KEY_C_A: | ||||||
|  |             input_mv_home(self); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case KEY_END: | ||||||
|  |         case T_KEY_C_E: | ||||||
|  |             input_mv_end(self, y, mx_x); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case KEY_LEFT: | ||||||
|  |             input_mv_left(self, x, mx_x); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case KEY_RIGHT: | ||||||
|  |             input_mv_right(self, x, mx_x); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case KEY_UP: | ||||||
|  |         case KEY_DOWN: | ||||||
|  |             input_history(self, key, mx_x); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case T_KEY_C_L: | ||||||
|  |             force_refresh(self->chatwin->history); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         default: | ||||||
|  |             match = false; | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* TODO: this special case is ugly. | ||||||
|  |        maybe convert entire function to if/else and make them all customizable keys? */ | ||||||
|  |     if (!match) { | ||||||
|  |         if (key == user_settings->key_toggle_peerlist) { | ||||||
|  |             if (self->is_groupchat) { | ||||||
|  |                 self->show_peerlist ^= 1; | ||||||
|  |                 redraw_groupchat_win(self); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             match = true; | ||||||
|  |         } else if (key == user_settings->key_toggle_pastemode) { | ||||||
|  |             self->chatwin->pastemode ^= 1; | ||||||
|  |             match = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return match; | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								src/input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/input.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | /*  input.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef INPUT_H | ||||||
|  | #define INPUT_H | ||||||
|  |  | ||||||
|  | /* add a char to input field and buffer for given chatcontext */ | ||||||
|  | void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y); | ||||||
|  |  | ||||||
|  | /* Handles non-printable input keys that behave the same for all types of chat windows. | ||||||
|  |    return true if key matches a function, false otherwise */ | ||||||
|  | bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y); | ||||||
|  |  | ||||||
|  | #endif /* #define INPUT_H */ | ||||||
							
								
								
									
										286
									
								
								src/line_info.c
									
									
									
									
									
								
							
							
						
						
									
										286
									
								
								src/line_info.c
									
									
									
									
									
								
							| @@ -20,51 +20,57 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "groupchat.h" | #include "groupchat.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
|  | #include "notify.h" | ||||||
|  | #include "message_queue.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| void line_info_init(struct history *hst) | void line_info_init(struct history *hst) | ||||||
| { | { | ||||||
|     hst->line_root = malloc(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); | ||||||
|  |  | ||||||
|     memset(hst->line_root, 0, sizeof(struct line_info)); |  | ||||||
|     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_sz = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* resets line_start */ | /* 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; | ||||||
|  |  | ||||||
|  |     if (line->prev == NULL) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     int y2, x2; |     int y2, x2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     struct line_info *line = hst->line_end; |     int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; | ||||||
|     uint16_t lncnt = 0; |     int top_offst = self->is_chat || self->is_prompt ? 2 : 0; | ||||||
|     int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; |  | ||||||
|     int top_offst = self->is_chat ? 3 : 0; |  | ||||||
|     int max_y = (y2 - CHATBOX_HEIGHT - top_offst); |     int max_y = (y2 - CHATBOX_HEIGHT - top_offst); | ||||||
|  |  | ||||||
|     while (line->prev && lncnt < max_y) { |     int curlines = 0; | ||||||
|  |     int nxtlines = line->newlines + (line->len / (x2 - side_offst)); | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |         curlines += 1 + nxtlines; | ||||||
|         line = line->prev; |         line = line->prev; | ||||||
|         lncnt += (1 + line->len / (x2 - side_offst)); |         nxtlines = line->newlines + (line->len / (x2 - side_offst)); | ||||||
|     } |     } while (line->prev && curlines + nxtlines < max_y); | ||||||
|  |  | ||||||
|     hst->line_start = line; |     hst->line_start = line; | ||||||
| } | } | ||||||
| @@ -78,6 +84,15 @@ void line_info_cleanup(struct history *hst) | |||||||
|         free(tmp1); |         free(tmp1); | ||||||
|         tmp1 = tmp2; |         tmp1 = tmp2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < hst->queue_sz; ++i) { | ||||||
|  |         if (hst->queue[i]) | ||||||
|  |             free(hst->queue[i]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     free(hst); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* moves root forward and frees previous root */ | /* moves root forward and frees previous root */ | ||||||
| @@ -96,22 +111,13 @@ static void line_info_root_fwd(struct history *hst) | |||||||
|     hst->line_root = tmp; |     hst->line_root = tmp; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* adds a line_info line to queue */ | /* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */ | ||||||
| static void line_info_add_queue(struct history *hst, struct line_info *line) |  | ||||||
| { |  | ||||||
|     if (hst->queue_sz >= MAX_QUEUE) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     hst->queue[hst->queue_sz++] = line; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* returns ptr to queue item 0 and removes it from queue */ |  | ||||||
| static struct line_info *line_info_ret_queue(struct history *hst) | static struct line_info *line_info_ret_queue(struct history *hst) | ||||||
| { | { | ||||||
|     if (hst->queue_sz <= 0) |     if (hst->queue_sz <= 0) | ||||||
|         return NULL; |         return NULL; | ||||||
|  |  | ||||||
|     struct line_info *ret = hst->queue[0]; |     struct line_info *line = hst->queue[0]; | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -120,33 +126,71 @@ static struct line_info *line_info_ret_queue(struct history *hst) | |||||||
|  |  | ||||||
|     --hst->queue_sz; |     --hst->queue_sz; | ||||||
|  |  | ||||||
|     return ret; |     return line; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* creates new line_info line and puts it in the queue */ | /* creates new line_info line and puts it in the queue. */ | ||||||
| void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *name2, uint8_t *msg, | void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, | ||||||
|                    uint8_t type, uint8_t bold, uint8_t colour) |                    uint8_t bold, uint8_t colour, const char *msg, ...) | ||||||
| { | { | ||||||
|  |     if (!self) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     struct history *hst = self->chatwin->hst; |     struct history *hst = self->chatwin->hst; | ||||||
|     struct line_info *new_line = malloc(sizeof(struct line_info)); |  | ||||||
|  |     if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     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); | ||||||
|  |  | ||||||
|     memset(new_line, 0, sizeof(struct line_info)); |     char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0}; | ||||||
|  |  | ||||||
|  |     va_list args; | ||||||
|  |     va_start(args, msg); | ||||||
|  |     vsnprintf(frmt_msg, sizeof(frmt_msg), msg, args); | ||||||
|  |     va_end(args); | ||||||
|  |  | ||||||
|     int len = 1;     /* there will always be a newline */ |     int len = 1;     /* there will always be a newline */ | ||||||
|  |  | ||||||
|     /* for type-specific formatting in print function */ |     /* for type-specific formatting in print function */ | ||||||
|     switch (type) { |     switch (type) { | ||||||
|         case ACTION: |         case IN_ACTION: | ||||||
|  |  | ||||||
|  |         /* fallthrough */ | ||||||
|  |         case OUT_ACTION: | ||||||
|  |             len += strlen(user_settings->line_normal) + 2; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case IN_MSG: | ||||||
|  |  | ||||||
|  |         /* fallthrough */ | ||||||
|  |         case OUT_MSG: | ||||||
|  |             len += strlen(user_settings->line_normal) + 3; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case IN_PRVT_MSG: | ||||||
|  |         case OUT_PRVT_MSG: | ||||||
|  |             len += strlen(user_settings->line_special) + 3; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|         case CONNECTION: |         case CONNECTION: | ||||||
|             len += 3; |             len += strlen(user_settings->line_join) + 2; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case DISCONNECTION: | ||||||
|  |             len += strlen(user_settings->line_quit) + 2; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case SYS_MSG: |         case SYS_MSG: | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|  |         case NAME_CHANGE: | ||||||
|  |             len += strlen(user_settings->line_alert) + 1; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|         case PROMPT: |         case PROMPT: | ||||||
|             ++len; |             ++len; | ||||||
|             break; |             break; | ||||||
| @@ -156,21 +200,21 @@ void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *na | |||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (msg) { |     if (frmt_msg[0]) { | ||||||
|         snprintf(new_line->msg, sizeof(new_line->msg), "%s", msg); |         snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg); | ||||||
|         len += strlen(new_line->msg); |         len += strlen(new_line->msg); | ||||||
|  |  | ||||||
|         int i; |         int i; | ||||||
|  |  | ||||||
|         for (i = 0; msg[i]; ++i) { |         for (i = 0; frmt_msg[i]; ++i) { | ||||||
|             if (msg[i] == '\n') |             if (frmt_msg[i] == '\n') | ||||||
|                 ++new_line->newlines; |                 ++new_line->newlines; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (tmstmp) { |     if (timestr) { | ||||||
|         snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp); |         snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr); | ||||||
|         len += strlen(new_line->timestamp); |         len += strlen(new_line->timestr) + 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (name1) { |     if (name1) { | ||||||
| @@ -187,8 +231,10 @@ void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *na | |||||||
|     new_line->type = type; |     new_line->type = type; | ||||||
|     new_line->bold = bold; |     new_line->bold = bold; | ||||||
|     new_line->colour = colour; |     new_line->colour = colour; | ||||||
|  |     new_line->noread_flag = false; | ||||||
|  |     new_line->timestamp = get_unix_time(); | ||||||
|  |  | ||||||
|     line_info_add_queue(hst, new_line); |     hst->queue[hst->queue_sz++] = new_line; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* adds a single queue item to hst if possible. only called once per call to line_info_print() */ | /* adds a single queue item to hst if possible. only called once per call to line_info_print() */ | ||||||
| @@ -211,13 +257,14 @@ static void line_info_check_queue(ToxWindow *self) | |||||||
|     int y, y2, x, x2; |     int y, y2, x, x2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|     getyx(self->chatwin->history, y, x); |     getyx(self->chatwin->history, y, x); | ||||||
|  |     (void) x; | ||||||
|  |  | ||||||
|     if (x2 <= SIDEBAR_WIDTH) |     if (x2 <= SIDEBAR_WIDTH) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;   /* offset width of groupchat sidebar */ |     int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;   /* offset width of groupchat sidebar */ | ||||||
|     int lines = 1 + line->newlines + (line->len / (x2 - offst)); |     int lines = 1 + line->newlines + (line->len / (x2 - offst)); | ||||||
|     int max_y = self->is_prompt ? y2 : y2 - CHATBOX_HEIGHT; |     int max_y = y2 - CHATBOX_HEIGHT; | ||||||
|  |  | ||||||
|     /* move line_start forward proportionate to the number of new lines */ |     /* move line_start forward proportionate to the number of new lines */ | ||||||
|     if (y + lines - 1 >= max_y) { |     if (y + lines - 1 >= max_y) { | ||||||
| @@ -229,6 +276,8 @@ static void line_info_check_queue(ToxWindow *self) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #define NOREAD_FLAG_TIMEOUT 5    /* seconds before a sent message with no read receipt is flagged as unread */ | ||||||
|  |  | ||||||
| void line_info_print(ToxWindow *self) | void line_info_print(ToxWindow *self) | ||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
| @@ -246,9 +295,6 @@ void line_info_print(ToxWindow *self) | |||||||
|     int y2, x2; |     int y2, x2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (self->is_prompt) |  | ||||||
|         y2 = user_settings->history_size;   /* temporary fix to make prompt scroll */ |  | ||||||
|  |  | ||||||
|     if (x2 <= SIDEBAR_WIDTH) |     if (x2 <= SIDEBAR_WIDTH) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
| @@ -258,7 +304,6 @@ void line_info_print(ToxWindow *self) | |||||||
|         wmove(win, 2, 0); |         wmove(win, 2, 0); | ||||||
|  |  | ||||||
|     struct line_info *line = hst->line_start->next; |     struct line_info *line = hst->line_start->next; | ||||||
|     int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; |  | ||||||
|     int numlines = 0; |     int numlines = 0; | ||||||
|  |  | ||||||
|     while (line && numlines++ <= y2) { |     while (line && numlines++ <= y2) { | ||||||
| @@ -266,9 +311,16 @@ void line_info_print(ToxWindow *self) | |||||||
|  |  | ||||||
|         switch (type) { |         switch (type) { | ||||||
|             case OUT_MSG: |             case OUT_MSG: | ||||||
|  |  | ||||||
|  |             /* fallthrough */ | ||||||
|  |             case OUT_MSG_READ: | ||||||
|  |  | ||||||
|  |             /* fallthrough */ | ||||||
|             case IN_MSG: |             case IN_MSG: | ||||||
|  |             case IN_PRVT_MSG: | ||||||
|  |             case OUT_PRVT_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; | ||||||
| @@ -279,34 +331,81 @@ void line_info_print(ToxWindow *self) | |||||||
|                     nameclr = CYAN; |                     nameclr = CYAN; | ||||||
|  |  | ||||||
|                 wattron(win, COLOR_PAIR(nameclr)); |                 wattron(win, COLOR_PAIR(nameclr)); | ||||||
|                 wprintw(win, "%s: ", line->name1); |                 wprintw(win, "%s %s: ", (type != OUT_PRVT_MSG && type != IN_PRVT_MSG) ? | ||||||
|  |                         user_settings->line_normal : | ||||||
|  |                         user_settings->line_special, | ||||||
|  |                         line->name1); | ||||||
|                 wattroff(win, COLOR_PAIR(nameclr)); |                 wattroff(win, COLOR_PAIR(nameclr)); | ||||||
|  |  | ||||||
|                 if (line->msg[0] == '>') |                 char *msg = line->msg; | ||||||
|  |  | ||||||
|  |                 while (msg) { | ||||||
|  |                     char *line = strsep(&msg, "\n"); | ||||||
|  |  | ||||||
|  |                     if (line[0] == '>') | ||||||
|                         wattron(win, COLOR_PAIR(GREEN)); |                         wattron(win, COLOR_PAIR(GREEN)); | ||||||
|  |                     else if (line[0] == '<') | ||||||
|  |                         wattron(win, COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|                 wprintw(win, "%s\n", line->msg); |                     wprintw(win, "%s%c", line, msg ? '\n' : '\0'); | ||||||
|  |  | ||||||
|                 if (line->msg[0] == '>') |                     if (line[0] == '>') | ||||||
|                         wattroff(win, COLOR_PAIR(GREEN)); |                         wattroff(win, COLOR_PAIR(GREEN)); | ||||||
|  |                     else if (line[0] == '<') | ||||||
|  |                         wattroff(win, COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |                     // change the \0 set by strsep back to \n | ||||||
|  |                     if (msg) | ||||||
|  |                         msg[-1] = '\n'; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||||
|  |                     wattron(win, COLOR_PAIR(RED)); | ||||||
|  |                     wprintw(win, " x", line->msg); | ||||||
|  |                     wattroff(win, COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |                     if (line->noread_flag == false) { | ||||||
|  |                         line->noread_flag = true; | ||||||
|  |                         line->len += 2; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 wprintw(win, "\n", line->msg); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case ACTION: |             case OUT_ACTION_READ: | ||||||
|  |  | ||||||
|  |             /* fallthrough */ | ||||||
|  |             case OUT_ACTION: | ||||||
|  |  | ||||||
|  |             /* fallthrough */ | ||||||
|  |             case IN_ACTION: | ||||||
|                 wattron(win, COLOR_PAIR(BLUE)); |                 wattron(win, COLOR_PAIR(BLUE)); | ||||||
|                 wprintw(win, "%s", line->timestamp); |                 wprintw(win, "%s ", line->timestr); | ||||||
|                 wattroff(win, COLOR_PAIR(BLUE)); |                 wattroff(win, COLOR_PAIR(BLUE)); | ||||||
|  |  | ||||||
|                 wattron(win, COLOR_PAIR(YELLOW)); |                 wattron(win, COLOR_PAIR(YELLOW)); | ||||||
|                 wprintw(win, "* %s %s\n", line->name1, line->msg); |                 wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg); | ||||||
|                 wattroff(win, COLOR_PAIR(YELLOW)); |                 wattroff(win, COLOR_PAIR(YELLOW)); | ||||||
|  |  | ||||||
|  |                 if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||||
|  |                     wattron(win, COLOR_PAIR(RED)); | ||||||
|  |                     wprintw(win, " x", line->msg); | ||||||
|  |                     wattroff(win, COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |                     if (line->noread_flag == false) { | ||||||
|  |                         line->noread_flag = true; | ||||||
|  |                         line->len += 2; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 wprintw(win, "\n", line->msg); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case SYS_MSG: |             case SYS_MSG: | ||||||
|                 if (line->timestamp[0]) { |                 if (line->timestr[0]) { | ||||||
|                     wattron(win, COLOR_PAIR(BLUE)); |                     wattron(win, COLOR_PAIR(BLUE)); | ||||||
|                     wprintw(win, "%s", line->timestamp); |                     wprintw(win, "%s ", line->timestr); | ||||||
|                     wattroff(win, COLOR_PAIR(BLUE)); |                     wattroff(win, COLOR_PAIR(BLUE)); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -339,13 +438,33 @@ void line_info_print(ToxWindow *self) | |||||||
|  |  | ||||||
|             case CONNECTION: |             case CONNECTION: | ||||||
|                 wattron(win, COLOR_PAIR(BLUE)); |                 wattron(win, COLOR_PAIR(BLUE)); | ||||||
|                 wprintw(win, "%s", line->timestamp); |                 wprintw(win, "%s ", line->timestr); | ||||||
|                 wattroff(win, COLOR_PAIR(BLUE)); |                 wattroff(win, COLOR_PAIR(BLUE)); | ||||||
|  |  | ||||||
|                 wattron(win, COLOR_PAIR(line->colour)); |                 wattron(win, COLOR_PAIR(line->colour)); | ||||||
|  |                 wprintw(win, "%s ", user_settings->line_join); | ||||||
|  |  | ||||||
|                 wattron(win, A_BOLD); |                 wattron(win, A_BOLD); | ||||||
|                 wprintw(win, "* %s ", line->name1); |                 wprintw(win, "%s ", line->name1); | ||||||
|                 wattroff(win, A_BOLD); |                 wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|  |                 wprintw(win, "%s\n", line->msg); | ||||||
|  |                 wattroff(win, COLOR_PAIR(line->colour)); | ||||||
|  |  | ||||||
|  |                 break; | ||||||
|  |  | ||||||
|  |             case DISCONNECTION: | ||||||
|  |                 wattron(win, COLOR_PAIR(BLUE)); | ||||||
|  |                 wprintw(win, "%s ", line->timestr); | ||||||
|  |                 wattroff(win, COLOR_PAIR(BLUE)); | ||||||
|  |  | ||||||
|  |                 wattron(win, COLOR_PAIR(line->colour)); | ||||||
|  |                 wprintw(win, "%s ", user_settings->line_quit); | ||||||
|  |  | ||||||
|  |                 wattron(win, A_BOLD); | ||||||
|  |                 wprintw(win, "%s ", line->name1); | ||||||
|  |                 wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|                 wprintw(win, "%s\n", line->msg); |                 wprintw(win, "%s\n", line->msg); | ||||||
|                 wattroff(win, COLOR_PAIR(line->colour)); |                 wattroff(win, COLOR_PAIR(line->colour)); | ||||||
|  |  | ||||||
| @@ -353,12 +472,13 @@ void line_info_print(ToxWindow *self) | |||||||
|  |  | ||||||
|             case NAME_CHANGE: |             case NAME_CHANGE: | ||||||
|                 wattron(win, COLOR_PAIR(BLUE)); |                 wattron(win, COLOR_PAIR(BLUE)); | ||||||
|                 wprintw(win, "%s", line->timestamp); |                 wprintw(win, "%s ", line->timestr); | ||||||
|                 wattroff(win, COLOR_PAIR(BLUE)); |                 wattroff(win, COLOR_PAIR(BLUE)); | ||||||
|  |  | ||||||
|                 wattron(win, COLOR_PAIR(MAGENTA)); |                 wattron(win, COLOR_PAIR(MAGENTA)); | ||||||
|  |                 wprintw(win, "%s ", user_settings->line_alert); | ||||||
|                 wattron(win, A_BOLD); |                 wattron(win, A_BOLD); | ||||||
|                 wprintw(win, "* %s", line->name1); |                 wprintw(win, "%s", line->name1); | ||||||
|                 wattroff(win, A_BOLD); |                 wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|                 wprintw(win, "%s", line->msg); |                 wprintw(win, "%s", line->msg); | ||||||
| @@ -379,7 +499,8 @@ void line_info_print(ToxWindow *self) | |||||||
|         line_info_print(self); |         line_info_print(self); | ||||||
| } | } | ||||||
|  |  | ||||||
| void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg) | /* puts msg in specified line_info msg buffer */ | ||||||
|  | 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; | ||||||
|  |  | ||||||
| @@ -393,29 +514,30 @@ void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void line_info_goto_root(struct history *hst) | /* static void line_info_goto_root(struct history *hst) | ||||||
| { | { | ||||||
|     hst->line_start = hst->line_root; |     hst->line_start = hst->line_root; | ||||||
| } | } */ | ||||||
|  |  | ||||||
| static void line_info_scroll_up(struct history *hst) | static void line_info_scroll_up(struct history *hst) | ||||||
| { | { | ||||||
|     if (hst->line_start->prev) |     if (hst->line_start->prev) | ||||||
|         hst->line_start = hst->line_start->prev; |         hst->line_start = hst->line_start->prev; | ||||||
|     else beep(); |     else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void line_info_scroll_down(struct history *hst) | static void line_info_scroll_down(struct history *hst) | ||||||
| { | { | ||||||
|     if (hst->line_start->next) |     if (hst->line_start->next) | ||||||
|         hst->line_start = hst->line_start->next; |         hst->line_start = hst->line_start->next; | ||||||
|     else beep(); |     else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void line_info_page_up(ToxWindow *self, struct history *hst) | static void line_info_page_up(ToxWindow *self, struct history *hst) | ||||||
| { | { | ||||||
|     int x2, y2; |     int x2, y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |     (void) x2; | ||||||
|     int jump_dist = y2 / 2; |     int jump_dist = y2 / 2; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -427,6 +549,7 @@ static void line_info_page_down(ToxWindow *self, struct history *hst) | |||||||
| { | { | ||||||
|     int x2, y2; |     int x2, y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |     (void) x2; | ||||||
|     int jump_dist = y2 / 2; |     int jump_dist = y2 / 2; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -439,35 +562,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 */ |  | ||||||
|         case T_KEY_C_H: |  | ||||||
|         line_info_page_up(self, hst); |         line_info_page_up(self, hst); | ||||||
|             break; |     } else if (key == user_settings->key_half_page_down) { | ||||||
|  |  | ||||||
|         case T_KEY_C_B: |  | ||||||
|         line_info_page_down(self, hst); |         line_info_page_down(self, hst); | ||||||
|             break;  |     } else if (key == user_settings->key_scroll_line_up) { | ||||||
|  |  | ||||||
|         case KEY_PPAGE: |  | ||||||
|         line_info_scroll_up(hst); |         line_info_scroll_up(hst); | ||||||
|             break; |     } else if (key == user_settings->key_scroll_line_down) { | ||||||
|  |  | ||||||
|         case KEY_NPAGE: |  | ||||||
|         line_info_scroll_down(hst); |         line_info_scroll_down(hst); | ||||||
|             break; |     } else if (key == user_settings->key_page_bottom) { | ||||||
|  |  | ||||||
|         /* case ?: |  | ||||||
|             line_info_goto_root(hst); |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case ?: |  | ||||||
|         line_info_reset_start(self, hst); |         line_info_reset_start(self, hst); | ||||||
|             break; */ |     } else { | ||||||
|  |  | ||||||
|         default: |  | ||||||
|         match = false; |         match = false; | ||||||
|             break; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return match; |     return match; | ||||||
|   | |||||||
| @@ -20,36 +20,45 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef _line_info_h | #ifndef LINE_INFO_H | ||||||
| #define _line_info_h | #define LINE_INFO_H | ||||||
|  |  | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  |  | ||||||
| #define MAX_HISTORY 10000 | #define MAX_HISTORY 100000 | ||||||
| #define MIN_HISTORY 20 | #define MIN_HISTORY 40 | ||||||
| #define MAX_QUEUE 32 | #define MAX_LINE_INFO_QUEUE 1024 | ||||||
|  | #define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32    /* needs extra room for log loading */ | ||||||
|  |  | ||||||
| enum { | enum { | ||||||
|     SYS_MSG, |     SYS_MSG, | ||||||
|     IN_MSG, |     IN_MSG, | ||||||
|     OUT_MSG, |     OUT_MSG, | ||||||
|  |     OUT_MSG_READ,    /* for sent messages that have received a read reply. */ | ||||||
|  |     IN_ACTION, | ||||||
|  |     OUT_ACTION, | ||||||
|  |     OUT_ACTION_READ,     /* same as OUT_MSG_READ but for actions */ | ||||||
|  |     IN_PRVT_MSG,   /* PRVT should only be used for groups */ | ||||||
|  |     OUT_PRVT_MSG, | ||||||
|     PROMPT, |     PROMPT, | ||||||
|     ACTION, |  | ||||||
|     CONNECTION, |     CONNECTION, | ||||||
|  |     DISCONNECTION, | ||||||
|     NAME_CHANGE, |     NAME_CHANGE, | ||||||
| } LINE_TYPE; | } LINE_TYPE; | ||||||
|  |  | ||||||
| struct line_info { | struct line_info { | ||||||
|     uint8_t timestamp[TIME_STR_SIZE]; |     char timestr[TIME_STR_SIZE]; | ||||||
|     uint8_t name1[TOXIC_MAX_NAME_LENGTH]; |     char name1[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     uint8_t name2[TOXIC_MAX_NAME_LENGTH]; |     char name2[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     uint8_t msg[TOX_MAX_MESSAGE_LENGTH]; |     char msg[MAX_LINE_INFO_MSG_SIZE]; | ||||||
|  |     time_t timestamp; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|     uint8_t bold; |     uint8_t bold; | ||||||
|     uint8_t colour; |     uint8_t colour; | ||||||
|  |     uint8_t noread_flag;   /* true if a line should be flagged as unread */ | ||||||
|     uint32_t id; |     uint32_t id; | ||||||
|     uint16_t len;   /* combined len of all strings */ |     uint16_t len;   /* combined len of entire line */ | ||||||
|     uint8_t newlines; |     uint8_t newlines; | ||||||
|  |  | ||||||
|     struct line_info *prev; |     struct line_info *prev; | ||||||
| @@ -63,13 +72,13 @@ struct history { | |||||||
|     struct line_info *line_end; |     struct line_info *line_end; | ||||||
|     uint32_t start_id;    /* keeps track of where line_start should be when at bottom of history */ |     uint32_t start_id;    /* keeps track of where line_start should be when at bottom of history */ | ||||||
|  |  | ||||||
|     struct line_info *queue[MAX_QUEUE]; |     struct line_info *queue[MAX_LINE_INFO_QUEUE]; | ||||||
|     int queue_sz; |     int queue_sz; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* creates new line_info line and puts it in the queue */ | /* creates new line_info line and puts it in the queue. */ | ||||||
| void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *name2, uint8_t *msg, | void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, | ||||||
|                    uint8_t type, uint8_t bold, uint8_t colour); |                    uint8_t bold, uint8_t colour, const char *msg, ...); | ||||||
|  |  | ||||||
| /* Prints a section of history starting at line_start */ | /* Prints a section of history starting at line_start */ | ||||||
| void line_info_print(ToxWindow *self); | void line_info_print(ToxWindow *self); | ||||||
| @@ -81,9 +90,12 @@ void line_info_cleanup(struct history *hst); | |||||||
| void line_info_clear(struct history *hst); | void line_info_clear(struct history *hst); | ||||||
|  |  | ||||||
| /* puts msg in specified line_info msg buffer */ | /* puts msg in specified line_info msg buffer */ | ||||||
| void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg); | void line_info_set(ToxWindow *self, uint32_t id, char *msg); | ||||||
|  |  | ||||||
|  | /* resets line_start (moves to end of chat history) */ | ||||||
|  | void line_info_reset_start(ToxWindow *self, struct history *hst); | ||||||
|  |  | ||||||
| void line_info_init(struct history *hst); | void line_info_init(struct history *hst); | ||||||
| bool line_info_onKey(ToxWindow *self, wint_t key);    /* returns true if key is a match */ | bool line_info_onKey(ToxWindow *self, wint_t key);    /* returns true if key is a match */ | ||||||
|  |  | ||||||
| #endif /* #define _line_info_h */ | #endif /* #define LINE_INFO_H */ | ||||||
|   | |||||||
							
								
								
									
										247
									
								
								src/log.c
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								src/log.c
									
									
									
									
									
								
							| @@ -20,13 +20,10 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  |  | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| @@ -34,59 +31,93 @@ | |||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
|  | #include "line_info.h" | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| /* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */ | /* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h) | ||||||
| void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log) |    A prompt log is in the format: LOGDIR/selfkey-home.log | ||||||
| { |    A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log | ||||||
|     if (!log->log_on) |    A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log | ||||||
|         return; |  | ||||||
|  |  | ||||||
|  |    Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used. | ||||||
|  |  | ||||||
|  |    Returns 0 on success, -1 if the path is too long */ | ||||||
|  | static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype) | ||||||
|  | { | ||||||
|     if (!valid_nick(name)) |     if (!valid_nick(name)) | ||||||
|         name = UNKNOWN_NAME; |         name = UNKNOWN_NAME; | ||||||
|  |  | ||||||
|  |     const char *namedash = logtype == LOG_PROMPT ? "" : "-"; | ||||||
|  |     const char *set_path = user_settings->chatlogs_path; | ||||||
|  |  | ||||||
|     char *user_config_dir = get_user_config_dir(); |     char *user_config_dir = get_user_config_dir(); | ||||||
|     int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name); |     int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash); | ||||||
|  |     path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR); | ||||||
|  |  | ||||||
|     /* use first 4 digits of key as log ident. If no key use a timestamp */ |     /* first 6 digits of selfkey */ | ||||||
|     uint8_t ident[32]; |     char self_id[32]; | ||||||
|  |     path_len += KEY_IDENT_DIGITS * 2; | ||||||
|  |     sprintf(&self_id[0], "%02X", selfkey[0] & 0xff); | ||||||
|  |     sprintf(&self_id[2], "%02X", selfkey[1] & 0xff); | ||||||
|  |     sprintf(&self_id[4], "%02X", selfkey[2] & 0xff); | ||||||
|  |     self_id[KEY_IDENT_DIGITS * 2] = '\0'; | ||||||
|  |  | ||||||
|     if (key != NULL) { |     char other_id[32] = {0}; | ||||||
|         path_len += (KEY_IDENT_DIGITS * 2 + 5); |  | ||||||
|  |  | ||||||
|         sprintf(&ident[0], "%02X", key[0] & 0xff); |     switch (logtype) { | ||||||
|         sprintf(&ident[2], "%02X", key[1] & 0xff); |         case LOG_CHAT: | ||||||
|         ident[KEY_IDENT_DIGITS * 2 + 1] = '\0'; |             path_len += KEY_IDENT_DIGITS * 2; | ||||||
|     } else { |             sprintf(&other_id[0], "%02X", otherkey[0] & 0xff); | ||||||
|         strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time()); |             sprintf(&other_id[2], "%02X", otherkey[1] & 0xff); | ||||||
|         path_len += strlen(ident) + 1; |             sprintf(&other_id[4], "%02X", otherkey[2] & 0xff); | ||||||
|  |             other_id[KEY_IDENT_DIGITS * 2] = '\0'; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case LOG_GROUP: | ||||||
|  |             strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time()); | ||||||
|  |             path_len += strlen(other_id); | ||||||
|  |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (path_len > MAX_STR_SIZE) { |     if (path_len >= destsize) { | ||||||
|         log->log_on = false; |  | ||||||
|         free(user_config_dir); |         free(user_config_dir); | ||||||
|         return; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t log_path[MAX_STR_SIZE]; |     if (!string_is_empty(set_path)) | ||||||
|  |         snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id); | ||||||
|     snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log", |     else | ||||||
|              user_config_dir, CONFIGDIR, name, ident); |         snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id); | ||||||
|  |  | ||||||
|     free(user_config_dir); |     free(user_config_dir); | ||||||
|  |  | ||||||
|     log->file = fopen(log_path, "a"); |     return 0; | ||||||
|  |  | ||||||
|     if (log->file == NULL) { |  | ||||||
|         log->log_on = false; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fprintf(log->file, "\n*** NEW SESSION ***\n\n"); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event) | /* Opens log file or creates a new one */ | ||||||
|  | static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) | ||||||
|  | { | ||||||
|  |     if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL)) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     char log_path[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |     if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     log->file = fopen(log_path, "a+"); | ||||||
|  |     snprintf(log->path, sizeof(log->path), "%s", log_path); | ||||||
|  |  | ||||||
|  |     if (log->file == NULL) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define LOG_FLUSH_LIMIT 1  /* limits calls to fflush to a max of one per LOG_FLUSH_LIMIT seconds */ | ||||||
|  |  | ||||||
|  | void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event) | ||||||
| { | { | ||||||
|     if (!log->log_on) |     if (!log->log_on) | ||||||
|         return; |         return; | ||||||
| @@ -96,40 +127,144 @@ void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool e | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3]; |     char name_frmt[TOXIC_MAX_NAME_LENGTH + 3]; | ||||||
|  |  | ||||||
|     if (event) |     if (event) | ||||||
|         snprintf(name_frmt, sizeof(name_frmt), "* %s", name); |         snprintf(name_frmt, sizeof(name_frmt), "* %s", name); | ||||||
|     else |     else | ||||||
|         snprintf(name_frmt, sizeof(name_frmt), "%s:", name); |         snprintf(name_frmt, sizeof(name_frmt), "%s:", name); | ||||||
|  |  | ||||||
|     const char *t = user_settings->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]"; |     const char *t = user_settings->log_timestamp_format; | ||||||
|     uint8_t s[MAX_STR_SIZE]; |     char s[MAX_STR_SIZE]; | ||||||
|     strftime(s, MAX_STR_SIZE, t, get_time()); |     strftime(s, MAX_STR_SIZE, t, get_time()); | ||||||
|     fprintf(log->file, "%s %s %s\n", s, name_frmt, msg); |     fprintf(log->file, "%s %s %s\n", s, name_frmt, msg); | ||||||
|  |  | ||||||
|     uint64_t curtime = get_unix_time(); |     if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) { | ||||||
|  |  | ||||||
|     if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) { |  | ||||||
|         fflush(log->file); |         fflush(log->file); | ||||||
|         log->lastwrite = curtime; |         log->lastwrite = get_unix_time(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log) |  | ||||||
| { |  | ||||||
|     log->log_on = true; |  | ||||||
|  |  | ||||||
|     if (log->file == NULL) |  | ||||||
|         init_logging_session(name, key, log); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void log_disable(struct chatlog *log) | void log_disable(struct chatlog *log) | ||||||
| { | { | ||||||
|     log->log_on = false; |     if (log->file != NULL) | ||||||
|  |  | ||||||
|     if (log->file != NULL) { |  | ||||||
|         fclose(log->file); |         fclose(log->file); | ||||||
|         log->file = NULL; |  | ||||||
|     } |     memset(log, 0, sizeof(struct chatlog)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) | ||||||
|  | { | ||||||
|  |     log->log_on = true; | ||||||
|  |  | ||||||
|  |     if (log->file != NULL) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) { | ||||||
|  |         log_disable(log); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Loads previous history from chat log */ | ||||||
|  | void load_chat_history(ToxWindow *self, struct chatlog *log) | ||||||
|  | { | ||||||
|  |     if (log->file == NULL) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     off_t sz = file_size(log->path); | ||||||
|  |  | ||||||
|  |     if (sz <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     char *hstbuf = malloc(sz + 1); | ||||||
|  |  | ||||||
|  |     if (hstbuf == NULL) | ||||||
|  |         exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY); | ||||||
|  |  | ||||||
|  |     if (fseek(log->file, 0L, SEEK_SET) == -1) { | ||||||
|  |         free(hstbuf); | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (fread(hstbuf, sz, 1, log->file) != 1) { | ||||||
|  |         free(hstbuf); | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hstbuf[sz] = '\0'; | ||||||
|  |  | ||||||
|  |     /* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */ | ||||||
|  |     int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size); | ||||||
|  |     int start, count = 0; | ||||||
|  |  | ||||||
|  |     /* start at end and backtrace L lines or to the beginning of buffer */ | ||||||
|  |     for (start = sz - 1; start >= 0 && count < L; --start) { | ||||||
|  |         if (hstbuf[start] == '\n') | ||||||
|  |             ++count; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *line = strtok(&hstbuf[start + 1], "\n"); | ||||||
|  |  | ||||||
|  |     if (line == NULL) { | ||||||
|  |         free(hstbuf); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (line != NULL && count--) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line); | ||||||
|  |         line = strtok(NULL, "\n"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
|  |     free(hstbuf); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* renames chatlog file replacing src with dest. | ||||||
|  |    Returns 0 on success or if no log exists, -1 on failure. */ | ||||||
|  | int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum) | ||||||
|  | { | ||||||
|  |     ToxWindow *toxwin = get_window_ptr(winnum); | ||||||
|  |     struct chatlog *log = NULL; | ||||||
|  |     bool log_on = false; | ||||||
|  |  | ||||||
|  |     /* disable log if necessary and save its state */ | ||||||
|  |     if (toxwin != NULL) { | ||||||
|  |         log = toxwin->chatwin->log; | ||||||
|  |         log_on = log->log_on; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (log_on) | ||||||
|  |         log_disable(log); | ||||||
|  |  | ||||||
|  |     char newpath[MAX_STR_SIZE]; | ||||||
|  |     char oldpath[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |     if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) | ||||||
|  |         goto on_error; | ||||||
|  |  | ||||||
|  |     if (!file_exists(oldpath)) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1) | ||||||
|  |         goto on_error; | ||||||
|  |  | ||||||
|  |     if (rename(oldpath, newpath) != 0) | ||||||
|  |         goto on_error; | ||||||
|  |  | ||||||
|  |     if (log_on) | ||||||
|  |         log_enable(dest, selfkey, otherkey, log, LOG_CHAT); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  | on_error: | ||||||
|  |  | ||||||
|  |     if (log_on) | ||||||
|  |         log_enable(src, selfkey, otherkey, log, LOG_CHAT); | ||||||
|  |  | ||||||
|  |     return -1; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								src/log.h
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/log.h
									
									
									
									
									
								
							| @@ -20,28 +20,40 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef _log_h | #ifndef LOG_H | ||||||
| #define _log_h | #define LOG_H | ||||||
|  |  | ||||||
| #define LOG_FLUSH_LIMIT 2  /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */ |  | ||||||
|  |  | ||||||
| struct chatlog { | struct chatlog { | ||||||
|     FILE *file; |     FILE *file; | ||||||
|     uint64_t lastwrite; |     time_t lastwrite; | ||||||
|     int pos; |     char path[MAX_STR_SIZE]; | ||||||
|     bool log_on;    /* specific to current chat window */ |     bool log_on;    /* specific to current chat window */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */ | enum { | ||||||
| void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log); |     LOG_GROUP, | ||||||
|  |     LOG_PROMPT, | ||||||
|  |     LOG_CHAT, | ||||||
|  | } LOG_TYPE; | ||||||
|  |  | ||||||
| /* formats/writes line to log file */ | /* formats/writes line to log file */ | ||||||
| void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event); | void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event); | ||||||
|  |  | ||||||
| /* enables logging for specified log and creates/fetches file if necessary */ | /* enables logging for specified log and creates/fetches file if necessary. | ||||||
| void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log); |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype); | ||||||
|  |  | ||||||
| /* disables logging for specified log and closes file */ | /* disables logging for specified log and closes file */ | ||||||
| void log_disable(struct chatlog *log); | void log_disable(struct chatlog *log); | ||||||
|  |  | ||||||
| #endif /* #define _log_h */ | /* Loads previous history from chat log */ | ||||||
|  | void load_chat_history(ToxWindow *self, struct chatlog *log); | ||||||
|  |  | ||||||
|  | /* renames chatlog file replacing src with dest. | ||||||
|  |    Returns 0 on success or if no log exists, -1 on failure. */ | ||||||
|  | int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum); | ||||||
|  |  | ||||||
|  | #endif /* #define LOG_H */ | ||||||
|   | |||||||
							
								
								
									
										154
									
								
								src/message_queue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/message_queue.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | |||||||
|  | /*  message_queue.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  | #include "message_queue.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "log.h" | ||||||
|  |  | ||||||
|  | void cqueue_cleanup(struct chat_queue *q) | ||||||
|  | { | ||||||
|  |     struct cqueue_msg *tmp1 = q->root; | ||||||
|  |  | ||||||
|  |     while (tmp1) { | ||||||
|  |         struct cqueue_msg *tmp2 = tmp1->next; | ||||||
|  |         free(tmp1); | ||||||
|  |         tmp1 = tmp2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     free(q); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id) | ||||||
|  | { | ||||||
|  |     struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg)); | ||||||
|  |  | ||||||
|  |     if (new_m == NULL) | ||||||
|  |         exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY); | ||||||
|  |  | ||||||
|  |     snprintf(new_m->message, sizeof(new_m->message), "%s", msg); | ||||||
|  |     new_m->len = len; | ||||||
|  |     new_m->type = type; | ||||||
|  |     new_m->line_id = line_id; | ||||||
|  |     new_m->last_send_try = 0; | ||||||
|  |     new_m->receipt = 0; | ||||||
|  |     new_m->next = NULL; | ||||||
|  |  | ||||||
|  |     if (q->root == NULL) { | ||||||
|  |         new_m->prev = NULL; | ||||||
|  |         q->root = new_m; | ||||||
|  |     } else { | ||||||
|  |         new_m->prev = q->end; | ||||||
|  |         q->end->next = new_m; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     q->end = new_m; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* update line to show receipt was received after queue removal */ | ||||||
|  | static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg) | ||||||
|  | { | ||||||
|  |     struct line_info *line = self->chatwin->hst->line_end; | ||||||
|  |  | ||||||
|  |     while (line) { | ||||||
|  |         if (line->id != msg->line_id) { | ||||||
|  |             line = line->prev; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ; | ||||||
|  |  | ||||||
|  |         if (line->noread_flag == true) { | ||||||
|  |             line->len -= 2; | ||||||
|  |             line->noread_flag = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ | ||||||
|  | void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | ||||||
|  | { | ||||||
|  |     struct chat_queue *q = self->chatwin->cqueue; | ||||||
|  |     struct cqueue_msg *msg = q->root; | ||||||
|  |  | ||||||
|  |     while (msg) { | ||||||
|  |         if (msg->receipt != receipt) { | ||||||
|  |             msg = msg->next; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         char selfname[TOX_MAX_NAME_LENGTH]; | ||||||
|  |         tox_self_get_name(m, (uint8_t *) selfname); | ||||||
|  |  | ||||||
|  |         size_t len = tox_self_get_name_size(m); | ||||||
|  |         selfname[len] = '\0'; | ||||||
|  |  | ||||||
|  |         write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION); | ||||||
|  |         cqueue_mark_read(self, msg); | ||||||
|  |  | ||||||
|  |         struct cqueue_msg *next = msg->next; | ||||||
|  |  | ||||||
|  |         if (msg->prev == NULL) {    /* root */ | ||||||
|  |             if (next) | ||||||
|  |                 next->prev = NULL; | ||||||
|  |  | ||||||
|  |             free(msg); | ||||||
|  |             q->root = next; | ||||||
|  |         } else { | ||||||
|  |             struct cqueue_msg *prev = msg->prev; | ||||||
|  |             free(msg); | ||||||
|  |             prev->next = next; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define CQUEUE_TRY_SEND_INTERVAL 60 | ||||||
|  |  | ||||||
|  | /* Tries to send the oldest unsent message in queue. */ | ||||||
|  | void cqueue_try_send(ToxWindow *self, Tox *m) | ||||||
|  | { | ||||||
|  |     struct chat_queue *q = self->chatwin->cqueue; | ||||||
|  |     struct cqueue_msg *msg = q->root; | ||||||
|  |  | ||||||
|  |     if (!msg) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     uint32_t receipt = 0; | ||||||
|  |  | ||||||
|  |     TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION; | ||||||
|  |     receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL); | ||||||
|  |  | ||||||
|  |     msg->last_send_try = get_unix_time(); | ||||||
|  |     msg->receipt = receipt; | ||||||
|  |     return; | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								src/message_queue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/message_queue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | /*  message_queue.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef MESSAGE_QUEUE_H | ||||||
|  | #define MESSAGE_QUEUE_H | ||||||
|  |  | ||||||
|  | struct cqueue_msg { | ||||||
|  |     char message[MAX_STR_SIZE]; | ||||||
|  |     size_t len; | ||||||
|  |     int line_id; | ||||||
|  |     uint8_t type; | ||||||
|  |     uint32_t receipt; | ||||||
|  |     time_t last_send_try; | ||||||
|  |     struct cqueue_msg *next; | ||||||
|  |     struct cqueue_msg *prev; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct chat_queue { | ||||||
|  |     struct cqueue_msg *root; | ||||||
|  |     struct cqueue_msg *end; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void cqueue_cleanup(struct chat_queue *q); | ||||||
|  | void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id); | ||||||
|  |  | ||||||
|  | /* Tries to send the oldest unsent message in queue. */ | ||||||
|  | void cqueue_try_send(ToxWindow *self, Tox *m); | ||||||
|  |  | ||||||
|  | /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ | ||||||
|  | void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt); | ||||||
|  |  | ||||||
|  | #endif  /* #define MESSAGE_QUEUE_H */ | ||||||
							
								
								
									
										545
									
								
								src/misc_tools.c
									
									
									
									
									
								
							
							
						
						
									
										545
									
								
								src/misc_tools.c
									
									
									
									
									
								
							| @@ -20,77 +20,189 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifdef HAVE_CONFIG_H |  | ||||||
| #include "config.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <ctype.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
|  | #include <dirent.h> | ||||||
|  | #if defined(__FreeBSD__) | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #else | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #endif | ||||||
|  | #include <sys/stat.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
|  | #include "file_transfers.h" | ||||||
|  |  | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| static uint64_t current_unix_time; | void hst_to_net(uint8_t *num, uint16_t numbytes) | ||||||
|  |  | ||||||
| void update_unix_time(void) |  | ||||||
| { | { | ||||||
|     current_unix_time = (uint64_t) time(NULL); | #ifndef WORDS_BIGENDIAN | ||||||
|  |     uint32_t i; | ||||||
|  |     uint8_t buff[numbytes]; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < numbytes; ++i) { | ||||||
|  |         buff[i] = num[numbytes - i - 1]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(num, buff, numbytes); | ||||||
|  | #endif | ||||||
|  |     return; | ||||||
| } | } | ||||||
|  |  | ||||||
| uint64_t get_unix_time(void) | time_t get_unix_time(void) | ||||||
| { | { | ||||||
|     return current_unix_time; |     return time(NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Returns 1 if connection has timed out, 0 otherwise */ | ||||||
|  | int timed_out(time_t timestamp, time_t timeout) | ||||||
|  | { | ||||||
|  |     return timestamp + timeout <= get_unix_time(); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Get the current local time */ | /* Get the current local time */ | ||||||
| struct tm *get_time(void) | struct tm *get_time(void) | ||||||
| { | { | ||||||
|     struct tm *timeinfo; |     struct tm *timeinfo; | ||||||
|     uint64_t t = get_unix_time(); |     time_t t = get_unix_time(); | ||||||
|     timeinfo = localtime(&t); |     timeinfo = localtime((const time_t *) &t); | ||||||
|     return timeinfo; |     return timeinfo; | ||||||
| } | } | ||||||
|  |  | ||||||
| void get_time_str(uint8_t *buf) | /* Puts the current time in buf in the format of [HH:mm:ss] */ | ||||||
|  | void get_time_str(char *buf, int bufsize) | ||||||
| { | { | ||||||
|     const char *t = user_settings->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] "; |     if (user_settings->timestamps == TIMESTAMPS_OFF) { | ||||||
|     strftime(buf, TIME_STR_SIZE, t, get_time()); |         buf[0] = '\0'; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *t = user_settings->timestamp_format; | ||||||
|  |     strftime(buf, bufsize, t, get_time()); | ||||||
| } | } | ||||||
|  |  | ||||||
| char *hex_string_to_bin(const char *hex_string) | /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||||
|  | void get_elapsed_time_str(char *buf, int bufsize, time_t secs) | ||||||
| { | { | ||||||
|     size_t len = strlen(hex_string); |     if (!secs) | ||||||
|     char *val = malloc(len); |         return; | ||||||
|  |  | ||||||
|     if (val == NULL) { |     long int seconds = secs % 60; | ||||||
|         endwin(); |     long int minutes = (secs % 3600) / 60; | ||||||
|         fprintf(stderr, "malloc() failed. Aborting...\n"); |     long int hours = secs / 3600; | ||||||
|         exit(EXIT_FAILURE); |  | ||||||
|  |     if (!minutes && !hours) | ||||||
|  |         snprintf(buf, bufsize, "%.2ld", seconds); | ||||||
|  |     else if (!hours) | ||||||
|  |         snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds); | ||||||
|  |     else | ||||||
|  |         snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Converts seconds to string in format H hours, m minutes, s seconds */ | ||||||
|  | void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs) | ||||||
|  | { | ||||||
|  |     if (!secs) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     long int seconds = secs % 60; | ||||||
|  |     long int minutes = (secs % 3600) / 60; | ||||||
|  |     long int hours = secs / 3600; | ||||||
|  |  | ||||||
|  |     if (!minutes && !hours) | ||||||
|  |         snprintf(buf, bufsize, "%ld seconds", seconds); | ||||||
|  |     else if (!hours) | ||||||
|  |         snprintf(buf, bufsize, "%ld minutes, %ld seconds", minutes, seconds); | ||||||
|  |     else | ||||||
|  |         snprintf(buf, bufsize, "%ld hours, %ld minutes, %ld seconds", hours, minutes, seconds); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. | ||||||
|  |  * output_size must be exactly half of hex_len. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size) | ||||||
|  | { | ||||||
|  |     if (output_size == 0 || hex_len != output_size * 2) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; i < output_size; ++i) { | ||||||
|  |         sscanf(hex_string, "%2hhx", &output[i]); | ||||||
|  |         hex_string += 2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int hex_string_to_bytes(char *buf, int size, const char *keystr) | ||||||
|  | { | ||||||
|  |     if (size % 2 != 0) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     int i, res; | ||||||
|  |     const char *pos = keystr; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < size; ++i) { | ||||||
|  |         res = sscanf(pos, "%2hhx", &buf[i]); | ||||||
|  |         pos += 2; | ||||||
|  |  | ||||||
|  |         if (res == EOF || res < 1) | ||||||
|  |             return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Converts a binary representation of a Tox ID into a string. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size) | ||||||
|  | { | ||||||
|  |     if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|     size_t i; |     size_t i; | ||||||
|  |  | ||||||
|     for (i = 0; i < len; ++i, hex_string += 2) |     for (i = 0; i < TOX_ADDRESS_SIZE; ++i) | ||||||
|         sscanf(hex_string, "%2hhx", &val[i]); |         snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); | ||||||
|  |  | ||||||
|     return val; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns 1 if the string is empty, 0 otherwise */ | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
| int string_is_empty(char *string) | int string_is_empty(const char *string) | ||||||
| { | { | ||||||
|  |     if (!string) | ||||||
|  |         return true; | ||||||
|  |  | ||||||
|     return string[0] == '\0'; |     return string[0] == '\0'; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* convert a multibyte string to a wide character string (must provide buffer) */ | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
| int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n) | int wstring_is_empty(const wchar_t *string) | ||||||
|  | { | ||||||
|  |     if (!string) | ||||||
|  |         return true; | ||||||
|  |  | ||||||
|  |     return string[0] == L'\0'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* convert a multibyte string to a wide character string and puts in buf. */ | ||||||
|  | int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | ||||||
| { | { | ||||||
|     size_t len = mbstowcs(NULL, string, 0) + 1; |     size_t len = mbstowcs(NULL, string, 0) + 1; | ||||||
|  |  | ||||||
| @@ -103,9 +215,8 @@ int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n) | |||||||
|     return len; |     return len; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* converts wide character string into a multibyte string. | /* converts wide character string into a multibyte string and puts in buf. */ | ||||||
|    Same thing as wcs_to_mbs() but caller must provide its own buffer */ | int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n) | ||||||
| int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n) |  | ||||||
| { | { | ||||||
|     size_t len = wcstombs(NULL, string, 0) + 1; |     size_t len = wcstombs(NULL, string, 0) + 1; | ||||||
|  |  | ||||||
| @@ -118,94 +229,19 @@ int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n) | |||||||
|     return len; |     return len; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* convert wide characters to multibyte string: string returned must be freed */ |  | ||||||
| uint8_t *wcs_to_mbs(wchar_t *string) |  | ||||||
| { |  | ||||||
|     uint8_t *ret = NULL; |  | ||||||
|     size_t len = wcstombs(NULL, string, 0); |  | ||||||
|  |  | ||||||
|     if (len != (size_t) - 1) { |  | ||||||
|         ret = malloc(++len); |  | ||||||
|  |  | ||||||
|         if (ret != NULL) { |  | ||||||
|             if (wcstombs(ret, string, len) == (size_t) - 1) |  | ||||||
|                 return NULL; |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         ret = malloc(2); |  | ||||||
|  |  | ||||||
|         if (ret != NULL) { |  | ||||||
|             ret[0] = ' '; |  | ||||||
|             ret[1] = '\0'; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (ret == NULL) { |  | ||||||
|         endwin(); |  | ||||||
|         fprintf(stderr, "malloc() failed. Aborting...\n"); |  | ||||||
|         exit(EXIT_FAILURE); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* convert a wide char to multibyte string */ |  | ||||||
| char *wc_to_char(wchar_t ch) |  | ||||||
| { |  | ||||||
|     static char ret[MB_LEN_MAX + 1]; |  | ||||||
|     int len = wctomb(ret, ch); |  | ||||||
|  |  | ||||||
|     if (len == -1) { |  | ||||||
|         ret[0] = ' '; |  | ||||||
|         ret[1] = '\0'; |  | ||||||
|     } else { |  | ||||||
|         ret[len] = '\0'; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Returns 1 if connection has timed out, 0 otherwise */ |  | ||||||
| int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout) |  | ||||||
| { |  | ||||||
|     return timestamp + timeout <= curtime; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Colours the window tab according to type. Beeps if is_beep is true */ |  | ||||||
| void alert_window(ToxWindow *self, int type, bool is_beep) |  | ||||||
| { |  | ||||||
|     switch (type) { |  | ||||||
|         case WINDOW_ALERT_0: |  | ||||||
|             self->alert0 = true; |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case WINDOW_ALERT_1: |  | ||||||
|             self->alert1 = true; |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case WINDOW_ALERT_2: |  | ||||||
|             self->alert2 = true; |  | ||||||
|             break; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     StatusBar *stb = prompt->stb; |  | ||||||
|  |  | ||||||
|     if (is_beep && stb->status != TOX_USERSTATUS_BUSY && user_settings->alerts == ALERTS_ENABLED) |  | ||||||
|         beep(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* case-insensitive string compare function for use with qsort */ | /* case-insensitive string compare function for use with qsort */ | ||||||
| int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2) | int qsort_strcasecmp_hlpr(const void *str1, const void *str2) | ||||||
| { | { | ||||||
|     return strcasecmp((const char *) nick1, (const char *) nick2); |     return strcasecmp((const char *) str1, (const char *) str2); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | ||||||
|       - cannot be empty |       - cannot be empty | ||||||
|       - cannot start with a space |       - cannot start with a space | ||||||
|       - must not contain a forward slash (for logfile naming purposes) |       - must not contain a forward slash (for logfile naming purposes) | ||||||
|       - must not contain contiguous spaces */ |       - must not contain contiguous spaces | ||||||
| int valid_nick(uint8_t *nick) |       - must not contain a newline or tab seqeunce */ | ||||||
|  | int valid_nick(const char *nick) | ||||||
| { | { | ||||||
|     if (!nick[0] || nick[0] == ' ') |     if (!nick[0] || nick[0] == ' ') | ||||||
|         return 0; |         return 0; | ||||||
| @@ -213,49 +249,294 @@ int valid_nick(uint8_t *nick) | |||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; nick[i]; ++i) { |     for (i = 0; nick[i]; ++i) { | ||||||
|         if (nick[i] == ' ' && nick[i + 1] == ' ') |         if ((nick[i] == ' ' && nick[i + 1] == ' ') | ||||||
|             return 0; |                 || nick[i] == '/' | ||||||
|  |                 || nick[i] == '\n' | ||||||
|  |                 || nick[i] == '\t' | ||||||
|  |                 || nick[i] == '\v' | ||||||
|  |                 || nick[i] == '\r') | ||||||
|  |  | ||||||
|         if (nick[i] == '/') |  | ||||||
|             return 0; |             return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Moves cursor to the end of the line in given window */ | /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | ||||||
| void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x) | void filter_str(char *str, size_t len) | ||||||
| { | { | ||||||
|     int end_y = (len / max_x) + (max_y - CURS_Y_OFFSET); |     size_t i; | ||||||
|     int end_x = len % max_x; |  | ||||||
|     wmove(w, end_y, end_x); |     for (i = 0; i < len; ++i) { | ||||||
|  |         if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') | ||||||
|  |             str[i] = ' '; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* gets base file name from path or original file name if no path is supplied */ | /* gets base file name from path or original file name if no path is supplied. | ||||||
| void get_file_name(uint8_t *namebuf, uint8_t *pathname) |  * Returns the file name length | ||||||
|  |  */ | ||||||
|  | size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname) | ||||||
| { | { | ||||||
|     int idx = strlen(pathname) - 1; |     int len = strlen(pathname) - 1; | ||||||
|  |     char *path = strdup(pathname); | ||||||
|  |  | ||||||
|     while (idx >= 0 && pathname[idx] == '/') |     if (path == NULL) | ||||||
|         pathname[idx--] = '\0'; |         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); | ||||||
|  |  | ||||||
|     uint8_t *filename = strrchr(pathname, '/'); |     while (len >= 0 && pathname[len] == '/') | ||||||
|  |         path[len--] = '\0'; | ||||||
|  |  | ||||||
|     if (filename != NULL) { |     char *finalname = strdup(path); | ||||||
|         if (!strlen(++filename)) |  | ||||||
|             filename = pathname; |     if (finalname == NULL) | ||||||
|     } else { |         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); | ||||||
|         filename = pathname; |  | ||||||
|  |     const char *basenm = strrchr(path, '/'); | ||||||
|  |  | ||||||
|  |     if (basenm != NULL) { | ||||||
|  |         if (basenm[1]) | ||||||
|  |             strcpy(finalname, &basenm[1]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     snprintf(namebuf, MAX_STR_SIZE, "%s", filename); |     snprintf(namebuf, bufsize, "%s", finalname); | ||||||
|  |     free(finalname); | ||||||
|  |     free(path); | ||||||
|  |  | ||||||
|  |     return strlen(namebuf); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Gets the base directory of path and puts it in dir. | ||||||
|  |  * dir must have at least as much space as path_len + 1. | ||||||
|  |  * | ||||||
|  |  * Returns the length of the base directory. | ||||||
|  |  */ | ||||||
|  | size_t get_base_dir(const char *path, size_t path_len, char *dir) | ||||||
|  | { | ||||||
|  |     if (path_len == 0 || path == NULL) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     size_t dir_len = char_rfind(path, '/', path_len); | ||||||
|  |  | ||||||
|  |     if (dir_len != 0 && dir_len < path_len) | ||||||
|  |         ++dir_len;  /* Leave trailing slash */ | ||||||
|  |  | ||||||
|  |     memcpy(dir, path, dir_len); | ||||||
|  |     dir[dir_len] = '\0'; | ||||||
|  |  | ||||||
|  |     return dir_len; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* converts str to all lowercase */ | /* converts str to all lowercase */ | ||||||
| void str_to_lower(uint8_t *str) | void str_to_lower(char *str) | ||||||
| { | { | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; str[i]; ++i) |     for (i = 0; str[i]; ++i) | ||||||
|         str[i] = tolower(str[i]); |         str[i] = tolower(str[i]); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary. | ||||||
|  |    if toxcore API call fails, put UNKNOWN_NAME in buf | ||||||
|  |    Returns nick len */ | ||||||
|  | size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum) | ||||||
|  | { | ||||||
|  |     size_t len = tox_friend_get_name_size(m, friendnum, NULL); | ||||||
|  |  | ||||||
|  |     if (len == 0) { | ||||||
|  |         strcpy(buf, UNKNOWN_NAME); | ||||||
|  |         len = strlen(UNKNOWN_NAME); | ||||||
|  |     } else { | ||||||
|  |         tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||||
|  |     buf[len] = '\0'; | ||||||
|  |     filter_str(buf, len); | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* same as get_nick_truncate but for groupchats */ | ||||||
|  | int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum) | ||||||
|  | { | ||||||
|  |     TOX_ERR_GROUP_PEER_QUERY err; | ||||||
|  |     size_t len = tox_group_peer_get_name_size(m, groupnum, peer_id, &err); | ||||||
|  |  | ||||||
|  |     if (err != TOX_ERR_GROUP_PEER_QUERY_OK) { | ||||||
|  |         strcpy(buf, UNKNOWN_NAME); | ||||||
|  |         len = strlen(UNKNOWN_NAME); | ||||||
|  |     } else { | ||||||
|  |         tox_group_peer_get_name(m, groupnum, peer_id, (uint8_t *) buf, &err); | ||||||
|  |  | ||||||
|  |         if (err != TOX_ERR_GROUP_PEER_QUERY_OK) { | ||||||
|  |             strcpy(buf, UNKNOWN_NAME); | ||||||
|  |             len = strlen(UNKNOWN_NAME); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||||
|  |     buf[len] = '\0'; | ||||||
|  |     filter_str(buf, len); | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* copies data to msg buffer. | ||||||
|  |    returns length of msg. | ||||||
|  |    returns 0 and nulls msg if length is too big for buffer size */ | ||||||
|  | size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length) | ||||||
|  | { | ||||||
|  |     if (length > size - 1) { | ||||||
|  |         msg[0] = '\0'; | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(msg, data, length); | ||||||
|  |     msg[length] = '\0'; | ||||||
|  |     return length; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* returns index of the first instance of ch in s starting at idx. | ||||||
|  |    returns length of s if char not found or 0 if s is NULL. */ | ||||||
|  | int char_find(int idx, const char *s, char ch) | ||||||
|  | { | ||||||
|  |     if (!s) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int i = idx; | ||||||
|  |  | ||||||
|  |     for (i = idx; s[i]; ++i) { | ||||||
|  |         if (s[i] == ch) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return i; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* returns index of the last instance of ch in s starting at len. | ||||||
|  |    returns 0 if char not found or s is NULL (skips 0th index). */ | ||||||
|  | int char_rfind(const char *s, char ch, int len) | ||||||
|  | { | ||||||
|  |     if (!s) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int i = 0; | ||||||
|  |  | ||||||
|  |     for (i = len; i > 0; --i) { | ||||||
|  |         if (s[i] == ch) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return i; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Converts bytes to appropriate unit and puts in buf as a string */ | ||||||
|  | void bytes_convert_str(char *buf, int size, uint64_t bytes) | ||||||
|  | { | ||||||
|  |     double conv = bytes; | ||||||
|  |     const char *unit; | ||||||
|  |  | ||||||
|  |     if (conv < KiB) { | ||||||
|  |         unit = "Bytes"; | ||||||
|  |     } else if (conv < MiB) { | ||||||
|  |         unit = "KiB"; | ||||||
|  |         conv /= (double) KiB; | ||||||
|  |     } else if (conv < GiB) { | ||||||
|  |         unit = "MiB"; | ||||||
|  |         conv /= (double) MiB; | ||||||
|  |     } else { | ||||||
|  |         unit = "GiB"; | ||||||
|  |         conv /= (double) GiB; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(buf, size, "%.1f %s", conv, unit); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* checks if a file exists. Returns true or false */ | ||||||
|  | bool file_exists(const char *path) | ||||||
|  | { | ||||||
|  |     struct stat s; | ||||||
|  |     return stat(path, &s) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* returns file size. If file doesn't exist returns 0. */ | ||||||
|  | off_t file_size(const char *path) | ||||||
|  | { | ||||||
|  |     struct stat st; | ||||||
|  |  | ||||||
|  |     if (stat(path, &st) == -1) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     return st.st_size; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* compares the first size bytes of fp to signature. | ||||||
|  |    Returns 0 if they are the same, 1 if they differ, and -1 on error. | ||||||
|  |  | ||||||
|  |    On success this function will seek back to the beginning of fp */ | ||||||
|  | int check_file_signature(const char *signature, size_t size, FILE *fp) | ||||||
|  | { | ||||||
|  |     char buf[size]; | ||||||
|  |  | ||||||
|  |     if (fread(buf, size, 1, fp) != 1) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     int ret = memcmp(signature, buf, size); | ||||||
|  |  | ||||||
|  |     if (fseek(fp, 0L, SEEK_SET) == -1) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     return ret == 0 ? 0 : 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* sets window title in tab bar. */ | ||||||
|  | void set_window_title(ToxWindow *self, const char *title, int len) | ||||||
|  | { | ||||||
|  |     char cpy[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|  |  | ||||||
|  |     if (self->is_groupchat)   /* keep groupnumber in title */ | ||||||
|  |         snprintf(cpy, sizeof(cpy), "%d %s", self->num, title); | ||||||
|  |     else | ||||||
|  |         snprintf(cpy, sizeof(cpy), "%s", title); | ||||||
|  |  | ||||||
|  |     if (len > MAX_WINDOW_NAME_LENGTH) { | ||||||
|  |         strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "..."); | ||||||
|  |         cpy[MAX_WINDOW_NAME_LENGTH] = '\0'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(self->name, sizeof(self->name), "%s", cpy); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return true if address appears to be a valid ipv4 address. */ | ||||||
|  | bool is_ip4_address(const char *address) | ||||||
|  | { | ||||||
|  |     struct sockaddr_in s_addr; | ||||||
|  |     return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return true if address roughly appears to be a valid ipv6 address. | ||||||
|  |  * | ||||||
|  |  * TODO: Improve this function (inet_pton behaves strangely with ipv6). | ||||||
|  |  * for now the only guarantee is that it won't return true if the | ||||||
|  |  * address is a domain or ipv4 address, and should only be used if you're | ||||||
|  |  * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). | ||||||
|  |  */ | ||||||
|  | bool is_ip6_address(const char *address) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |     size_t num_colons = 0; | ||||||
|  |     char ch = 0; | ||||||
|  |  | ||||||
|  |     for (i = 0; (ch = address[i]); ++i) { | ||||||
|  |         if (ch == '.') { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (ch == ':') { | ||||||
|  |             ++num_colons; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return num_colons > 1 && num_colons < 8; | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										149
									
								
								src/misc_tools.h
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								src/misc_tools.h
									
									
									
									
									
								
							| @@ -19,8 +19,10 @@ | |||||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| #ifndef _misc_tools_h | #ifndef MISC_TOOLS_H | ||||||
| #define _misc_tools_h | #define MISC_TOOLS_H | ||||||
|  |  | ||||||
|  | #include <sys/stat.h> | ||||||
|  |  | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| @@ -33,60 +35,143 @@ | |||||||
| #define MAX(x, y) (((x) > (y)) ? (x) : (y)) | #define MAX(x, y) (((x) > (y)) ? (x) : (y)) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| /* convert a hex string to binary */ | #ifndef net_to_host | ||||||
| char *hex_string_to_bin(const char *hex_string); | #define net_to_host(x, y) hst_to_net(x, y) | ||||||
|  | #endif | ||||||
|  |  | ||||||
| /* get the current unix time */ | void hst_to_net(uint8_t *num, uint16_t numbytes); | ||||||
| uint64_t get_unix_time(void); |  | ||||||
|  |  | ||||||
| /*Puts the current time in buf in the format of [Hour:Min:Sec] */ | /* | ||||||
| void get_time_str(uint8_t *buf); |  * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. | ||||||
|  |  * output_size must be exactly half of hex_len. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size); | ||||||
|  |  | ||||||
| /* get the current local time */ | /* convert a hex string to bytes. returns 0 on success, -1 on failure */ | ||||||
|  | int hex_string_to_bytes(char *buf, int size, const char *keystr); | ||||||
|  |  | ||||||
|  | /* Converts a binary representation of a Tox ID into a string. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size); | ||||||
|  |  | ||||||
|  | /* get the current unix time (not thread safe) */ | ||||||
|  | time_t get_unix_time(void); | ||||||
|  |  | ||||||
|  | /* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */ | ||||||
|  | void get_time_str(char *buf, int bufsize); | ||||||
|  |  | ||||||
|  | /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||||
|  | void get_elapsed_time_str(char *buf, int bufsize, time_t secs); | ||||||
|  |  | ||||||
|  | /* Converts seconds to string in format H hours, m minutes, s seconds */ | ||||||
|  | void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs); | ||||||
|  |  | ||||||
|  | /* get the current local time (not thread safe) */ | ||||||
| struct tm *get_time(void); | struct tm *get_time(void); | ||||||
|  |  | ||||||
| /* updates current unix time (should be run once per do_toxic loop) */ |  | ||||||
| void update_unix_time(void); |  | ||||||
|  |  | ||||||
| /* Returns 1 if the string is empty, 0 otherwise */ | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
| int string_is_empty(char *string); | int string_is_empty(const char *string); | ||||||
|  |  | ||||||
|  | /* Same as above but for wide character strings */ | ||||||
|  | int wstring_is_empty(const wchar_t *string); | ||||||
|  |  | ||||||
| /* convert a multibyte string to a wide character string (must provide buffer) */ | /* convert a multibyte string to a wide character string (must provide buffer) */ | ||||||
| int char_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n); | int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||||
|  |  | ||||||
| /* converts wide character string into a multibyte string. | /* converts wide character string into a multibyte string and puts in buf. */ | ||||||
|    Same thing as wcs_to_mbs() but caller must provide its own buffer */ | int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n); | ||||||
| int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n); |  | ||||||
|  |  | ||||||
| /* convert wide characters to multibyte string: string returned must be free'd */ | /* convert a multibyte string to a wide character string and puts in buf) */ | ||||||
| uint8_t *wcs_to_mbs(wchar_t *string); | int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||||
|  |  | ||||||
| /* convert a wide char to multibyte char */ |  | ||||||
| char *wc_to_char(wchar_t ch); |  | ||||||
|  |  | ||||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | /* Returns 1 if connection has timed out, 0 otherwise */ | ||||||
| int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime); | int timed_out(time_t timestamp, time_t timeout); | ||||||
|  |  | ||||||
| /* Colours the window tab according to type. Beeps if is_beep is true */ | /* Colours the window tab according to type. Beeps if is_beep is true */ | ||||||
| void alert_window(ToxWindow *self, int type, bool is_beep); | void alert_window(ToxWindow *self, int type, bool is_beep); | ||||||
|  |  | ||||||
| /* case-insensitive string compare function for use with qsort */ | /* case-insensitive string compare function for use with qsort */ | ||||||
| int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2); | int qsort_strcasecmp_hlpr(const void *str1, const void *str2); | ||||||
|  |  | ||||||
| /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | ||||||
|       - cannot be empty |       - cannot be empty | ||||||
|       - cannot start with a space |       - cannot start with a space | ||||||
|       - must not contain a forward slash (for logfile naming purposes) |       - must not contain a forward slash (for logfile naming purposes) | ||||||
|       - must not contain contiguous spaces */ |       - must not contain contiguous spaces | ||||||
| int valid_nick(uint8_t *nick); |       - must not contain a newline or tab seqeunce */ | ||||||
|  | int valid_nick(const char *nick); | ||||||
|  |  | ||||||
| /* Moves the cursor to the end of the line in given window */ | /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | ||||||
| void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x); | void filter_str(char *str, size_t len); | ||||||
|  |  | ||||||
| /* gets base file name from path or original file name if no path is supplied */ | /* gets base file name from path or original file name if no path is supplied */ | ||||||
| void get_file_name(uint8_t *namebuf, uint8_t *pathname); | size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname); | ||||||
|  |  | ||||||
| /* converts str to all uppercase */ | /* Gets the base directory of path and puts it in dir. | ||||||
| void str_to_lower(uint8_t *str); |  * dir must have at least as much space as path_len. | ||||||
|  |  * | ||||||
|  |  * Returns the length of the base directory on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | size_t get_base_dir(const char *path, size_t path_len, char *dir); | ||||||
|  |  | ||||||
| #endif /* #define _misc_tools_h */ | /* converts str to all lowercase */ | ||||||
|  | void str_to_lower(char *str); | ||||||
|  |  | ||||||
|  | /* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary. | ||||||
|  |    Returns nick len on success, -1 on failure */ | ||||||
|  | size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | ||||||
|  |  | ||||||
|  | /* same as get_nick_truncate but for groupchats */ | ||||||
|  | int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum); | ||||||
|  |  | ||||||
|  | /* copies data to msg buffer. | ||||||
|  |    returns length of msg. | ||||||
|  |    returns 0 and nulls msg if length is too big for buffer size */ | ||||||
|  | size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length); | ||||||
|  |  | ||||||
|  | /* returns index of the first instance of ch in s starting at idx. | ||||||
|  |    returns length of s if char not found or 0 if s is NULL. */ | ||||||
|  | int char_find(int idx, const char *s, char ch); | ||||||
|  |  | ||||||
|  | /* returns index of the last instance of ch in s starting at len. | ||||||
|  |    returns 0 if char not found or s is NULL (skips 0th index). */ | ||||||
|  | int char_rfind(const char *s, char ch, int len); | ||||||
|  |  | ||||||
|  | /* Converts bytes to appropriate unit and puts in buf as a string */ | ||||||
|  | void bytes_convert_str(char *buf, int size, uint64_t bytes); | ||||||
|  |  | ||||||
|  | /* checks if a file exists. Returns true or false */ | ||||||
|  | bool file_exists(const char *path); | ||||||
|  |  | ||||||
|  | /* returns file size. If file doesn't exist returns 0. */ | ||||||
|  | off_t file_size(const char *path); | ||||||
|  |  | ||||||
|  | /* compares the first size bytes of fp and signature. | ||||||
|  |    Returns 0 if they are the same, 1 if they differ, and -1 on error. | ||||||
|  |  | ||||||
|  |    On success this function will seek back to the beginning of fp */ | ||||||
|  | int check_file_signature(const char *signature, size_t size, FILE *fp); | ||||||
|  |  | ||||||
|  | /* sets window title in tab bar. */ | ||||||
|  | void set_window_title(ToxWindow *self, const char *title, int len); | ||||||
|  |  | ||||||
|  | /* Return true if address appears to be a valid ipv4 address. */ | ||||||
|  | bool is_ip4_address(const char *address); | ||||||
|  |  | ||||||
|  | /* Return true if address roughly appears to be a valid ipv6 address. | ||||||
|  |  * | ||||||
|  |  * TODO: Improve this function (inet_pton behaves strangely with ipv6). | ||||||
|  |  * for now the only guarantee is that it won't return true if the | ||||||
|  |  * address is a domain or ipv4 address, and should only be used if you're | ||||||
|  |  * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). | ||||||
|  |  */ | ||||||
|  | bool is_ip6_address(const char *address); | ||||||
|  |  | ||||||
|  | #endif /* #define MISC_TOOLS_H */ | ||||||
|   | |||||||
							
								
								
									
										401
									
								
								src/name_lookup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										401
									
								
								src/name_lookup.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,401 @@ | |||||||
|  | /*  name_lookup.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2015 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <curl/curl.h> | ||||||
|  |  | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "global_commands.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "configdir.h" | ||||||
|  | #include "curl_util.h" | ||||||
|  |  | ||||||
|  | extern struct arg_opts arg_opts; | ||||||
|  | extern struct Winthread Winthread; | ||||||
|  |  | ||||||
|  | #define NAMESERVER_API_PATH "api" | ||||||
|  | #define SERVER_KEY_SIZE 32 | ||||||
|  | #define MAX_SERVERS 50 | ||||||
|  | #define MAX_DOMAIN_SIZE 32 | ||||||
|  | #define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3 | ||||||
|  |  | ||||||
|  | struct Nameservers { | ||||||
|  |     int     lines; | ||||||
|  |     char    names[MAX_SERVERS][MAX_DOMAIN_SIZE]; | ||||||
|  |     char    keys[MAX_SERVERS][SERVER_KEY_SIZE]; | ||||||
|  | } Nameservers; | ||||||
|  |  | ||||||
|  | static struct thread_data { | ||||||
|  |     Tox       *m; | ||||||
|  |     ToxWindow *self; | ||||||
|  |     char    id_bin[TOX_ADDRESS_SIZE]; | ||||||
|  |     char    addr[MAX_STR_SIZE]; | ||||||
|  |     char    msg[MAX_STR_SIZE]; | ||||||
|  |     bool    disabled; | ||||||
|  |     volatile bool busy; | ||||||
|  | } t_data; | ||||||
|  |  | ||||||
|  | static struct lookup_thread { | ||||||
|  |     pthread_t tid; | ||||||
|  |     pthread_attr_t attr; | ||||||
|  | } lookup_thread; | ||||||
|  |  | ||||||
|  | static int lookup_error(ToxWindow *self, const char *errmsg, ...) | ||||||
|  | { | ||||||
|  |     char frmt_msg[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |     va_list args; | ||||||
|  |     va_start(args, errmsg); | ||||||
|  |     vsnprintf(frmt_msg, sizeof(frmt_msg), errmsg, args); | ||||||
|  |     va_end(args); | ||||||
|  |  | ||||||
|  |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg); | ||||||
|  |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void kill_lookup_thread(void) | ||||||
|  | { | ||||||
|  |     memset(&t_data, 0, sizeof(struct thread_data)); | ||||||
|  |     pthread_attr_destroy(&lookup_thread.attr); | ||||||
|  |     pthread_exit(NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Attempts to load the nameserver list pointed at by path into the Nameservers structure. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * -1 is reserved. | ||||||
|  |  * Returns -2 if the supplied path does not exist. | ||||||
|  |  * Returns -3 if the list does not contain any valid entries. | ||||||
|  |  */ | ||||||
|  | static int load_nameserver_list(const char *path) | ||||||
|  | { | ||||||
|  |     FILE *fp = fopen(path, "r"); | ||||||
|  |  | ||||||
|  |     if (fp == NULL) | ||||||
|  |         return -2; | ||||||
|  |  | ||||||
|  |     char line[MAX_SERVER_LINE]; | ||||||
|  |  | ||||||
|  |     while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) { | ||||||
|  |         int linelen = strlen(line); | ||||||
|  |  | ||||||
|  |         if (linelen < SERVER_KEY_SIZE * 2 + 5) | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|  |         if (line[linelen - 1] == '\n') | ||||||
|  |             line[--linelen] = '\0'; | ||||||
|  |  | ||||||
|  |         const char *name = strtok(line, " "); | ||||||
|  |         const char *keystr = strtok(NULL, " "); | ||||||
|  |  | ||||||
|  |         if (name == NULL || keystr == NULL) | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|  |         if (strlen(keystr) != SERVER_KEY_SIZE * 2) | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|  |         snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name); | ||||||
|  |         int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr); | ||||||
|  |  | ||||||
|  |         if (res == -1) | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|  |         ++Nameservers.lines; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fclose(fp); | ||||||
|  |  | ||||||
|  |     if (Nameservers.lines < 1) | ||||||
|  |         return -3; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Takes address addr in the form "username@domain", puts the username in namebuf, | ||||||
|  |  * and the domain in dombuf. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure | ||||||
|  |  */ | ||||||
|  | static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz) | ||||||
|  | { | ||||||
|  |     if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH))) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     char tmpaddr[MAX_STR_SIZE]; | ||||||
|  |     char *tmpname = NULL; | ||||||
|  |     char *tmpdom = NULL; | ||||||
|  |  | ||||||
|  |     snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr); | ||||||
|  |     tmpname = strtok(tmpaddr, "@"); | ||||||
|  |     tmpdom = strtok(NULL, ""); | ||||||
|  |  | ||||||
|  |     if (tmpname == NULL || tmpdom == NULL) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     str_to_lower(tmpdom); | ||||||
|  |     snprintf(namebuf, namebuf_sz, "%s", tmpname); | ||||||
|  |     snprintf(dombuf, dombuf_sz, "%s", tmpdom); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* matches input domain name with domains in list and obtains key. | ||||||
|  |  * Turns out_domain into the full domain we need to make a POST request. | ||||||
|  |  * | ||||||
|  |  * Return true on match. | ||||||
|  |  * Returns false on no match. | ||||||
|  |  */ | ||||||
|  | static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_size, const char *inputdomain) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < Nameservers.lines; ++i) { | ||||||
|  |         if (strcmp(Nameservers.names[i], inputdomain) == 0) { | ||||||
|  |             memcpy(pubkey, Nameservers.keys[i], SERVER_KEY_SIZE); | ||||||
|  |             snprintf(out_domain, out_domain_size, "https://%s/%s", Nameservers.names[i], NAMESERVER_API_PATH); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | #define ID_PREFIX "\"tox_id\": \"" | ||||||
|  | static int process_response(struct Recv_Curl_Data *recv_data) | ||||||
|  | { | ||||||
|  |     size_t prefix_size = strlen(ID_PREFIX); | ||||||
|  |  | ||||||
|  |     if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     const char *IDstart = strstr(recv_data->data, ID_PREFIX); | ||||||
|  |  | ||||||
|  |     if (IDstart == NULL) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     char ID_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||||
|  |     memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2); | ||||||
|  |     ID_string[TOX_ADDRESS_SIZE * 2] = 0; | ||||||
|  |  | ||||||
|  |     if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void *lookup_thread_func(void *data) | ||||||
|  | { | ||||||
|  |     ToxWindow *self = t_data.self; | ||||||
|  |  | ||||||
|  |     char input_domain[MAX_STR_SIZE]; | ||||||
|  |     char name[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |     if (parse_addr(t_data.addr, name, sizeof(name), input_domain, sizeof(input_domain)) == -1) { | ||||||
|  |         lookup_error(self, "Input must be a 76 character Tox ID or an address in the form: username@domain"); | ||||||
|  |         kill_lookup_thread(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char nameserver_key[SERVER_KEY_SIZE]; | ||||||
|  |     char real_domain[MAX_DOMAIN_SIZE]; | ||||||
|  |  | ||||||
|  |     if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) { | ||||||
|  |         if (!strcasecmp(input_domain, "utox.org")) | ||||||
|  |             lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic."); | ||||||
|  |         else | ||||||
|  |             lookup_error(self, "Name server domain not found."); | ||||||
|  |  | ||||||
|  |         kill_lookup_thread(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     CURL *c_handle = curl_easy_init(); | ||||||
|  |  | ||||||
|  |     if (!c_handle) { | ||||||
|  |         lookup_error(self, "curl handler error"); | ||||||
|  |         kill_lookup_thread(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     struct Recv_Curl_Data recv_data; | ||||||
|  |  | ||||||
|  |     memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); | ||||||
|  |  | ||||||
|  |     char post_data[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |     snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name); | ||||||
|  |  | ||||||
|  |     struct curl_slist *headers = NULL; | ||||||
|  |  | ||||||
|  |     headers = curl_slist_append(headers, "Content-Type: application/json"); | ||||||
|  |  | ||||||
|  |     headers = curl_slist_append(headers, "charsets: utf-8"); | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers); | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_URL, real_domain); | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data); | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, &recv_data); | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); | ||||||
|  |  | ||||||
|  |     curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data); | ||||||
|  |  | ||||||
|  |     int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type); | ||||||
|  |  | ||||||
|  |     if (proxy_ret != 0) { | ||||||
|  |         lookup_error(self, "Failed to set proxy (error %d)\n"); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         lookup_error(self, "TLS could not be enabled (libcurl error %d)", ret); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         lookup_error(self, "TLSv1.2 could not be set (libcurl error %d)", ret); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         lookup_error(self, "Failed to set TLS cipher list (libcurl error %d)", ret); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = curl_easy_perform(c_handle); | ||||||
|  |  | ||||||
|  |     if (ret != CURLE_OK) { | ||||||
|  |         /* If system doesn't support any of the specified ciphers suites, fall back to default */ | ||||||
|  |         if (ret == CURLE_SSL_CIPHER) { | ||||||
|  |             curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL); | ||||||
|  |             ret = curl_easy_perform(c_handle); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (ret != CURLE_OK) { | ||||||
|  |             lookup_error(self, "HTTPS lookup error (libcurl error %d)", ret); | ||||||
|  |             goto on_exit; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (process_response(&recv_data) == -1) { | ||||||
|  |         lookup_error(self, "Bad response."); | ||||||
|  |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |     cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg); | ||||||
|  |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|  | on_exit: | ||||||
|  |     curl_slist_free_all(headers); | ||||||
|  |     curl_easy_cleanup(c_handle); | ||||||
|  |     kill_lookup_thread(); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message) | ||||||
|  | { | ||||||
|  |     if (t_data.disabled) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (t_data.busy) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin); | ||||||
|  |     snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr); | ||||||
|  |     snprintf(t_data.msg, sizeof(t_data.msg), "%s", message); | ||||||
|  |     t_data.self = self; | ||||||
|  |     t_data.m = m; | ||||||
|  |     t_data.busy = true; | ||||||
|  |  | ||||||
|  |     if (pthread_attr_init(&lookup_thread.attr) != 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init"); | ||||||
|  |         memset(&t_data, 0, sizeof(struct thread_data)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set"); | ||||||
|  |         pthread_attr_destroy(&lookup_thread.attr); | ||||||
|  |         memset(&t_data, 0, sizeof(struct thread_data)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init"); | ||||||
|  |         pthread_attr_destroy(&lookup_thread.attr); | ||||||
|  |         memset(&t_data, 0, sizeof(struct thread_data)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Initializes http based name lookups. Note: This function must be called only once before additional | ||||||
|  |  * threads are spawned. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 if curl failed to init. | ||||||
|  |  * Returns -2 if the nameserver list cannot be found. | ||||||
|  |  * Returns -3 if the nameserver list does not contain any valid entries. | ||||||
|  |  */ | ||||||
|  | int name_lookup_init(int curl_init_status) | ||||||
|  | { | ||||||
|  |     if (curl_init_status != 0) { | ||||||
|  |         t_data.disabled = true; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char *path = arg_opts.nameserver_path[0] ? arg_opts.nameserver_path : PACKAGE_DATADIR "/nameservers"; | ||||||
|  |     int ret = load_nameserver_list(path); | ||||||
|  |  | ||||||
|  |     if (ret != 0) { | ||||||
|  |         t_data.disabled = true; | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								src/name_lookup.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/name_lookup.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | /*  name_lookup.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2015 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef NAME_LOOKUP | ||||||
|  | #define NAME_LOOKUP | ||||||
|  |  | ||||||
|  | /* Initializes http based name lookups. Note: This function must be called only once before additional | ||||||
|  |  * threads are spawned. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int name_lookup_init(int curl_init_status); | ||||||
|  |  | ||||||
|  | int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message); | ||||||
|  |  | ||||||
|  | #endif /* NAME_LOOKUP */ | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user