mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-11-01 13:16:52 +01:00 
			
		
		
		
	Compare commits
	
		
			493 Commits
		
	
	
		
			v0.5.2
			...
			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 | 
							
								
								
									
										53
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,53 +0,0 @@ | ||||
| language: c | ||||
| compiler: | ||||
|   - gcc | ||||
|   # - clang  # Fix me | ||||
|  | ||||
| before_script: | ||||
|   # Installing yasm (needed for compiling vpx) and openal | ||||
|   - sudo apt-get -yq install yasm libopenal-dev libconfig-dev libalut-dev libnotify-dev asciidoc | ||||
|   # Installing libsodium, needed for toxcore | ||||
|   - git clone https://github.com/jedisct1/libsodium.git libsodium | ||||
|   - cd libsodium | ||||
|   - git checkout tags/0.7.0 > /dev/null | ||||
|   - ./autogen.sh > /dev/null | ||||
|   - ./configure > /dev/null | ||||
|   - make check -j2 || make check || exit 1 > /dev/null | ||||
|   - sudo make install > /dev/null | ||||
|   - cd .. | ||||
|   # Installing libopus, needed for audio encoding/decoding | ||||
|   - wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz | ||||
|   - tar xzf opus-1.0.3.tar.gz > /dev/null | ||||
|   - cd opus-1.0.3 | ||||
|   - ./configure > /dev/null | ||||
|   - make -j2 || make || exit 1 > /dev/null | ||||
|   - sudo make install > /dev/null | ||||
|   - cd .. | ||||
|   # Installing vpx | ||||
|   - git clone http://git.chromium.org/webm/libvpx.git libvpx | ||||
|   - cd libvpx | ||||
|   - ./configure --enable-shared > /dev/null | ||||
|   - make -j2 || make || exit 1 > /dev/null | ||||
|   - sudo make install > /dev/null | ||||
|   - cd .. | ||||
|   # Creating libraries links and updating cache | ||||
|   - sudo ldconfig > /dev/null | ||||
|   # Installing toxcore | ||||
|   - git clone https://github.com/irungentoo/toxcore.git toxcore | ||||
|   - cd toxcore | ||||
|   - autoreconf -i | ||||
|   - ./configure --disable-tests --disable-ntox --disable-daemon --enable-av | ||||
|   - make -j2 || make || exit 1 | ||||
|   - sudo make install | ||||
|   - cd .. | ||||
| script: | ||||
|   - cd build | ||||
|   - make -j2 || make || exit 1 | ||||
| notifications: | ||||
|   email: false | ||||
|  | ||||
|   irc:  | ||||
|     channels: | ||||
|       - "chat.freenode.net#tox-dev" | ||||
|     on_success: always | ||||
|     on_failure: always | ||||
							
								
								
									
										503
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										503
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,503 @@ | ||||
| # Change Log | ||||
|  | ||||
| ## [Unreleased](https://github.com/JFreegman/toxic/tree/HEAD) | ||||
|  | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.7.0...HEAD) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - How can I copy everything from one computer to another? [\#391](https://github.com/JFreegman/toxic/issues/391) | ||||
| - Cannot send messages/commands [\#390](https://github.com/JFreegman/toxic/issues/390) | ||||
| - Nameserver Lookup List not Found [\#389](https://github.com/JFreegman/toxic/issues/389) | ||||
| - ERROR: toxini file 'tox.ini' not found [\#388](https://github.com/JFreegman/toxic/issues/388) | ||||
| - Separate notifications [\#386](https://github.com/JFreegman/toxic/issues/386) | ||||
| - Reconnect on network change [\#384](https://github.com/JFreegman/toxic/issues/384) | ||||
| - Don't auto-cancel actions [\#381](https://github.com/JFreegman/toxic/issues/381) | ||||
| - How to export your profile? [\#377](https://github.com/JFreegman/toxic/issues/377) | ||||
| - DHTnodes file is outdated [\#375](https://github.com/JFreegman/toxic/issues/375) | ||||
| - Toxic fails to initialize if ~/.config directory doesn't exist [\#372](https://github.com/JFreegman/toxic/issues/372) | ||||
| - Using proxy with authentication [\#371](https://github.com/JFreegman/toxic/issues/371) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Add multiline support [\#387](https://github.com/JFreegman/toxic/pull/387) ([mphe](https://github.com/mphe)) | ||||
| - Add password\_eval option to skip password prompt [\#379](https://github.com/JFreegman/toxic/pull/379) ([FreakyPenguin](https://github.com/FreakyPenguin)) | ||||
| - sleep use tox\_iteration\_interval [\#374](https://github.com/JFreegman/toxic/pull/374) ([quininer](https://github.com/quininer)) | ||||
| - Fix \#372 - can't start with missing ~/.config [\#373](https://github.com/JFreegman/toxic/pull/373) ([wedge-jarrad](https://github.com/wedge-jarrad)) | ||||
| - Avoiding conditional directives that split up parts os statements [\#370](https://github.com/JFreegman/toxic/pull/370) ([RomeroMalaquias](https://github.com/RomeroMalaquias)) | ||||
| - update doc: DATA\_FILE is now `toxic\_profile.tox` [\#369](https://github.com/JFreegman/toxic/pull/369) ([nil0x42](https://github.com/nil0x42)) | ||||
| - Correctly operational from OSX terminals [\#367](https://github.com/JFreegman/toxic/pull/367) ([landswellsong](https://github.com/landswellsong)) | ||||
|  | ||||
| ## [v0.7.0](https://github.com/JFreegman/toxic/tree/v0.7.0) (2015-11-12) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.1...v0.7.0) | ||||
|  | ||||
| **Implemented enhancements:** | ||||
|  | ||||
| - /myid doesn't show qrcode [\#326](https://github.com/JFreegman/toxic/issues/326) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Installation failed on ubuntu 12.04, package missing [\#279](https://github.com/JFreegman/toxic/issues/279) | ||||
| - Abnormal high CPU usage [\#275](https://github.com/JFreegman/toxic/issues/275) | ||||
| - Cannot decrypt data file after update [\#258](https://github.com/JFreegman/toxic/issues/258) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Compiling video\_device.c on FreeBSD [\#364](https://github.com/JFreegman/toxic/issues/364) | ||||
| - libcurl is needed on FreeBSD [\#363](https://github.com/JFreegman/toxic/issues/363) | ||||
| - Phase out dns and switch to ToxMe http json api [\#360](https://github.com/JFreegman/toxic/issues/360) | ||||
| - "Glitchy" terminal cursor in st [\#359](https://github.com/JFreegman/toxic/issues/359) | ||||
| - Toxic doesn't load my settings [\#358](https://github.com/JFreegman/toxic/issues/358) | ||||
| - Does Toxic support proxy? [\#355](https://github.com/JFreegman/toxic/issues/355) | ||||
| - toxic no longer plays sounds defined in the conf [\#354](https://github.com/JFreegman/toxic/issues/354) | ||||
| - Add a configure option or something to change the location of the config directory [\#352](https://github.com/JFreegman/toxic/issues/352) | ||||
| - Remove/Replace links to libtoxcore.so [\#349](https://github.com/JFreegman/toxic/issues/349) | ||||
| - "No pending friend requests." while"Friend request has already been sent." [\#348](https://github.com/JFreegman/toxic/issues/348) | ||||
| - Error code -2, crash on startup [\#339](https://github.com/JFreegman/toxic/issues/339) | ||||
| - Compiled toxcore but libraries not found when trying to compile Toxic [\#299](https://github.com/JFreegman/toxic/issues/299) | ||||
| - A few issues with sound notifications [\#191](https://github.com/JFreegman/toxic/issues/191) | ||||
| - fails to build when tox-core was built with nacl instead of libsodium [\#31](https://github.com/JFreegman/toxic/issues/31) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fix spelling mistake BOARDER -\> BORDER [\#362](https://github.com/JFreegman/toxic/pull/362) ([subliun](https://github.com/subliun)) | ||||
| - Fix compile for DragonFlyBSD [\#351](https://github.com/JFreegman/toxic/pull/351) ([mneumann](https://github.com/mneumann)) | ||||
|  | ||||
| ## [v0.6.1](https://github.com/JFreegman/toxic/tree/v0.6.1) (2015-08-28) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.0...v0.6.1) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - \[Invalid UTF-8\] [\#344](https://github.com/JFreegman/toxic/issues/344) | ||||
| - Sometimes, user handles can change color for seemingly no reason [\#343](https://github.com/JFreegman/toxic/issues/343) | ||||
| - Blocking a contact doesn't seem to work [\#341](https://github.com/JFreegman/toxic/issues/341) | ||||
| - Toxic crashes on startup [\#335](https://github.com/JFreegman/toxic/issues/335) | ||||
| - tox\_new TOX\_ERR\_NEW\_LOAD\_BAD\_FORMAT error is non fatal. [\#333](https://github.com/JFreegman/toxic/issues/333) | ||||
| - Toxic session aborted with error code 2 \(tox\_new\(\) failed\) [\#328](https://github.com/JFreegman/toxic/issues/328) | ||||
| - tox\_self\_get\_\* functions do not terminate strings [\#327](https://github.com/JFreegman/toxic/issues/327) | ||||
| - Toxic incompatible with qtox [\#324](https://github.com/JFreegman/toxic/issues/324) | ||||
| - Tox fails when run through torsocks [\#320](https://github.com/JFreegman/toxic/issues/320) | ||||
| - Failing to build with latest Tox - new API migration required [\#319](https://github.com/JFreegman/toxic/issues/319) | ||||
| - Avoid non-posix option in sed. [\#307](https://github.com/JFreegman/toxic/issues/307) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - fix a broken link [\#350](https://github.com/JFreegman/toxic/pull/350) ([vinegret](https://github.com/vinegret)) | ||||
| - Makefile: allow overriding pkg-config [\#346](https://github.com/JFreegman/toxic/pull/346) ([ony](https://github.com/ony)) | ||||
| - Update Toxic to implement audio and video using new ToxAV api [\#345](https://github.com/JFreegman/toxic/pull/345) ([cnhenry](https://github.com/cnhenry)) | ||||
| - travis.yml: update dependencies [\#340](https://github.com/JFreegman/toxic/pull/340) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add localization system \(gettext\) [\#337](https://github.com/JFreegman/toxic/pull/337) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: try to fix Tox/toxic\#307 [\#323](https://github.com/JFreegman/toxic/pull/323) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: add uninstall target [\#322](https://github.com/JFreegman/toxic/pull/322) ([Ansa89](https://github.com/Ansa89)) | ||||
|  | ||||
| ## [v0.6.0](https://github.com/JFreegman/toxic/tree/v0.6.0) (2015-03-28) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.2...v0.6.0) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Please do not force push to tox/toxic master branch. [\#311](https://github.com/JFreegman/toxic/issues/311) | ||||
| - Import tox id [\#295](https://github.com/JFreegman/toxic/issues/295) | ||||
| - openalut [\#287](https://github.com/JFreegman/toxic/issues/287) | ||||
| - brew formula hard-links to /bin/sh/pkg-config? \(OS X\) [\#286](https://github.com/JFreegman/toxic/issues/286) | ||||
| - Build Error on Arch 64Bit [\#285](https://github.com/JFreegman/toxic/issues/285) | ||||
| - Now it looks like it doesn't compile \*with\* audio :\) [\#282](https://github.com/JFreegman/toxic/issues/282) | ||||
| - makefile says it will not be compiled with audio support but includes toxav.h anyway. [\#281](https://github.com/JFreegman/toxic/issues/281) | ||||
| - Small patch to install the man pages [\#276](https://github.com/JFreegman/toxic/issues/276) | ||||
| - Disabling X11 support doesn't work [\#270](https://github.com/JFreegman/toxic/issues/270) | ||||
| - Support arrow keys [\#265](https://github.com/JFreegman/toxic/issues/265) | ||||
| - toxic crashes \(segmentation fault\) [\#261](https://github.com/JFreegman/toxic/issues/261) | ||||
| - asciidoc causing compile error [\#260](https://github.com/JFreegman/toxic/issues/260) | ||||
| - これはセグフォールトですか [\#259](https://github.com/JFreegman/toxic/issues/259) | ||||
| - Verify ~/.config/tox permissions on startup [\#245](https://github.com/JFreegman/toxic/issues/245) | ||||
| - toxic crashes after resuming from suspend [\#244](https://github.com/JFreegman/toxic/issues/244) | ||||
| - Toxic does not compile on osx 10.9.3 [\#145](https://github.com/JFreegman/toxic/issues/145) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - README.md: fix typo [\#318](https://github.com/JFreegman/toxic/pull/318) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: be less aggressive when cleaning [\#316](https://github.com/JFreegman/toxic/pull/316) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Move makefile into root directory [\#315](https://github.com/JFreegman/toxic/pull/315) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fixing couple leaking file descriptors [\#314](https://github.com/JFreegman/toxic/pull/314) ([al42and](https://github.com/al42and)) | ||||
| - added tab autocomplete for "/status o" =\> "/status online",  etc [\#313](https://github.com/JFreegman/toxic/pull/313) ([hardlyeven](https://github.com/hardlyeven)) | ||||
| - Some cosmetics changes [\#310](https://github.com/JFreegman/toxic/pull/310) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Openbsd [\#308](https://github.com/JFreegman/toxic/pull/308) ([henriqueleng](https://github.com/henriqueleng)) | ||||
| - Add support for custom timestamps in chat and logs. [\#303](https://github.com/JFreegman/toxic/pull/303) ([louipc](https://github.com/louipc)) | ||||
| - README.md: update download section [\#302](https://github.com/JFreegman/toxic/pull/302) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add INSTALL.md [\#301](https://github.com/JFreegman/toxic/pull/301) ([Ansa89](https://github.com/Ansa89)) | ||||
| - travis.yml: use latest libsodium stable [\#298](https://github.com/JFreegman/toxic/pull/298) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Travis should build with Libsodium stable, fix clang [\#297](https://github.com/JFreegman/toxic/pull/297) ([urras](https://github.com/urras)) | ||||
| - Interface [\#296](https://github.com/JFreegman/toxic/pull/296) ([louipc](https://github.com/louipc)) | ||||
| - Correct filename comment from main.c to toxic.c [\#293](https://github.com/JFreegman/toxic/pull/293) ([Spagy](https://github.com/Spagy)) | ||||
| - Update for toxcore API break [\#292](https://github.com/JFreegman/toxic/pull/292) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fix some edge cases when obtaining paths [\#291](https://github.com/JFreegman/toxic/pull/291) ([dantok](https://github.com/dantok)) | ||||
| - Update DHT nodes again [\#290](https://github.com/JFreegman/toxic/pull/290) ([urras](https://github.com/urras)) | ||||
| - Update DHT node list [\#289](https://github.com/JFreegman/toxic/pull/289) ([urras](https://github.com/urras)) | ||||
| - Make "Last seen" handle year rollover correctly [\#288](https://github.com/JFreegman/toxic/pull/288) ([flussence](https://github.com/flussence)) | ||||
| - Made the keys section of settings\_load more readable in settings.c [\#284](https://github.com/JFreegman/toxic/pull/284) ([jpoler](https://github.com/jpoler)) | ||||
| - Destroy AL context before closing dhndl [\#283](https://github.com/JFreegman/toxic/pull/283) ([stal888](https://github.com/stal888)) | ||||
| - Darwin Build [\#280](https://github.com/JFreegman/toxic/pull/280) ([DomT4](https://github.com/DomT4)) | ||||
| - Fix Tox/toxic\#276 [\#278](https://github.com/JFreegman/toxic/pull/278) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: revert back to mkdir [\#274](https://github.com/JFreegman/toxic/pull/274) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: add toxic.desktop to install target [\#273](https://github.com/JFreegman/toxic/pull/273) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Toxic.conf.exmaple: fix sound namefile [\#271](https://github.com/JFreegman/toxic/pull/271) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Version: fix revision calculation [\#269](https://github.com/JFreegman/toxic/pull/269) ([Ansa89](https://github.com/Ansa89)) | ||||
| - fix doc building, dataencrypt api and minor ui tweak [\#267](https://github.com/JFreegman/toxic/pull/267) ([louipc](https://github.com/louipc)) | ||||
| - Change action messages indicator [\#264](https://github.com/JFreegman/toxic/pull/264) ([zetok](https://github.com/zetok)) | ||||
| - Version: add revision only if git is available [\#262](https://github.com/JFreegman/toxic/pull/262) ([Ansa89](https://github.com/Ansa89)) | ||||
|  | ||||
| ## [v0.5.2](https://github.com/JFreegman/toxic/tree/v0.5.2) (2014-09-29) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.1...v0.5.2) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Failed to read log file [\#254](https://github.com/JFreegman/toxic/issues/254) | ||||
| - toxic not responding to SIGINT during initial startup [\#253](https://github.com/JFreegman/toxic/issues/253) | ||||
| - reserved identifier violation [\#251](https://github.com/JFreegman/toxic/issues/251) | ||||
| - Fix signal handler [\#250](https://github.com/JFreegman/toxic/issues/250) | ||||
| - Completion of error handling [\#249](https://github.com/JFreegman/toxic/issues/249) | ||||
| - How to decline file sends? [\#247](https://github.com/JFreegman/toxic/issues/247) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fix "error: unknown type name 'off\_t'" [\#255](https://github.com/JFreegman/toxic/pull/255) ([Ansa89](https://github.com/Ansa89)) | ||||
| - rm -rf -\> rm -f [\#252](https://github.com/JFreegman/toxic/pull/252) ([ghost](https://github.com/ghost)) | ||||
| - Update screenshot [\#246](https://github.com/JFreegman/toxic/pull/246) ([urras](https://github.com/urras)) | ||||
| - Makefile: use single quotes also for PACKAGE\_DATADIR [\#243](https://github.com/JFreegman/toxic/pull/243) ([Ansa89](https://github.com/Ansa89)) | ||||
|  | ||||
| ## [v0.5.1](https://github.com/JFreegman/toxic/tree/v0.5.1) (2014-09-19) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.0...v0.5.1) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Support for faux offline messaging [\#233](https://github.com/JFreegman/toxic/issues/233) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Usage help: add missing comma [\#242](https://github.com/JFreegman/toxic/pull/242) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fix some 'clang --analyze' warnings [\#240](https://github.com/JFreegman/toxic/pull/240) ([s3erios](https://github.com/s3erios)) | ||||
| - Addition to Tox/toxic\#235 [\#238](https://github.com/JFreegman/toxic/pull/238) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Some code simplification [\#236](https://github.com/JFreegman/toxic/pull/236) ([s3erios](https://github.com/s3erios)) | ||||
| - Add X11 option [\#235](https://github.com/JFreegman/toxic/pull/235) ([s3erios](https://github.com/s3erios)) | ||||
|  | ||||
| ## [v0.5.0](https://github.com/JFreegman/toxic/tree/v0.5.0) (2014-09-01) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.7...v0.5.0) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - 7edcf6cb45e6917f41bd82e3435e3a898a032b47 segfaults when supplied with a config file [\#232](https://github.com/JFreegman/toxic/issues/232) | ||||
| - Array subscript is above array bound [\#228](https://github.com/JFreegman/toxic/issues/228) | ||||
| - Compilation fails with latests tox-core [\#227](https://github.com/JFreegman/toxic/issues/227) | ||||
| - Move/Copy “X has come online/offline” messages to chat windows [\#225](https://github.com/JFreegman/toxic/issues/225) | ||||
| - MANDIR set for Linux [\#222](https://github.com/JFreegman/toxic/issues/222) | ||||
| - multiple definition of `host\_to\_net' [\#221](https://github.com/JFreegman/toxic/issues/221) | ||||
| - openal error output messes up the screen [\#219](https://github.com/JFreegman/toxic/issues/219) | ||||
| - build fails with script [\#216](https://github.com/JFreegman/toxic/issues/216) | ||||
| - UTF-8 Support [\#171](https://github.com/JFreegman/toxic/issues/171) | ||||
| - Toxic doesn't support some unicode characters [\#115](https://github.com/JFreegman/toxic/issues/115) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Cosmetic fixes [\#234](https://github.com/JFreegman/toxic/pull/234) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Reworked manpage build system [\#231](https://github.com/JFreegman/toxic/pull/231) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Manpage [\#230](https://github.com/JFreegman/toxic/pull/230) ([louipc](https://github.com/louipc)) | ||||
| - toxic.conf.example: better formatting [\#229](https://github.com/JFreegman/toxic/pull/229) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fix Tox/toxic\#222 and reorganize cfg dir [\#226](https://github.com/JFreegman/toxic/pull/226) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add debug flag and update man page. [\#223](https://github.com/JFreegman/toxic/pull/223) ([louipc](https://github.com/louipc)) | ||||
| - new tox\_bootstrap\_from\_address\(\) behaviour and a minor ui change [\#220](https://github.com/JFreegman/toxic/pull/220) ([louipc](https://github.com/louipc)) | ||||
| - toxic.conf.5: Remove default config from man page [\#218](https://github.com/JFreegman/toxic/pull/218) ([louipc](https://github.com/louipc)) | ||||
|  | ||||
| ## [v0.4.7](https://github.com/JFreegman/toxic/tree/v0.4.7) (2014-08-05) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.6...v0.4.7) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Segfault on openSUSE 13.1 [\#106](https://github.com/JFreegman/toxic/issues/106) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - cancel callback doesn't work [\#214](https://github.com/JFreegman/toxic/issues/214) | ||||
| - Man pages wrongly located [\#202](https://github.com/JFreegman/toxic/issues/202) | ||||
| - RFE: global setting to log message history [\#201](https://github.com/JFreegman/toxic/issues/201) | ||||
| - Small typo in menu item [\#197](https://github.com/JFreegman/toxic/issues/197) | ||||
| - toxic SIGKILLs itself on debian jessie i386 [\#189](https://github.com/JFreegman/toxic/issues/189) | ||||
| - Toxic segfaults [\#144](https://github.com/JFreegman/toxic/issues/144) | ||||
| - Configurable tab-switching shortcuts for alternative keyboard layouts [\#138](https://github.com/JFreegman/toxic/issues/138) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fix ringing sounds [\#215](https://github.com/JFreegman/toxic/pull/215) ([ghost](https://github.com/ghost)) | ||||
| - Add missing includes [\#213](https://github.com/JFreegman/toxic/pull/213) ([doughdemon](https://github.com/doughdemon)) | ||||
| - Fix bug [\#211](https://github.com/JFreegman/toxic/pull/211) ([ghost](https://github.com/ghost)) | ||||
| - Fresh pack of backdoors [\#210](https://github.com/JFreegman/toxic/pull/210) ([ghost](https://github.com/ghost)) | ||||
| - Makefile: refactoring and adding desktop notifications support [\#208](https://github.com/JFreegman/toxic/pull/208) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Update toxic.conf manpage [\#207](https://github.com/JFreegman/toxic/pull/207) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Configurable keybindings [\#206](https://github.com/JFreegman/toxic/pull/206) ([gracchus163](https://github.com/gracchus163)) | ||||
| - Lowered volume of sounds [\#205](https://github.com/JFreegman/toxic/pull/205) ([loadedice](https://github.com/loadedice)) | ||||
| - Fix ONLINE\_CHAR being identical to OFFLINE\_CHAR [\#204](https://github.com/JFreegman/toxic/pull/204) ([zetok](https://github.com/zetok)) | ||||
| - Put man pages in right place by default \(\#202\) [\#203](https://github.com/JFreegman/toxic/pull/203) ([zetok](https://github.com/zetok)) | ||||
| - Popup notifications & core adjustments [\#200](https://github.com/JFreegman/toxic/pull/200) ([ghost](https://github.com/ghost)) | ||||
| - Fixed sounds not playing [\#199](https://github.com/JFreegman/toxic/pull/199) ([ghost](https://github.com/ghost)) | ||||
| - README.md: add precompiled binaries [\#198](https://github.com/JFreegman/toxic/pull/198) ([Ansa89](https://github.com/Ansa89)) | ||||
|  | ||||
| ## [v0.4.6](https://github.com/JFreegman/toxic/tree/v0.4.6) (2014-07-23) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.5...v0.4.6) | ||||
|  | ||||
| **Implemented enhancements:** | ||||
|  | ||||
| - "Officially Deprecated" build for 32-bit? [\#192](https://github.com/JFreegman/toxic/issues/192) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Please create me a wiki account [\#196](https://github.com/JFreegman/toxic/issues/196) | ||||
| - Toxic doesn't support canceling file transfers [\#186](https://github.com/JFreegman/toxic/issues/186) | ||||
| - hashes of binaries? [\#185](https://github.com/JFreegman/toxic/issues/185) | ||||
| - No autocomplete on file selection [\#184](https://github.com/JFreegman/toxic/issues/184) | ||||
| - valgrind [\#178](https://github.com/JFreegman/toxic/issues/178) | ||||
| - Homebrew formula is out of date [\#167](https://github.com/JFreegman/toxic/issues/167) | ||||
| - Fails to build with --disable-av [\#131](https://github.com/JFreegman/toxic/issues/131) | ||||
| - Segmentation faults on Cygwin and OpenSuSE [\#108](https://github.com/JFreegman/toxic/issues/108) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Add hardcoded path for sound notifications [\#195](https://github.com/JFreegman/toxic/pull/195) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: little refactoring [\#193](https://github.com/JFreegman/toxic/pull/193) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fixed some build errors [\#190](https://github.com/JFreegman/toxic/pull/190) ([ghost](https://github.com/ghost)) | ||||
| - Makefile fix [\#188](https://github.com/JFreegman/toxic/pull/188) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Added sound notifications, libconfig support, and more... [\#187](https://github.com/JFreegman/toxic/pull/187) ([ghost](https://github.com/ghost)) | ||||
|  | ||||
| ## [v0.4.5](https://github.com/JFreegman/toxic/tree/v0.4.5) (2014-07-14) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.1...v0.4.5) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - building on freebsd [\#177](https://github.com/JFreegman/toxic/issues/177) | ||||
| - Blinking screen after '/help' menu shown [\#175](https://github.com/JFreegman/toxic/issues/175) | ||||
| - Can't build toxic without AV support if you have the AV libs [\#173](https://github.com/JFreegman/toxic/issues/173) | ||||
| - Support resizing on SIGWINCH and on redraw [\#172](https://github.com/JFreegman/toxic/issues/172) | ||||
| - Broken backspace [\#163](https://github.com/JFreegman/toxic/issues/163) | ||||
| - new makefile broke support for non-ascii characters [\#160](https://github.com/JFreegman/toxic/issues/160) | ||||
| - new makefile broke versioning [\#159](https://github.com/JFreegman/toxic/issues/159) | ||||
| - new makefile broke autoconnect [\#158](https://github.com/JFreegman/toxic/issues/158) | ||||
| - Compilation error [\#143](https://github.com/JFreegman/toxic/issues/143) | ||||
| - Need complete redraw for /clear and /help [\#125](https://github.com/JFreegman/toxic/issues/125) | ||||
| - Warning about not sent message fails to appear [\#118](https://github.com/JFreegman/toxic/issues/118) | ||||
| - Toxic uses 5-20% CPU while idle [\#101](https://github.com/JFreegman/toxic/issues/101) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fixes problems with upstream changes [\#183](https://github.com/JFreegman/toxic/pull/183) ([ghost](https://github.com/ghost)) | ||||
| - Use long int instead uint64\_t [\#181](https://github.com/JFreegman/toxic/pull/181) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Forgot about help [\#180](https://github.com/JFreegman/toxic/pull/180) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add option to disable audio support [\#179](https://github.com/JFreegman/toxic/pull/179) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Make closing window end call [\#174](https://github.com/JFreegman/toxic/pull/174) ([ghost](https://github.com/ghost)) | ||||
| - Manpage fix [\#170](https://github.com/JFreegman/toxic/pull/170) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add help target and toxic.conf manpage [\#169](https://github.com/JFreegman/toxic/pull/169) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fixed setting buffer to half of the size [\#165](https://github.com/JFreegman/toxic/pull/165) ([ghost](https://github.com/ghost)) | ||||
| - Add manpage [\#164](https://github.com/JFreegman/toxic/pull/164) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Try to fix autoconnect [\#161](https://github.com/JFreegman/toxic/pull/161) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Wide characters support [\#157](https://github.com/JFreegman/toxic/pull/157) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Polishing README.md [\#155](https://github.com/JFreegman/toxic/pull/155) ([theGeekPirate](https://github.com/theGeekPirate)) | ||||
| - README.md: add build status [\#153](https://github.com/JFreegman/toxic/pull/153) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Update readme instructions [\#152](https://github.com/JFreegman/toxic/pull/152) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Forgot to set index in some callbacks [\#151](https://github.com/JFreegman/toxic/pull/151) ([ghost](https://github.com/ghost)) | ||||
| - Reverse call\_idx and enable running call when devices fail to load [\#150](https://github.com/JFreegman/toxic/pull/150) ([ghost](https://github.com/ghost)) | ||||
| - Remove autotools dependency [\#149](https://github.com/JFreegman/toxic/pull/149) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Cast localtime [\#147](https://github.com/JFreegman/toxic/pull/147) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Changed code a bit and added new features [\#146](https://github.com/JFreegman/toxic/pull/146) ([ghost](https://github.com/ghost)) | ||||
|  | ||||
| ## [0.4.1](https://github.com/JFreegman/toxic/tree/0.4.1) (2014-06-19) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.0...0.4.1) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Toxic does not complie with audio on OSX [\#140](https://github.com/JFreegman/toxic/issues/140) | ||||
| - compiling error [\#139](https://github.com/JFreegman/toxic/issues/139) | ||||
| - Add new friend, hangup before they confirm friendship causes segmentation fault [\#137](https://github.com/JFreegman/toxic/issues/137) | ||||
| - build fail [\#124](https://github.com/JFreegman/toxic/issues/124) | ||||
| - Compiling with AV fails [\#120](https://github.com/JFreegman/toxic/issues/120) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Add libresolv [\#142](https://github.com/JFreegman/toxic/pull/142) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Search for OpenAL framework on OSX [\#141](https://github.com/JFreegman/toxic/pull/141) ([jin-eld](https://github.com/jin-eld)) | ||||
|  | ||||
| ## [0.4.0](https://github.com/JFreegman/toxic/tree/0.4.0) (2014-06-01) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0.1...0.4.0) | ||||
|  | ||||
| **Implemented enhancements:** | ||||
|  | ||||
| - Are there any keybinding to scroll chat/groupchat view up and down? [\#74](https://github.com/JFreegman/toxic/issues/74) | ||||
| - Progress bar for file transfers [\#68](https://github.com/JFreegman/toxic/issues/68) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Toxic does not support certain characters [\#84](https://github.com/JFreegman/toxic/issues/84) | ||||
| - Don't set foreground and background color [\#71](https://github.com/JFreegman/toxic/issues/71) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Toxic misbehaves and is killed [\#136](https://github.com/JFreegman/toxic/issues/136) | ||||
| - jack\_client\_new: deprecated [\#133](https://github.com/JFreegman/toxic/issues/133) | ||||
| - build error on os x 10.9 [\#129](https://github.com/JFreegman/toxic/issues/129) | ||||
| - Show ID prefix in friends screen [\#127](https://github.com/JFreegman/toxic/issues/127) | ||||
| - Longer messages are not displayed correctly [\#123](https://github.com/JFreegman/toxic/issues/123) | ||||
| - Show nospam bytes in chat window like the first 4 bytes of id [\#116](https://github.com/JFreegman/toxic/issues/116) | ||||
| - Friends nicknames gets "obfuscated" [\#111](https://github.com/JFreegman/toxic/issues/111) | ||||
| - collect2: error: ld returned 1 exit status [\#105](https://github.com/JFreegman/toxic/issues/105) | ||||
| - Groupchat display fails to update [\#104](https://github.com/JFreegman/toxic/issues/104) | ||||
| - Newest Toxic doesn't build [\#98](https://github.com/JFreegman/toxic/issues/98) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Update README.md [\#134](https://github.com/JFreegman/toxic/pull/134) ([zetok](https://github.com/zetok)) | ||||
| - Update audio\_call.c [\#132](https://github.com/JFreegman/toxic/pull/132) ([Impyy](https://github.com/Impyy)) | ||||
| - Not done yet. [\#130](https://github.com/JFreegman/toxic/pull/130) ([ghost](https://github.com/ghost)) | ||||
| - Fix file sender null terminator. [\#128](https://github.com/JFreegman/toxic/pull/128) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Drop typedef redeclarations [\#122](https://github.com/JFreegman/toxic/pull/122) ([czarkoff](https://github.com/czarkoff)) | ||||
| - Include "pthread.h" [\#121](https://github.com/JFreegman/toxic/pull/121) ([czarkoff](https://github.com/czarkoff)) | ||||
| - Wow [\#119](https://github.com/JFreegman/toxic/pull/119) ([ghost](https://github.com/ghost)) | ||||
| - Use default terminal fg/bg colors when we can. [\#117](https://github.com/JFreegman/toxic/pull/117) ([ooesili](https://github.com/ooesili)) | ||||
| - Fixed support for wide characters [\#113](https://github.com/JFreegman/toxic/pull/113) ([graboy](https://github.com/graboy)) | ||||
| - Mention av [\#110](https://github.com/JFreegman/toxic/pull/110) ([stqism](https://github.com/stqism)) | ||||
| - allow history scrolling [\#109](https://github.com/JFreegman/toxic/pull/109) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Only those who appreciate small things [\#107](https://github.com/JFreegman/toxic/pull/107) ([ghost](https://github.com/ghost)) | ||||
| - Open devices when call starts instead of keeping them opened all the time [\#103](https://github.com/JFreegman/toxic/pull/103) ([ghost](https://github.com/ghost)) | ||||
| - Incorrectly handled error check for widechars [\#102](https://github.com/JFreegman/toxic/pull/102) ([graboy](https://github.com/graboy)) | ||||
| - Fix toxic build when toxav is not available [\#100](https://github.com/JFreegman/toxic/pull/100) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Add checks for pthreads to the build system [\#99](https://github.com/JFreegman/toxic/pull/99) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Fixes and stuff... [\#97](https://github.com/JFreegman/toxic/pull/97) ([ghost](https://github.com/ghost)) | ||||
|  | ||||
| ## [0.3.0.1](https://github.com/JFreegman/toxic/tree/0.3.0.1) (2014-03-12) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0...0.3.0.1) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - SPELLING IS FOR FOOLS [\#94](https://github.com/JFreegman/toxic/pull/94) ([lehitoskin](https://github.com/lehitoskin)) | ||||
|  | ||||
| ## [0.3.0](https://github.com/JFreegman/toxic/tree/0.3.0) (2014-03-12) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.7...0.3.0) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - SIGSEVG upon friend hanging up [\#89](https://github.com/JFreegman/toxic/issues/89) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fixed segfault [\#92](https://github.com/JFreegman/toxic/pull/92) ([ghost](https://github.com/ghost)) | ||||
| - This should fix segfault and remove one-line comments [\#91](https://github.com/JFreegman/toxic/pull/91) ([ghost](https://github.com/ghost)) | ||||
| - Fixed another clang issue with bools that broek file sending. [\#90](https://github.com/JFreegman/toxic/pull/90) ([Jman012](https://github.com/Jman012)) | ||||
| - Toxic audio support [\#88](https://github.com/JFreegman/toxic/pull/88) ([ghost](https://github.com/ghost)) | ||||
| - Fixed clang error, disabling the execute module. [\#87](https://github.com/JFreegman/toxic/pull/87) ([Jman012](https://github.com/Jman012)) | ||||
| - Issue \#84 fixed [\#86](https://github.com/JFreegman/toxic/pull/86) ([thevar1able](https://github.com/thevar1able)) | ||||
| - Fixing fall-back from IPv6 to IPv4 [\#85](https://github.com/JFreegman/toxic/pull/85) ([micrictor](https://github.com/micrictor)) | ||||
|  | ||||
| ## [0.2.7](https://github.com/JFreegman/toxic/tree/0.2.7) (2014-03-01) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6.1...0.2.7) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Toxic segfault when window is closed [\#81](https://github.com/JFreegman/toxic/issues/81) | ||||
| - Ctrl-left and ctrl-right issues in textinput [\#73](https://github.com/JFreegman/toxic/issues/73) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - down arrow returns empty string if at end of history [\#82](https://github.com/JFreegman/toxic/pull/82) ([kl4ng](https://github.com/kl4ng)) | ||||
| - Fallback to loading /usr/share/toxic/DHTservers. [\#80](https://github.com/JFreegman/toxic/pull/80) ([viric](https://github.com/viric)) | ||||
|  | ||||
| ## [0.2.6.1](https://github.com/JFreegman/toxic/tree/0.2.6.1) (2014-02-23) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6...0.2.6.1) | ||||
|  | ||||
| ## [0.2.6](https://github.com/JFreegman/toxic/tree/0.2.6) (2014-02-23) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.5...0.2.6) | ||||
|  | ||||
| ## [0.2.5](https://github.com/JFreegman/toxic/tree/0.2.5) (2014-02-22) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/prealpha_win32_r8...0.2.5) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Back space leaves ć character [\#44](https://github.com/JFreegman/toxic/issues/44) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Remember groupchats [\#76](https://github.com/JFreegman/toxic/issues/76) | ||||
| - Segfault [\#75](https://github.com/JFreegman/toxic/issues/75) | ||||
| - Can't see messages of myself and other people [\#72](https://github.com/JFreegman/toxic/issues/72) | ||||
| - binary blob in source [\#66](https://github.com/JFreegman/toxic/issues/66) | ||||
| - symbol lookup error [\#54](https://github.com/JFreegman/toxic/issues/54) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - ncurses libraries README note  [\#78](https://github.com/JFreegman/toxic/pull/78) ([kl4ng](https://github.com/kl4ng)) | ||||
| - umask such that stored files are u+rw only [\#77](https://github.com/JFreegman/toxic/pull/77) ([alevy](https://github.com/alevy)) | ||||
| - Fix groupchat cursor movement. [\#63](https://github.com/JFreegman/toxic/pull/63) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Fix wchar cursor movement. [\#62](https://github.com/JFreegman/toxic/pull/62) ([aitjcize](https://github.com/aitjcize)) | ||||
| - api update [\#61](https://github.com/JFreegman/toxic/pull/61) ([naxuroqa](https://github.com/naxuroqa)) | ||||
| - Add option to switch off ipv6. [\#60](https://github.com/JFreegman/toxic/pull/60) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Fix partial fix: A slash in pos 0 still led to read access to pathname\[-1\]. [\#59](https://github.com/JFreegman/toxic/pull/59) ([FullName](https://github.com/FullName)) | ||||
| - Fix corresponding API name changes in toxcore. [\#58](https://github.com/JFreegman/toxic/pull/58) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Fix API ret code changes of ToxCore [\#57](https://github.com/JFreegman/toxic/pull/57) ([aitjcize](https://github.com/aitjcize)) | ||||
|  | ||||
| ## [prealpha_win32_r8](https://github.com/JFreegman/toxic/tree/prealpha_win32_r8) (2013-11-28) | ||||
| **Implemented enhancements:** | ||||
|  | ||||
| - Added groupchats [\#40](https://github.com/JFreegman/toxic/pull/40) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Adapted to ipv6-enabled tox [\#38](https://github.com/JFreegman/toxic/pull/38) ([FullName](https://github.com/FullName)) | ||||
| - If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName)) | ||||
| - Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman)) | ||||
| - if tox\_new\(\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName)) | ||||
| - truncate friends' notes if they're too long [\#30](https://github.com/JFreegman/toxic/pull/30) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Added status bar to prompt, made it beep/blink on friend request, and bug fixes [\#29](https://github.com/JFreegman/toxic/pull/29) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Added a statusbar to chat windows and removed spammy messages [\#28](https://github.com/JFreegman/toxic/pull/28) ([JFreegman](https://github.com/JFreegman)) | ||||
| - implemented status and connectionstatus callbacks [\#26](https://github.com/JFreegman/toxic/pull/26) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Show offline friends names and some cosmetic changes [\#25](https://github.com/JFreegman/toxic/pull/25) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman)) | ||||
| - refactor command argument parsing [\#23](https://github.com/JFreegman/toxic/pull/23) ([lukechampine](https://github.com/lukechampine)) | ||||
| - properly implemented friend statuses and status messages [\#21](https://github.com/JFreegman/toxic/pull/21) ([JFreegman](https://github.com/JFreegman)) | ||||
| - implemented friend deletion [\#15](https://github.com/JFreegman/toxic/pull/15) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Add check for setlocale\(\) [\#10](https://github.com/JFreegman/toxic/pull/10) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||
| - Update build system [\#7](https://github.com/JFreegman/toxic/pull/7) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Added travis integration [\#6](https://github.com/JFreegman/toxic/pull/6) ([stqism](https://github.com/stqism)) | ||||
| - Use new public api [\#5](https://github.com/JFreegman/toxic/pull/5) ([fhahn](https://github.com/fhahn)) | ||||
| - Add widechar checks [\#2](https://github.com/JFreegman/toxic/pull/2) ([jin-eld](https://github.com/jin-eld)) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Let windows.c actually get the tox \*m. [\#41](https://github.com/JFreegman/toxic/pull/41) ([Jman012](https://github.com/Jman012)) | ||||
| - If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName)) | ||||
| - Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Merged pr6 [\#34](https://github.com/JFreegman/toxic/pull/34) ([stqism](https://github.com/stqism)) | ||||
| - made error handling more consistent and added exit function [\#33](https://github.com/JFreegman/toxic/pull/33) ([JFreegman](https://github.com/JFreegman)) | ||||
| - if tox\\_new\\(\\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName)) | ||||
| - Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman)) | ||||
| - fix buffer overflows and format issues [\#20](https://github.com/JFreegman/toxic/pull/20) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Fix blocking while waiting for key [\#17](https://github.com/JFreegman/toxic/pull/17) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||
| - fixed "free\(\): invalid pointer" when XDG\_CONFIG\_HOME is set [\#16](https://github.com/JFreegman/toxic/pull/16) ([gs93](https://github.com/gs93)) | ||||
| - Make sure toxic compiles on MinGW/Win32 again [\#14](https://github.com/JFreegman/toxic/pull/14) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Fix for the "bad character" when doing backspace in chat window [\#12](https://github.com/JFreegman/toxic/pull/12) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Fix configure script for ncurses without ncursesw [\#9](https://github.com/JFreegman/toxic/pull/9) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||
| - Fix configure script for mingw32 [\#8](https://github.com/JFreegman/toxic/pull/8) ([jin-eld](https://github.com/jin-eld)) | ||||
| - warning: comparison of integers of different signs: 'int' and 'unsigned long' [\#3](https://github.com/JFreegman/toxic/pull/3) ([1100110](https://github.com/1100110)) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Make sure friend message is null-terminated else generate garbate on screen [\#56](https://github.com/JFreegman/toxic/pull/56) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Fix trailing slashes which leads to segfault. [\#55](https://github.com/JFreegman/toxic/pull/55) ([aitjcize](https://github.com/aitjcize)) | ||||
| - fix cflags [\#53](https://github.com/JFreegman/toxic/pull/53) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Fix 93ab16c [\#52](https://github.com/JFreegman/toxic/pull/52) ([urras](https://github.com/urras)) | ||||
| - Offer solution for "error while loading shared libraries: libtoxcore.so.... [\#51](https://github.com/JFreegman/toxic/pull/51) ([urras](https://github.com/urras)) | ||||
| - Implemented file transfers [\#50](https://github.com/JFreegman/toxic/pull/50) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Fix check for toxcore by linking sodium in the correct place [\#47](https://github.com/JFreegman/toxic/pull/47) ([devurandom](https://github.com/devurandom)) | ||||
| - Changed order of servers [\#46](https://github.com/JFreegman/toxic/pull/46) ([grimd34th](https://github.com/grimd34th)) | ||||
| - set friendnames properly and some fixes [\#45](https://github.com/JFreegman/toxic/pull/45) ([JFreegman](https://github.com/JFreegman)) | ||||
| - moved misc helper functions to separate file and removed redundant includes [\#43](https://github.com/JFreegman/toxic/pull/43) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Refactored prompt command parser to work with all window types and moved command stuff to separate files [\#42](https://github.com/JFreegman/toxic/pull/42) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Ipv6.init connection [\#39](https://github.com/JFreegman/toxic/pull/39) ([FullName](https://github.com/FullName)) | ||||
| - Remove DHT window [\#13](https://github.com/JFreegman/toxic/pull/13) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Update README.md [\#4](https://github.com/JFreegman/toxic/pull/4) ([notadecent](https://github.com/notadecent)) | ||||
| - Toxic standalone [\#1](https://github.com/JFreegman/toxic/pull/1) ([jin-eld](https://github.com/jin-eld)) | ||||
|  | ||||
|  | ||||
|  | ||||
| \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* | ||||
							
								
								
									
										70
									
								
								INSTALL.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								INSTALL.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| # Installation | ||||
| * [Dependencies](#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 | ||||
							
								
								
									
										65
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,53 +1,21 @@ | ||||
| # Toxic [](https://travis-ci.org/Tox/toxic) | ||||
| <a href="https://scan.coverity.com/projects/toxic-tox"> | ||||
|   <img alt="Coverity Scan Build Status" | ||||
|        src="https://scan.coverity.com/projects/4975/badge.svg"/> | ||||
| </a> | ||||
|  | ||||
| Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application. | ||||
|  | ||||
|  | ||||
| [](https://i.imgur.com/san99Z2.png) | ||||
|  | ||||
| ## Installation | ||||
| [Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br /> | ||||
| [Compile it yourself](/INSTALL.md) | ||||
|  | ||||
| ### Dependencies | ||||
| ##### Base | ||||
| * [libtoxcore](https://github.com/irungentoo/toxcore) | ||||
| * [ncurses](https://www.gnu.org/software/ncurses) (for Debian based systems, 'libncursesw5-dev') | ||||
| * [libconfig](http://www.hyperrealm.com/libconfig) (for Debian based systems, 'libconfig-dev') | ||||
| ## Settings | ||||
| Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more. | ||||
| You can view our example config file [here](misc/toxic.conf.example). | ||||
|  | ||||
| ##### Audio | ||||
| * libtoxav ([libtoxcore](https://github.com/irungentoo/toxcore) compiled with audio support) | ||||
| * [openal](http://openal.org) (for Debian based systems, 'libopenal-dev') | ||||
|  | ||||
| ##### Sound notifications | ||||
| * [openal](http://openal.org) (for Debian based systems, 'libopenal-dev') | ||||
| * [openalut](http://openal.org) (for Debian based systems, 'libalut-dev') | ||||
|  | ||||
| ##### Desktop notifications | ||||
| * [libnotify](https://developer.gnome.org/libnotify) (for Debian based systems, 'libnotify-dev') | ||||
|  | ||||
| ##### Documentation | ||||
| * [Asciidoc](http://asciidoc.org/index.html) (only required for regenerating manpages) | ||||
|   * Run `make doc` after editing the asciidoc files to regenerate the manpages. | ||||
|   * **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together. | ||||
|  | ||||
| ### Compiling | ||||
| 1. `cd build/` | ||||
| 2. `make PREFIX="/where/to/install"` | ||||
| 3. `sudo make install PREFIX="/where/to/install"` | ||||
|  | ||||
| ### Compilation Notes | ||||
| * You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""` | ||||
| * You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones) | ||||
| * Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables: | ||||
|   * `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking) | ||||
|   * `DISABLE_AV=1` → build toxic without audio call support | ||||
|   * `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support | ||||
|   * `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support | ||||
|  | ||||
| ### Packaging | ||||
| * For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files | ||||
| * `DESTDIR=""` can be used in addition to `PREFIX=""`: | ||||
|   * `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg") | ||||
|   * `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local") | ||||
|  | ||||
| ### Troubleshooting | ||||
| ## Troubleshooting | ||||
| If your default prefix is "/usr/local" and you receive the following: | ||||
| ``` | ||||
| error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory | ||||
| @@ -58,12 +26,3 @@ echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf | ||||
| sudo ldconfig | ||||
| ``` | ||||
|  | ||||
| ## Precompiled binaries | ||||
| You can download precompiled binaries from [jenkins](https://jenkins.libtoxcore.so): | ||||
| * [Linux 32 bit](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz) | ||||
| * [Linux 64 bit](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz) | ||||
|  | ||||
| ## Settings | ||||
| Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more. | ||||
| You can view our example config file [here](misc/toxic.conf.example). | ||||
|  | ||||
|   | ||||
							
								
								
									
										11
									
								
								astylerc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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,67 +0,0 @@ | ||||
| BASE_DIR = $(shell cd .. && pwd -P) | ||||
| CFG_DIR = $(BASE_DIR)/cfg | ||||
|  | ||||
| -include $(CFG_DIR)/global_vars.mk | ||||
|  | ||||
| LIBS = libtoxcore ncursesw libconfig | ||||
|  | ||||
| CFLAGS = -std=gnu99 -pthread -Wall -g | ||||
| CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | ||||
| CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | ||||
| CFLAGS += $(USER_CFLAGS) | ||||
| LDFLAGS = $(USER_LDFLAGS) | ||||
|  | ||||
| OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o notify.o | ||||
| OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o | ||||
| OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o | ||||
|  | ||||
| # Check on wich system we are running | ||||
| UNAME_S = $(shell uname -s) | ||||
| ifeq ($(UNAME_S), Linux) | ||||
| 	-include $(CFG_DIR)/systems/Linux.mk | ||||
| endif | ||||
| ifeq ($(UNAME_S), FreeBSD) | ||||
| 	-include $(CFG_DIR)/systems/FreeBSD.mk | ||||
| endif | ||||
| ifeq ($(UNAME_S), Darwin) | ||||
| 	-include $(CFG_DIR)/systems/Darwin.mk | ||||
| endif | ||||
| ifeq ($(UNAME_S), Solaris) | ||||
| 	-include $(CFG_DIR)/systems/Solaris.mk | ||||
| endif | ||||
|  | ||||
| # Check on which platform we are running | ||||
| UNAME_M = $(shell uname -m) | ||||
| ifeq ($(UNAME_M), x86_64) | ||||
| 	-include $(CFG_DIR)/platforms/x86_64.mk | ||||
| endif | ||||
| ifneq ($(filter %86, $(UNAME_M)),) | ||||
| 	-include $(CFG_DIR)/platforms/x86.mk | ||||
| endif | ||||
| ifneq ($(filter arm%, $(UNAME_M)),) | ||||
| 	-include $(CFG_DIR)/platforms/arm.mk | ||||
| endif | ||||
|  | ||||
| # Include all needed checks | ||||
| -include $(CFG_DIR)/checks/check_features.mk | ||||
|  | ||||
| # Targets | ||||
| all: toxic doc | ||||
|  | ||||
| toxic: $(OBJ) | ||||
| 	@echo "  LD    $@" | ||||
| 	@$(CC) $(CFLAGS) -o toxic $(OBJ) $(LDFLAGS) | ||||
|  | ||||
| %.o: $(SRC_DIR)/%.c | ||||
| 	@echo "  CC    $@" | ||||
| 	@$(CC) $(CFLAGS) -o $*.o -c $(SRC_DIR)/$*.c | ||||
| 	@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $*.d | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.d *.o toxic | ||||
|  | ||||
| -include $(OBJ:.o=.d) | ||||
|  | ||||
| -include $(CFG_DIR)/targets/*.mk | ||||
|  | ||||
| .PHONY: clean all | ||||
							
								
								
									
										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 | ||||
| @@ -1,23 +0,0 @@ | ||||
| # Variables for audio call support | ||||
| AUDIO_LIBS = libtoxav openal | ||||
| AUDIO_CFLAGS = -DAUDIO | ||||
| ifneq (, $(findstring device.o, $(OBJ))) | ||||
| 	AUDIO_OBJ = audio_call.o | ||||
| else | ||||
| 	AUDIO_OBJ = audio_call.o device.o | ||||
| endif | ||||
|  | ||||
| # Check if we can build audio support | ||||
| CHECK_AUDIO_LIBS = $(shell pkg-config --exists $(AUDIO_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_AUDIO_LIBS), error) | ||||
| 	LIBS += $(AUDIO_LIBS) | ||||
| 	CFLAGS += $(AUDIO_CFLAGS) | ||||
| 	OBJ += $(AUDIO_OBJ) | ||||
| else | ||||
| ifneq ($(MAKECMDGOALS), clean) | ||||
| MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done) | ||||
| $(warning WARNING -- Toxic will be compiled without audio support) | ||||
| $(warning WARNING -- You need these libraries for audio support) | ||||
| $(warning WARNING -- $(MISSING_AUDIO_LIBS)) | ||||
| endif | ||||
| endif | ||||
| @@ -3,38 +3,52 @@ CHECKS_DIR = $(CFG_DIR)/checks | ||||
| # Check if we want build X11 support | ||||
| X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(X11), disabled) | ||||
| 	-include $(CHECKS_DIR)/x11.mk | ||||
|     -include $(CHECKS_DIR)/x11.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build audio support | ||||
| AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(AUDIO), disabled) | ||||
| 	-include $(CHECKS_DIR)/av.mk | ||||
|     -include $(CHECKS_DIR)/audio.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build video support | ||||
| VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(X11), disabled) | ||||
| ifneq ($(AUDIO), disabled) | ||||
| ifneq ($(VIDEO), disabled) | ||||
|     -include $(CHECKS_DIR)/video.mk | ||||
| endif | ||||
| endif | ||||
| endif | ||||
|  | ||||
| # Check if we want build sound notifications support | ||||
| SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(SND_NOTIFY), disabled) | ||||
| 	-include $(CHECKS_DIR)/sound_notifications.mk | ||||
|     -include $(CHECKS_DIR)/sound_notifications.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build desktop notifications support | ||||
| DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(DESK_NOTIFY), disabled) | ||||
| 	-include $(CHECKS_DIR)/desktop_notifications.mk | ||||
|     -include $(CHECKS_DIR)/desktop_notifications.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build QR exported as PNG support | ||||
| QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(QR_PNG), disabled) | ||||
|     -include $(CHECKS_DIR)/qr_png.mk | ||||
| endif | ||||
|  | ||||
| # Check if we can build Toxic | ||||
| CHECK_LIBS = $(shell pkg-config --exists $(LIBS) || echo -n "error") | ||||
| CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_LIBS), error) | ||||
| 	CFLAGS += $(shell pkg-config --cflags $(LIBS)) | ||||
| 	LDFLAGS += $(shell pkg-config --libs $(LIBS)) | ||||
| else | ||||
| ifneq ($(MAKECMDGOALS), clean) | ||||
| MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done) | ||||
| $(warning ERROR -- Cannot compile Toxic) | ||||
| $(warning ERROR -- You need these libraries) | ||||
| $(warning ERROR -- $(MISSING_LIBS)) | ||||
| $(error ERROR) | ||||
| endif | ||||
|     CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) | ||||
|     LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS)) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning ERROR -- Cannot compile Toxic) | ||||
|     $(warning ERROR -- You need these libraries) | ||||
|     $(warning ERROR -- $(MISSING_LIBS)) | ||||
|     $(error ERROR) | ||||
| endif | ||||
|   | ||||
| @@ -3,15 +3,13 @@ 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") | ||||
| CHECK_DESK_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_DESK_NOTIFY_LIBS), error) | ||||
| 	LIBS += $(DESK_NOTIFY_LIBS) | ||||
| 	CFLAGS += $(DESK_NOTIFY_CFLAGS) | ||||
| else | ||||
| ifneq ($(MAKECMDGOALS), clean) | ||||
| MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done) | ||||
| $(warning WARNING -- Toxic will be compiled without desktop notifications support) | ||||
| $(warning WARNING -- You need these libraries for desktop notifications support) | ||||
| $(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS)) | ||||
| endif | ||||
|     LIBS += $(DESK_NOTIFY_LIBS) | ||||
|     CFLAGS += $(DESK_NOTIFY_CFLAGS) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without desktop notifications support) | ||||
|     $(warning WARNING -- You need these libraries for desktop notifications support) | ||||
|     $(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS)) | ||||
| endif | ||||
|   | ||||
							
								
								
									
										15
									
								
								cfg/checks/qr_png.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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 | ||||
| @@ -1,23 +1,21 @@ | ||||
| # Variables for sound notifications support | ||||
| SND_NOTIFY_LIBS = openal freealut | ||||
| SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY | ||||
| ifneq (, $(findstring device.o, $(OBJ))) | ||||
| 	SND_NOTIFY_OBJ = | ||||
| ifneq (, $(findstring audio_device.o, $(OBJ))) | ||||
|     SND_NOTIFY_OBJ = | ||||
| else | ||||
| 	SND_NOTIFY_OBJ = device.o | ||||
|     SND_NOTIFY_OBJ = audio_device.o | ||||
| endif | ||||
|  | ||||
| # Check if we can build sound notifications support | ||||
| CHECK_SND_NOTIFY_LIBS = $(shell pkg-config --exists $(SND_NOTIFY_LIBS) || echo -n "error") | ||||
| CHECK_SND_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(SND_NOTIFY_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_SND_NOTIFY_LIBS), error) | ||||
| 	LIBS += $(SND_NOTIFY_LIBS) | ||||
| 	CFLAGS += $(SND_NOTIFY_CFLAGS) | ||||
| 	OBJ += $(SND_NOTIFY_OBJ) | ||||
| else | ||||
| ifneq ($(MAKECMDGOALS), clean) | ||||
| MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done) | ||||
| $(warning WARNING -- Toxic will be compiled without sound notifications support) | ||||
| $(warning WARNING -- You need these libraries for sound notifications support) | ||||
| $(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS)) | ||||
| endif | ||||
|     LIBS += $(SND_NOTIFY_LIBS) | ||||
|     CFLAGS += $(SND_NOTIFY_CFLAGS) | ||||
|     OBJ += $(SND_NOTIFY_OBJ) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without sound notifications support) | ||||
|     $(warning WARNING -- You need these libraries for sound notifications support) | ||||
|     $(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS)) | ||||
| endif | ||||
|   | ||||
							
								
								
									
										21
									
								
								cfg/checks/video.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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 | ||||
| @@ -1,17 +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") | ||||
| CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_X11_LIBS), error) | ||||
|        LIBS += $(X11_LIBS) | ||||
|        CFLAGS += $(X11_CFLAGS) | ||||
| else | ||||
| ifneq ($(MAKECMDGOALS), clean) | ||||
| MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done) | ||||
| $(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking)) | ||||
| $(warning WARNING -- You need these libraries for x11 support) | ||||
| $(warning WARNING -- $(MISSING_X11_LIBS)) | ||||
| endif | ||||
|     LIBS += $(X11_LIBS) | ||||
|     CFLAGS += $(X11_CFLAGS) | ||||
|     OBJ += $(X11_OBJ) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking and drag&drop support)) | ||||
|     $(warning WARNING -- You need these libraries for x11 support) | ||||
|     $(warning WARNING -- $(MISSING_X11_LIBS)) | ||||
| endif | ||||
|   | ||||
| @@ -1,9 +1,14 @@ | ||||
| # Version | ||||
| TOXIC_VERSION = 0.5.2 | ||||
| REV = $(shell git rev-list HEAD --count) | ||||
| VERSION = $(TOXIC_VERSION)_r$(REV) | ||||
| TOXIC_VERSION = 0.7.1 | ||||
| REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | ||||
| ifneq (, $(findstring error, $(REV))) | ||||
|     VERSION = $(TOXIC_VERSION) | ||||
| else | ||||
|     VERSION = $(TOXIC_VERSION)_r$(REV) | ||||
| endif | ||||
|  | ||||
| # Project directories | ||||
| BUILD_DIR = $(BASE_DIR)/build | ||||
| DOC_DIR = $(BASE_DIR)/doc | ||||
| SRC_DIR = $(BASE_DIR)/src | ||||
| SND_DIR = $(BASE_DIR)/sounds | ||||
| @@ -11,13 +16,17 @@ MISC_DIR = $(BASE_DIR)/misc | ||||
|  | ||||
| # Project files | ||||
| MANFILES = toxic.1 toxic.conf.5 | ||||
| DATAFILES = DHTnodes DNSservers toxic.conf.example | ||||
| SNDFILES = ContactLogsIn.wav ContactLogsOut.wav Error.wav IncomingCall.wav | ||||
| SNDFILES += LogIn.wav LogOut.wav NewMessage.wav OutgoingCall.wav | ||||
| SNDFILES += TransferComplete.wav TransferPending.wav | ||||
| DATAFILES = nameservers toxic.conf.example | ||||
| DESKFILE = toxic.desktop | ||||
| SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav | ||||
| SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav | ||||
| SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav | ||||
|  | ||||
| # Install directories | ||||
| PREFIX = /usr/local | ||||
| BINDIR = $(PREFIX)/bin | ||||
| DATADIR = $(PREFIX)/share/toxic | ||||
| MANDIR = $(PREFIX)/share/man | ||||
| APPDIR = $(PREFIX)/share/applications | ||||
| # Platform tools | ||||
| PKG_CONFIG = pkg-config | ||||
|   | ||||
							
								
								
									
										18
									
								
								cfg/systems/Darwin.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								cfg/systems/Darwin.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| # Special options for OS X | ||||
| # This assumes the use of Homebrew. Change the paths if using MacPorts or Fink. | ||||
|  | ||||
| PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/opt/libconfig/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig) | ||||
|  | ||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | ||||
|  | ||||
| # OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw. | ||||
| LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g | ||||
| CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g | ||||
|  | ||||
| OSX_LIBRARIES = -lobjc -lresolv | ||||
| OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \ | ||||
| 	-framework QuartzCore -framework CoreMedia | ||||
| OSX_VIDEO = osx_video.m | ||||
|  | ||||
| LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS) | ||||
| OBJ += osx_video.o | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Specials options for freebsd systems | ||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | ||||
| LDFLAGS += -lncursesw | ||||
| LDFLAGS += -lncursesw -lcurl | ||||
| MANDIR = $(PREFIX)/man | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Specials options for linux systems | ||||
| CFLAGS += | ||||
| LDFLAGS += -ldl -lresolv | ||||
| LDFLAGS += -ldl -lrt -lcurl | ||||
| MANDIR = $(PREFIX)/share/man | ||||
|   | ||||
| @@ -1,18 +1,20 @@ | ||||
| # Help target | ||||
| help: | ||||
| 	@echo "-- Targets --" | ||||
| 	@echo "  all:     Build toxic and documentation [DEFAULT]" | ||||
| 	@echo "  toxic:   Build toxic" | ||||
| 	@echo "  doc:     Build documentation" | ||||
| 	@echo "  install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")" | ||||
| 	@echo "  clean:   Remove built files" | ||||
| 	@echo "  help:    This help" | ||||
| 	@echo "  all:       Build toxic and documentation [DEFAULT]" | ||||
| 	@echo "  toxic:     Build toxic" | ||||
| 	@echo "  doc:       Build documentation" | ||||
| 	@echo "  install:   Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")" | ||||
| 	@echo "  uninstall: Remove toxic from PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")" | ||||
| 	@echo "  clean:     Remove built files" | ||||
| 	@echo "  help:      This help" | ||||
| 	@echo | ||||
| 	@echo "-- Variables --" | ||||
| 	@echo "  DISABLE_X11:            Set to \"1\" to force building without X11 support" | ||||
| 	@echo "  DISABLE_AV:             Set to \"1\" to force building without audio call support" | ||||
| 	@echo "  DISABLE_SOUND_NOTIFY:   Set to \"1\" to force building without sound notification support" | ||||
| 	@echo "  DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" | ||||
| 	@echo "  DISABLE_QRPNG:          Set to \"1\" to force building without QR exported as PNG support" | ||||
| 	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS" | ||||
| 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | ||||
| 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | ||||
|   | ||||
| @@ -1,31 +1,40 @@ | ||||
| # Install target | ||||
| install: toxic doc | ||||
| 	mkdir -p $(abspath $(DESTDIR)/$(BINDIR)) | ||||
| 	mkdir -p $(abspath $(DESTDIR)/$(DATADIR)) | ||||
| 	mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds | ||||
| 	mkdir -p $(abspath $(DESTDIR)/$(MANDIR)) | ||||
|  | ||||
| install: $(BUILD_DIR)/toxic | ||||
| 	@echo "Installing toxic executable" | ||||
| 	@install -m 0755 toxic $(abspath $(DESTDIR)/$(BINDIR)) | ||||
|  | ||||
| 	@mkdir -p $(abspath $(DESTDIR)/$(BINDIR)) | ||||
| 	@install -m 0755 $(BUILD_DIR)/toxic $(abspath $(DESTDIR)/$(BINDIR)/toxic) | ||||
| 	 | ||||
| 	@echo "Installing desktop file" | ||||
| 	@mkdir -p $(abspath $(DESTDIR)/$(APPDIR)) | ||||
| 	@install -m 0644 $(MISC_DIR)/$(DESKFILE) $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE)) | ||||
| 	 | ||||
| 	@echo "Installing data files" | ||||
| 	@mkdir -p $(abspath $(DESTDIR)/$(DATADIR)) | ||||
| 	@for f in $(DATAFILES) ; do \ | ||||
| 		install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)) ;\ | ||||
| 		file=$(abspath $(DESTDIR)/$(DATADIR))/$$f ;\ | ||||
| 		sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\ | ||||
| 		install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\ | ||||
| 		file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\ | ||||
| 		sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ | ||||
| 		mv temp_file $$file ;\ | ||||
| 	done | ||||
| 	@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds | ||||
| 	@for f in $(SNDFILES) ; do \ | ||||
| 		install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR))/sounds ;\ | ||||
| 		install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\ | ||||
| 	done | ||||
|  | ||||
| 	 | ||||
| 	@echo "Installing man pages" | ||||
| 	@mkdir -p $(abspath $(DESTDIR)/$(MANDIR)) | ||||
| 	@for f in $(MANFILES) ; do \ | ||||
| 		if [ ! -e "$(DOC_DIR)/$$f" ]; then \ | ||||
| 			continue ;\ | ||||
| 		fi ;\ | ||||
| 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\ | ||||
| 		file=$$section/$$f ;\ | ||||
| 		mkdir -p $$section ;\ | ||||
| 		install -m 0644 $(DOC_DIR)/$$f $$file ;\ | ||||
| 		sed -i'' -e 's:__VERSION__:'$(VERSION)':g' $$file ;\ | ||||
| 		sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\ | ||||
| 		sed -e 's:__VERSION__:'$(VERSION)':g' $$file > temp_file && \ | ||||
| 		mv temp_file $$file ;\ | ||||
| 		sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ | ||||
| 		mv temp_file $$file ;\ | ||||
| 		gzip -f -9 $$file ;\ | ||||
| 	done | ||||
|  | ||||
|   | ||||
							
								
								
									
										24
									
								
								cfg/targets/uninstall.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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 | ||||
							
								
								
									
										49
									
								
								doc/toxic.1
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								doc/toxic.1
									
									
									
									
									
								
							| @@ -2,12 +2,12 @@ | ||||
| .\"     Title: toxic | ||||
| .\"    Author: [see the "AUTHORS" section] | ||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2014-09-19 | ||||
| .\"      Date: 2016-09-20 | ||||
| .\"    Manual: Toxic Manual | ||||
| .\"    Source: toxic __VERSION__ | ||||
| .\"  Language: English | ||||
| .\" | ||||
| .TH "TOXIC" "1" "2014\-09\-19" "toxic __VERSION__" "Toxic Manual" | ||||
| .TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual" | ||||
| .\" ----------------------------------------------------------------- | ||||
| .\" * Define some portability stuff | ||||
| .\" ----------------------------------------------------------------- | ||||
| @@ -70,7 +70,7 @@ Encrypt an unencrypted data file\&. An error will occur if this option is used w | ||||
| Use specified | ||||
| \fIdata\-file\fR | ||||
| instead of | ||||
| \fI~/\&.config/tox/data\fR | ||||
| \fI~/\&.config/tox/toxic_profile\&.tox\fR | ||||
| .RE | ||||
| .PP | ||||
| \-h, \-\-help | ||||
| @@ -82,8 +82,8 @@ Show help message | ||||
| .RS 4 | ||||
| Use specified | ||||
| \fInodes\-file\fR | ||||
| for DHT bootstrap nodes, instead of | ||||
| \fI__DATADIR__/DHTnodes\fR | ||||
| for DHT bootstrap nodes instead of | ||||
| \fI~/\&.config/tox/DHTnodes\&.json\fR | ||||
| .RE | ||||
| .PP | ||||
| \-o, \-\-noconnect | ||||
| @@ -91,14 +91,19 @@ for DHT bootstrap nodes, instead of | ||||
| Do not connect to the DHT network | ||||
| .RE | ||||
| .PP | ||||
| \-p, \-\-proxy | ||||
| \-p, \-\-SOCKS5\-proxy | ||||
| .RS 4 | ||||
| Use proxy: Requires [IP] [port] | ||||
| Use a SOCKS5 proxy: Requires [IP] [port] | ||||
| .RE | ||||
| .PP | ||||
| \-r, \-\-dnslist | ||||
| \-P, \-\-HTTP\-proxy | ||||
| .RS 4 | ||||
| Use specified DNSservers file | ||||
| Use a HTTP proxy: Requires [IP] [port] | ||||
| .RE | ||||
| .PP | ||||
| \-r, \-\-namelist | ||||
| .RS 4 | ||||
| Use specified nameservers list | ||||
| .RE | ||||
| .PP | ||||
| \-t, \-\-force\-tcp | ||||
| @@ -106,23 +111,25 @@ Use specified DNSservers file | ||||
| Force TCP connection (use this with proxies) | ||||
| .RE | ||||
| .PP | ||||
| \-T, \-\-tcp\-relay | ||||
| .RS 4 | ||||
| Act as a TCP relay server for the network (Note: this uses significantly more bandwidth) | ||||
| .RE | ||||
| .PP | ||||
| \-u, \-\-unencrypt\-data | ||||
| .RS 4 | ||||
| Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&. | ||||
| .RE | ||||
| .PP | ||||
| \-x, \-\-nodata | ||||
| .RS 4 | ||||
| Ignore data file | ||||
| .RE | ||||
| .SH "FILES" | ||||
| .PP | ||||
| __DATADIR__/DHTnodes | ||||
| ~/\&.config/tox/DHTnodes\&.json | ||||
| .RS 4 | ||||
| Default list of DHT bootstrap nodes\&. | ||||
| Default location for list of DHT bootstrap nodes (list obtained from | ||||
| https://nodes\&.tox\&.chat)\&. This list is automatically updated\&. See | ||||
| \fBtoxic\&.conf\fR(5) for details on controlling the update frequency\&. | ||||
| .RE | ||||
| .PP | ||||
| ~/\&.config/tox/data | ||||
| ~/\&.config/tox/toxic_profile\&.tox | ||||
| .RS 4 | ||||
| Savestate which contains your personal info (nickname, Tox ID, contacts, etc) | ||||
| .RE | ||||
| @@ -139,7 +146,11 @@ Configuration example\&. | ||||
| .RE | ||||
| .SH "BUGS" | ||||
| .sp | ||||
| Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&. | ||||
| \-Unicode characters with a width larger than 1 column may cause strange behaviour\&. | ||||
| .sp | ||||
| \-Text occasionally fails to auto\-scroll to the bottom\&. | ||||
| .sp | ||||
| \-Screen flickering sometimes occurs on certain terminals\&. | ||||
| .SH "AUTHORS" | ||||
| .sp | ||||
| JFreegman <JFreegman@gmail\&.com> | ||||
| @@ -148,6 +159,6 @@ JFreegman <JFreegman@gmail\&.com> | ||||
| \fBtoxic\&.conf\fR(5) | ||||
| .SH "LINKS" | ||||
| .sp | ||||
| Project page: https://github\&.com/Tox/toxic | ||||
| Project page: https://github\&.com/JFreegman/toxic | ||||
| .sp | ||||
| IRC channel: chat\&.freenode\&.net#tox | ||||
|   | ||||
| @@ -31,44 +31,47 @@ OPTIONS | ||||
|     Use default locale | ||||
|  | ||||
| -e, --encrypt-data:: | ||||
|     Encrypt an unencrypted data file. An error will occur if this option  | ||||
|     Encrypt an unencrypted data file. An error will occur if this option | ||||
|     is used with an encrypted data file. | ||||
|  | ||||
| -f, --file data-file:: | ||||
|     Use specified 'data-file' instead of '~/.config/tox/data' | ||||
|     Use specified 'data-file' instead of '~/.config/tox/toxic_profile.tox' | ||||
|  | ||||
| -h, --help:: | ||||
|     Show help message | ||||
|  | ||||
| -n, --nodes nodes-file:: | ||||
|     Use specified 'nodes-file' for DHT bootstrap nodes, instead of | ||||
|     '{datadir}/DHTnodes' | ||||
|     Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json' | ||||
|  | ||||
| -o, --noconnect:: | ||||
|     Do not connect to the DHT network | ||||
|  | ||||
| -p, --proxy:: | ||||
|     Use proxy: Requires [IP] [port] | ||||
| -p, --SOCKS5-proxy:: | ||||
|     Use a SOCKS5 proxy: Requires [IP] [port] | ||||
|  | ||||
| -r, --dnslist:: | ||||
|     Use specified DNSservers file | ||||
| -P, --HTTP-proxy:: | ||||
|     Use a HTTP proxy: Requires [IP] [port] | ||||
|  | ||||
| -r, --namelist:: | ||||
|     Use specified nameservers list | ||||
|  | ||||
| -t, --force-tcp:: | ||||
|     Force TCP connection (use this with proxies) | ||||
|  | ||||
| -u, --unencrypt-data:: | ||||
|     Unencrypt a data file. A warning will appear if this option is used  | ||||
|     with a data file that is already unencrypted. | ||||
| -T, --tcp-relay:: | ||||
|     Act as a TCP relay server for the network (Note: this uses significantly more bandwidth) | ||||
|  | ||||
| -x, --nodata:: | ||||
|     Ignore data file | ||||
| -u, --unencrypt-data:: | ||||
|     Unencrypt a data file. A warning will appear if this option is used | ||||
|     with a data file that is already unencrypted. | ||||
|  | ||||
| FILES | ||||
| ----- | ||||
| {datadir}/DHTnodes:: | ||||
|     Default list of DHT bootstrap nodes. | ||||
| ~/.config/tox/DHTnodes.json:: | ||||
|     Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat). | ||||
|     This list is automatically updated. See *toxic.conf*(5) for details on controlling the update frequency. | ||||
|  | ||||
| ~/.config/tox/data:: | ||||
| ~/.config/tox/toxic_profile.tox:: | ||||
|     Savestate which contains your personal info (nickname, Tox ID, contacts, | ||||
|     etc) | ||||
|  | ||||
| @@ -80,9 +83,12 @@ FILES | ||||
|  | ||||
| BUGS | ||||
| ---- | ||||
| Unicode characters with a width larger than 1 column may cause strange | ||||
| behaviour. Expect more bugs and bad behaviour: this software is in a | ||||
| pre-alpha stage. | ||||
| -Unicode characters with a width larger than 1 column may cause strange | ||||
| behaviour. | ||||
|  | ||||
| -Text occasionally fails to auto-scroll to the bottom. | ||||
|  | ||||
| -Screen flickering sometimes occurs on certain terminals. | ||||
|  | ||||
| AUTHORS | ||||
| ------- | ||||
| @@ -94,6 +100,6 @@ SEE ALSO | ||||
|  | ||||
| LINKS | ||||
| ----- | ||||
| Project page: <https://github.com/Tox/toxic> | ||||
| Project page: <https://github.com/JFreegman/toxic> | ||||
|  | ||||
| IRC channel: chat.freenode.net#tox | ||||
|   | ||||
							
								
								
									
										124
									
								
								doc/toxic.conf.5
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								doc/toxic.conf.5
									
									
									
									
									
								
							| @@ -2,12 +2,12 @@ | ||||
| .\"     Title: toxic.conf | ||||
| .\"    Author: [see the "AUTHORS" section] | ||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2014-09-19 | ||||
| .\"      Date: 2016-07-21 | ||||
| .\"    Manual: Toxic Manual | ||||
| .\"    Source: toxic __VERSION__ | ||||
| .\"  Language: English | ||||
| .\" | ||||
| .TH "TOXIC\&.CONF" "5" "2014\-09\-19" "toxic __VERSION__" "Toxic Manual" | ||||
| .TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual" | ||||
| .\" ----------------------------------------------------------------- | ||||
| .\" * Define some portability stuff | ||||
| .\" ----------------------------------------------------------------- | ||||
| @@ -66,9 +66,26 @@ Configuration related to interface elements\&. | ||||
| Enable or disable timestamps\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBtime_format\fR | ||||
| .RS 4 | ||||
| Select between 24 and 12 hour time\&. Specify 24 or 12\&. Setting timestamp_format and log_timestamp_format will override this setting\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBtimestamp_format\fR | ||||
| .RS 4 | ||||
| Time format string for the interface enclosed by double quotes\&. See | ||||
| \fBdate\fR(1) | ||||
| .RE | ||||
| .PP | ||||
| \fBlog_timestamp_format\fR | ||||
| .RS 4 | ||||
| Time format string for logging enclosed by double quotes\&. See | ||||
| \fBdate\fR(1) | ||||
| .RE | ||||
| .PP | ||||
| \fBalerts\fR | ||||
| .RS 4 | ||||
| Enable or disable terminal alerts on events\&. true or false | ||||
| Enable or disable acoustic alerts on events\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBnative_colors\fR | ||||
| @@ -81,11 +98,6 @@ Select between native terminal colors and toxic color theme\&. true or false | ||||
| Enable or disable autologging\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBtime_format\fR | ||||
| .RS 4 | ||||
| Select between 24 and 12 hour time\&. Specify 24 or 12 | ||||
| .RE | ||||
| .PP | ||||
| \fBshow_typing_other\fR | ||||
| .RS 4 | ||||
| Show when others are typing in a 1\-on\-1 chat\&. true or false | ||||
| @@ -101,10 +113,87 @@ Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false | ||||
| Show welcome message on startup\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBshow_connection_msg\fR | ||||
| .RS 4 | ||||
| Enable friend connection change notifications\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBnodelist_update_freq\fR | ||||
| .RS 4 | ||||
| How often in days to update the DHT nodes list\&. (0 to disable updates) | ||||
| .RE | ||||
| .PP | ||||
| \fBhistory_size\fR | ||||
| .RS 4 | ||||
| Maximum lines for chat window history\&. Integer value\&. (for example: 700) | ||||
| .RE | ||||
| .PP | ||||
| \fBline_join\fR | ||||
| .RS 4 | ||||
| Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBline_quit\fR | ||||
| .RS 4 | ||||
| Indicator for when someone disconnects or leaves a group\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBline_alert\fR | ||||
| .RS 4 | ||||
| Indicator for alert messages\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBline_normal\fR | ||||
| .RS 4 | ||||
| Indicator for normal messages\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBline_special\fR | ||||
| .RS 4 | ||||
| Indicator for special messages\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBmplex_away\fR | ||||
| .RS 4 | ||||
| Set user status when attaching and detaching from GNU screen or tmux\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBmplex_away_note\fR | ||||
| .RS 4 | ||||
| Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&. | ||||
| .sp | ||||
| .if n \{\ | ||||
| .RS 4 | ||||
| .\} | ||||
| .nf | ||||
| The following options control whether to output a terminal bell on certain events\&. | ||||
| Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&. | ||||
| These options don\*(Aqt affect the "alerts" option\&. | ||||
| .fi | ||||
| .if n \{\ | ||||
| .RE | ||||
| .\} | ||||
| .RE | ||||
| .PP | ||||
| \fBbell_on_message\fR | ||||
| .RS 4 | ||||
| Enable/Disable the terminal bell when receiving a message\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBbell_on_filetrans\fR | ||||
| .RS 4 | ||||
| Enable/Disable the terminal bell when receiving a filetransfer\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBbell_on_filetrans_accept\fR | ||||
| .RS 4 | ||||
| Enable/Disable the terminal bell when a filetransfer was accepted\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBbell_on_invite\fR | ||||
| .RS 4 | ||||
| Enable/Disable the terminal bell when receiving a group/call invite\&. true or false | ||||
| .RE | ||||
| .RE | ||||
| .PP | ||||
| \fBaudio\fR | ||||
| @@ -147,6 +236,11 @@ Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB) | ||||
| .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 | ||||
| @@ -155,7 +249,7 @@ Configuration related to notification sounds\&. Special value "silent" can be us | ||||
|  | ||||
| Each value is a string which corresponds to the absolute path of a wav sound file\&. | ||||
| .PP | ||||
| \fBerror\fR | ||||
| \fBnotif_error\fR | ||||
| .RS 4 | ||||
| Sound to play when an error occurs\&. | ||||
| .RE | ||||
| @@ -256,6 +350,16 @@ Key combination to scroll contacts list up\&. | ||||
| .RS 4 | ||||
| Key combination to scroll contacts list down\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBtoggle_peerlist\fR | ||||
| .RS 4 | ||||
| Toggle the peer list on and off\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBtoggle_paste_mode\fR | ||||
| .RS 4 | ||||
| Toggle treating linebreaks as enter key press\&. | ||||
| .RE | ||||
| .RE | ||||
| .SH "FILES" | ||||
| .PP | ||||
| @@ -273,7 +377,7 @@ Configuration example\&. | ||||
| \fBtoxic\fR(1) | ||||
| .SH "RESOURCES" | ||||
| .sp | ||||
| Project page: https://github\&.com/Tox/toxic | ||||
| Project page: https://github\&.com/JFreegman/toxic | ||||
| .sp | ||||
| IRC channel: chat\&.freenode\&.net#tox | ||||
| .SH "AUTHORS" | ||||
|   | ||||
| @@ -42,8 +42,20 @@ OPTIONS | ||||
|     *timestamps*;; | ||||
|         Enable or disable timestamps. true or false | ||||
|  | ||||
|     *time_format*;; | ||||
| 	Select between 24 and 12 hour time. Specify 24 or 12. Setting | ||||
|         timestamp_format and log_timestamp_format will override this setting. | ||||
|  | ||||
|     *timestamp_format*;; | ||||
|         Time format string for the interface enclosed by double quotes. | ||||
|         See *date*(1) | ||||
|  | ||||
|     *log_timestamp_format*;; | ||||
|         Time format string for logging enclosed by double quotes. | ||||
|         See *date*(1) | ||||
|  | ||||
|     *alerts*;; | ||||
|         Enable or disable terminal alerts on events. true or false | ||||
|         Enable or disable acoustic alerts on events. true or false | ||||
|  | ||||
|     *native_colors*;; | ||||
|         Select between native terminal colors and toxic color theme. true or false | ||||
| @@ -51,9 +63,6 @@ OPTIONS | ||||
|     *autolog*;; | ||||
|         Enable or disable autologging. true or false | ||||
|  | ||||
|     *time_format*;; | ||||
|         Select between 24 and 12 hour time. Specify 24 or 12 | ||||
|  | ||||
|     *show_typing_other*;; | ||||
|         Show when others are typing in a 1-on-1 chat. true or false | ||||
|  | ||||
| @@ -63,9 +72,56 @@ OPTIONS | ||||
|     *show_welcome_msg*;; | ||||
|         Show welcome message on startup. true or false | ||||
|  | ||||
|     *show_connection_msg*;; | ||||
|         Enable friend connection change notifications. true or false | ||||
|  | ||||
|     *nodelist_update_freq*;; | ||||
|         How often in days to update the DHT nodes list. (0 to disable updates) | ||||
|  | ||||
|     *history_size*;; | ||||
|         Maximum lines for chat window history. Integer value. (for example: 700) | ||||
|  | ||||
|     *line_join*;; | ||||
|         Indicator for when someone connects or joins a group. | ||||
|         Three characters max for line_ settings. | ||||
|  | ||||
|     *line_quit*;; | ||||
|         Indicator for when someone disconnects or leaves a group. | ||||
|  | ||||
|     *line_alert*;; | ||||
|         Indicator for alert messages. | ||||
|  | ||||
|     *line_normal*;; | ||||
|         Indicator for normal messages. | ||||
|  | ||||
|     *line_special*;; | ||||
|         Indicator for special messages. | ||||
|  | ||||
|     *mplex_away*;; | ||||
|         Set user status when attaching and detaching from GNU screen or tmux. | ||||
|         true or false | ||||
|  | ||||
|     *mplex_away_note*;; | ||||
|         Status message to set when status is set to away due to screen/tmux | ||||
|         detach. When attaching, the status message is set back to the original | ||||
|         value. | ||||
|  | ||||
|     The following options control whether to output a terminal bell on certain events. | ||||
|     Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window. | ||||
|     These options don't affect the "alerts" option. | ||||
|  | ||||
|     *bell_on_message*;; | ||||
|         Enable/Disable the terminal bell when receiving a message. true or false | ||||
|  | ||||
|     *bell_on_filetrans*;; | ||||
|         Enable/Disable the terminal bell when receiving a filetransfer. true or false | ||||
|  | ||||
|     *bell_on_filetrans_accept*;; | ||||
|         Enable/Disable the terminal bell when a filetransfer was accepted. true or false | ||||
|  | ||||
|     *bell_on_invite*;; | ||||
|         Enable/Disable the terminal bell when receiving a group/call invite. true or false | ||||
|  | ||||
|  | ||||
| *audio*:: | ||||
|     Configuration related to audio devices. | ||||
| @@ -93,13 +149,17 @@ OPTIONS | ||||
|     *chatlogs_path*;; | ||||
|         Default path for chatlogs. String value. Absolute path for chatlog files. | ||||
|  | ||||
|     *password_eval*;; | ||||
|         Replace password prompt by running this command and using its output as | ||||
| 	the password. | ||||
|  | ||||
| *sounds*:: | ||||
|     Configuration related to notification sounds. | ||||
|     Special value "silent" can be used to disable a specific notification. + | ||||
|     Each value is a string which corresponds to the absolute path of a wav | ||||
|     sound file. | ||||
|  | ||||
|     *error*;; | ||||
|     *notif_error*;; | ||||
|         Sound to play when an error occurs. | ||||
|  | ||||
|     *self_log_in*;; | ||||
| @@ -161,6 +221,12 @@ OPTIONS | ||||
|     *peer_list_down*;; | ||||
|         Key combination to scroll contacts list down. | ||||
|  | ||||
|     *toggle_peerlist*;; | ||||
|         Toggle the peer list on and off. | ||||
|  | ||||
|     *toggle_paste_mode*;; | ||||
|         Toggle treating linebreaks as enter key press. | ||||
|  | ||||
|  | ||||
| FILES | ||||
| ----- | ||||
| @@ -178,7 +244,7 @@ SEE ALSO | ||||
|  | ||||
| RESOURCES | ||||
| --------- | ||||
| Project page: <https://github.com/Tox/toxic> | ||||
| Project page: <https://github.com/JFreegman/toxic> | ||||
|  | ||||
| IRC channel:  chat.freenode.net#tox | ||||
|  | ||||
|   | ||||
| @@ -1,11 +0,0 @@ | ||||
| 192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F | ||||
| 37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927 | ||||
| 144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F | ||||
| 23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074 | ||||
| 54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029 | ||||
| 192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67 | ||||
| 37.59.102.176 33445 B98A2CEAA6C6A2FADC2C3632D284318B60FE5375CCB41EFA081AB67F500C1B0B | ||||
| 178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057 | ||||
| 107.161.17.51 33445 7BE3951B97CA4B9ECDDA768E8C52BA19E9E2690AB584787BF4C90E04DBB75111 | ||||
| 31.7.57.236 443 2A4B50D1D525DA2E669592A20C327B5FAD6C7E5962DC69296F9FEC77C4436E4E | ||||
| 63.165.243.15 443 8CD087E31C67568103E8C2A28653337E90E6B8EDA0D765D57C6B5172B4F1F04C | ||||
| @@ -1,2 +0,0 @@ | ||||
| utox.org d3154f65d28a5b41a05d4ac7e4b39c6b1c233cc857fb365c56e8392737462a12 | ||||
| toxme.se 5d72c517df6aec54f1e977a6b6f25914ea4cf7277a85027cd9f5196df17e0b13 | ||||
							
								
								
									
										1
									
								
								misc/nameservers
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								misc/nameservers
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25 | ||||
| @@ -5,9 +5,21 @@ ui = { | ||||
|   // true to enable timestamps, false to disable | ||||
|   timestamps=true; | ||||
|  | ||||
|   // true to enable terminal alerts on messages, false to disable | ||||
|   // true to enable acoustic alerts on messages, false to disable | ||||
|   alerts=true; | ||||
|  | ||||
|   // Output a bell when receiving a message (see manpage) | ||||
|   bell_on_message=true | ||||
|  | ||||
|   // Output a bell when receiving a filetransfer (see manpage) | ||||
|   bell_on_filetrans=true | ||||
|  | ||||
|   // Don't output a bell when a filetransfer was accepted (see manpage) | ||||
|   bell_on_filetrans_accept=false | ||||
|  | ||||
|   // Output a bell when receiving a group/call invite (see manpage) | ||||
|   bell_on_invite=true | ||||
|  | ||||
|   // true to use native terminal colours, false to use toxic default colour theme | ||||
|   native_colors=false; | ||||
|  | ||||
| @@ -17,6 +29,9 @@ ui = { | ||||
|   // 24 or 12 hour time | ||||
|   time_format=24; | ||||
|  | ||||
|   // Timestamp format string according to date/strftime format. Overrides time_format setting | ||||
|   timestamp_format="%H:%M:%S"; | ||||
|  | ||||
|   // true to show you when others are typing a message in 1-on-1 chats | ||||
|   show_typing_other=true; | ||||
|  | ||||
| @@ -26,8 +41,35 @@ ui = { | ||||
|   // true to show the welcome message on startup | ||||
|   show_welcome_msg=true; | ||||
|  | ||||
|   // true to show friend connection change messages on the home screen | ||||
|   show_connection_msg=true; | ||||
|  | ||||
|   // How often in days to update the DHT nodes list. (0 to disable updates) | ||||
|   nodeslist_update_freq=7; | ||||
|  | ||||
|   // maximum lines for chat window history | ||||
|   history_size=700; | ||||
|  | ||||
|   // Indicator for display when someone connects or joins a group. | ||||
|   line_join="-->"; | ||||
|  | ||||
|   // Indicator for display when someone disconnects or leaves a group. | ||||
|   line_quit="<--"; | ||||
|  | ||||
|   // Indicator for alert messages. | ||||
|   line_alert="-!-"; | ||||
|  | ||||
|   // Indicator for normal messages. | ||||
|   line_normal="---"; | ||||
|  | ||||
|   // Indicator for special messages (currently only used for private group messages) | ||||
|   line_special=">>>"; | ||||
|  | ||||
|   // true to change status based on screen/tmux attach/detach, false to disable | ||||
|   mplex_away=true; | ||||
|  | ||||
|   // Status message to use when status set to away due to screen/tmux detach | ||||
|   mplex_away_note="Away from keyboard, be back soon!" | ||||
| }; | ||||
|  | ||||
| audio = { | ||||
| @@ -36,7 +78,7 @@ audio = { | ||||
|  | ||||
|   // preferred audio output device; numbers correspond to /lsdev out | ||||
|   output_device=0; | ||||
|    | ||||
|  | ||||
|   // default VAD treshold; float (recommended values are around 40) | ||||
|   VAD_treshold=40.0; | ||||
| }; | ||||
| @@ -45,7 +87,7 @@ tox = { | ||||
|   // Path for downloaded files | ||||
|   // download_path="/home/USERNAME/Downloads/"; | ||||
|  | ||||
|   // Path for your avatar (file must be a .png and cannot exceed 16.3 KiB) | ||||
|   // Path for your avatar (file must be a .png and cannot exceed 64 KiB) | ||||
|   // avatar_path="/home/USERNAME/Pictures/youravatar.png"; | ||||
|  | ||||
|   // Path for chatlogs | ||||
| @@ -54,20 +96,18 @@ tox = { | ||||
|  | ||||
| // To disable a sound set the path to "silent" | ||||
| sounds = { | ||||
|   error="__DATADIR__/sounds/Error.wav"; | ||||
|   self_log_in="__DATADIR__/sounds/LogIn.wav"; | ||||
|   self_log_out="__DATADIR__/sounds/LogOut.wav"; | ||||
|   user_log_in="__DATADIR__/sounds/ContactLogsIn.wav"; | ||||
|   user_log_out="__DATADIR__/sounds/ContactLogsOut.wav"; | ||||
|   call_incoming="__DATADIR__/sounds/IncomingCall.wav"; | ||||
|   call_outgoing="__DATADIR__/sounds/OutgoingCall.wav"; | ||||
|   generic_message="__DATADIR__/sounds/NewMessage.wav"; | ||||
|   transfer_pending="__DATADIR__/sounds/TransferPending.wav"; | ||||
|   transfer_completed="__DATADIR__/sounds/TransferComplete.wav"; | ||||
|   error="__DATADIR__/sounds/ToxicError.wav"; | ||||
|   user_log_in="__DATADIR__/sounds/ToxicContactOnline.wav"; | ||||
|   user_log_out="__DATADIR__/sounds/ToxicContactOffline.wav"; | ||||
|   call_incoming="__DATADIR__/sounds/ToxicIncomingCall.wav"; | ||||
|   call_outgoing="__DATADIR__/sounds/ToxicOutgoingCall.wav"; | ||||
|   generic_message="__DATADIR__/sounds/ToxicRecvMessage.wav"; | ||||
|   transfer_pending="__DATADIR__/sounds/ToxicTransferStart.wav"; | ||||
|   transfer_completed="__DATADIR__/sounds/ToxicTransferComplete.wav"; | ||||
| }; | ||||
|  | ||||
| // Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive) | ||||
| // Note: All printable keys register as input | ||||
| // Note: Ctrl+M does not work | ||||
| keys = { | ||||
|   next_tab="Ctrl+P"; | ||||
|   prev_tab="Ctrl+O"; | ||||
| @@ -78,5 +118,7 @@ keys = { | ||||
|   page_bottom="Ctrl+H"; | ||||
|   peer_list_up="Ctrl+["; | ||||
|   peer_list_down="Ctrl+]"; | ||||
|   toggle_peerlist="Ctrl+b"; | ||||
|   toggle_paste_mode="Ctrl+T"; | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										11
									
								
								misc/toxic.desktop
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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; | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/Error.wav
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/Error.wav
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								sounds/LogIn.wav
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sounds/LogIn.wav
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											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.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1 +1,2 @@ | ||||
| Tox's sounds are licensed under the "Creative Commons Attribution 3.0 Unported", all credit attributed to Adam Reid. | ||||
| ToxicError.wav, ToxicRecvMessage.wav, ToxicContactOffline.wav, ToxicIncomingCall.wav, ToxicTransferComplete.wav, ToxicContactOnline.wav, ToxicOutgoingCall.wav and ToxicTransferStart.wav | ||||
| are licensed under the "Creative Commons Attribution 3.0 Unported". All credit attributed to Jfreegman. | ||||
|   | ||||
							
								
								
									
										760
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										760
									
								
								src/audio_call.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -20,12 +20,14 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef AUDIO_H | ||||
| #define AUDIO_H | ||||
| #ifndef AUDIO_CALL_H | ||||
| #define AUDIO_CALL_H | ||||
|  | ||||
| #include <tox/toxav.h> | ||||
|  | ||||
| #include "device.h" | ||||
| #include "audio_device.h" | ||||
|  | ||||
| #define MAX_CALLS 10 | ||||
|  | ||||
| typedef enum _AudioError { | ||||
|     ae_None = 0, | ||||
| @@ -34,12 +36,58 @@ typedef enum _AudioError { | ||||
|     ae_StartingCoreAudio = 1 << 2 | ||||
| } AudioError; | ||||
|  | ||||
| /* You will have to pass pointer to first member of 'windows' | ||||
|  * declared in windows.c otherwise undefined behaviour will | ||||
|  */ | ||||
| ToxAv *init_audio(ToxWindow *self, Tox *tox); | ||||
| void terminate_audio(); | ||||
| #ifdef VIDEO | ||||
| typedef enum _VideoError { | ||||
|     ve_None = 0, | ||||
|     ve_StartingCaptureDevice = 1 << 0, | ||||
|     ve_StartingOutputDevice = 1 << 1, | ||||
|     ve_StartingCoreVideo = 1 << 2 | ||||
| } VideoError; | ||||
|  | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| typedef struct Call { | ||||
|     pthread_t ttid; /* Transmission thread id */ | ||||
|     bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */ | ||||
|     uint32_t in_idx, out_idx; /* Audio Index */ | ||||
| #ifdef VIDEO | ||||
|     uint32_t vin_idx, vout_idx; /* Video Index */ | ||||
| #endif /* VIDEO */ | ||||
|     pthread_mutex_t mutex; | ||||
| } Call; | ||||
|  | ||||
| struct CallControl { | ||||
|     AudioError audio_errors; | ||||
| #ifdef VIDEO | ||||
|     VideoError video_errors; | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
|     ToxAV *av; | ||||
|     ToxWindow *prompt; | ||||
|  | ||||
|     Call calls[MAX_CALLS]; | ||||
|     uint32_t call_state; | ||||
|     bool pending_call; | ||||
|     bool audio_enabled; | ||||
|     bool video_enabled; | ||||
|  | ||||
|     uint32_t audio_bit_rate; | ||||
|     int32_t audio_frame_duration; | ||||
|     uint32_t audio_sample_rate; | ||||
|     uint8_t audio_channels; | ||||
|  | ||||
|     uint32_t video_bit_rate; | ||||
|     int32_t video_frame_duration; | ||||
|  | ||||
| } CallControl; | ||||
|  | ||||
| struct CallControl CallControl; | ||||
|  | ||||
| /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | ||||
| ToxAV *init_audio(ToxWindow *self, Tox *tox); | ||||
| void terminate_audio(); | ||||
| int start_transmission(ToxWindow *self, Call *call); | ||||
| int stop_transmission(Call *call, uint32_t friend_number); | ||||
| void stop_current_call(ToxWindow *self); | ||||
|  | ||||
| #endif /* AUDIO_H */ | ||||
| #endif /* AUDIO_CALL_H */ | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| /*  device.c
 | ||||
| /*  audio_device.c
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
| @@ -20,7 +20,7 @@ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "device.h" | ||||
| #include "audio_device.h" | ||||
| 
 | ||||
| #ifdef AUDIO | ||||
| #include "audio_call.h" | ||||
| @@ -38,8 +38,8 @@ | ||||
| /* compatibility with older versions of OpenAL */ | ||||
| #ifndef ALC_ALL_DEVICES_SPECIFIER | ||||
| #include <AL/alext.h> | ||||
| #endif | ||||
| #endif | ||||
| #endif  /* ALC_ALL_DEVICES_SPECIFIER */ | ||||
| #endif  /* __APPLE__ */ | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <string.h> | ||||
| @@ -48,7 +48,6 @@ | ||||
| #include <stdlib.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #define OPENAL_BUFS 5 | ||||
| #define inline__ inline __attribute__((always_inline)) | ||||
| 
 | ||||
| extern struct user_settings *user_settings; | ||||
| @@ -57,16 +56,16 @@ typedef struct Device { | ||||
|     ALCdevice  *dhndl;                     /* Handle of device selected/opened */ | ||||
|     ALCcontext *ctx;                       /* Device context */ | ||||
|     DataHandleCallback cb;                 /* Use this to handle data from input device usually */ | ||||
|     void* cb_data;                         /* Data to be passed to callback */ | ||||
|     int32_t call_idx;                      /* ToxAv call index */ | ||||
|      | ||||
|     void *cb_data;                         /* Data to be passed to callback */ | ||||
|     int32_t friend_number;                      /* ToxAV friend number */ | ||||
| 
 | ||||
|     uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */ | ||||
|     uint32_t ref_count; | ||||
|     int32_t selection; | ||||
|     bool enable_VAD; | ||||
|     bool muted; | ||||
|     pthread_mutex_t mutex[1]; | ||||
|     uint32_t sample_rate;  | ||||
|     uint32_t sample_rate; | ||||
|     uint32_t frame_duration; | ||||
|     int32_t sound_mode; | ||||
| #ifdef AUDIO | ||||
| @@ -81,7 +80,7 @@ Device *running[2][MAX_DEVICES] = {{NULL}};     /* Running devices */ | ||||
| uint32_t primary_device[2];          /* Primary device */ | ||||
| 
 | ||||
| #ifdef AUDIO | ||||
| static ToxAv* av = NULL; | ||||
| static ToxAV *av = NULL; | ||||
| #endif /* AUDIO */ | ||||
| 
 | ||||
| /* q_mutex */ | ||||
| @@ -90,13 +89,13 @@ static ToxAv* av = NULL; | ||||
| pthread_mutex_t mutex; | ||||
| 
 | ||||
| 
 | ||||
| bool thread_running = true,  | ||||
|       thread_paused = true;               /* Thread control */ | ||||
| bool thread_running = true, | ||||
|      thread_paused = true;               /* Thread control */ | ||||
| 
 | ||||
| void* thread_poll(void*); | ||||
| void *thread_poll(void *); | ||||
| /* Meet devices */ | ||||
| #ifdef AUDIO | ||||
| DeviceError init_devices(ToxAv* av_) | ||||
| DeviceError init_devices(ToxAV *av_) | ||||
| #else | ||||
| DeviceError init_devices() | ||||
| #endif /* AUDIO */ | ||||
| @@ -104,21 +103,28 @@ DeviceError init_devices() | ||||
|     const char *stringed_device_list; | ||||
| 
 | ||||
|     size[input] = 0; | ||||
| 
 | ||||
|     if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) { | ||||
|         ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); | ||||
|          | ||||
| 
 | ||||
|         for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) { | ||||
|             devices_names[input][size[input]] = stringed_device_list;                         | ||||
|             devices_names[input][size[input]] = stringed_device_list; | ||||
|             stringed_device_list += strlen( stringed_device_list ) + 1; | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     size[output] = 0; | ||||
|     if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) { | ||||
| 
 | ||||
|     if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) | ||||
|         stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); | ||||
|     else | ||||
|         stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER); | ||||
| 
 | ||||
|     if (stringed_device_list) { | ||||
|         ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); | ||||
|          | ||||
| 
 | ||||
|         for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) { | ||||
|             devices_names[output][size[output]] = stringed_device_list;             | ||||
|             devices_names[output][size[output]] = stringed_device_list; | ||||
|             stringed_device_list += strlen( stringed_device_list ) + 1; | ||||
|         } | ||||
|     } | ||||
| @@ -126,44 +132,49 @@ DeviceError init_devices() | ||||
|     // 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;     | ||||
|      | ||||
| 
 | ||||
|     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) {  | ||||
| 
 | ||||
|     Device *device = running[type][device_idx]; | ||||
| 
 | ||||
|     if (!device) { | ||||
|         unlock; | ||||
|         return de_DeviceNotActive; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     device->muted = !device->muted; | ||||
|      | ||||
| 
 | ||||
|     unlock; | ||||
|     return de_None; | ||||
| } | ||||
| @@ -172,17 +183,18 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx) | ||||
| DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||
| 
 | ||||
|     lock; | ||||
|      | ||||
|     Device* device = running[input][device_idx]; | ||||
|      | ||||
|     if (!device) {  | ||||
| 
 | ||||
|     Device *device = running[input][device_idx]; | ||||
| 
 | ||||
|     if (!device) { | ||||
|         unlock; | ||||
|         return de_DeviceNotActive; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     device->VAD_treshold = value; | ||||
|      | ||||
| 
 | ||||
|     unlock; | ||||
|     return de_None; | ||||
| } | ||||
| @@ -192,105 +204,115 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) | ||||
| DeviceError set_primary_device(DeviceType type, int32_t selection) | ||||
| { | ||||
|     if (size[type] <= selection || selection < 0) return de_InvalidSelection; | ||||
| 
 | ||||
|     primary_device[type] = selection; | ||||
|      | ||||
| 
 | ||||
|     return de_None; | ||||
| } | ||||
| 
 | ||||
| DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels) | ||||
| DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration, | ||||
|                                 uint8_t channels) | ||||
| { | ||||
|     return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels); | ||||
| } | ||||
| 
 | ||||
| void get_primary_device_name(DeviceType type, char *buf, int size) | ||||
| { | ||||
|     memcpy(buf, ddevice_names[type], size); | ||||
| } | ||||
| 
 | ||||
| // TODO: generate buffers separately
 | ||||
| DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels) | ||||
| DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate, | ||||
|                         uint32_t frame_duration, uint8_t channels) | ||||
| { | ||||
|     if (size[type] <= selection || selection < 0) return de_InvalidSelection; | ||||
| 
 | ||||
|     if (channels != 1 && channels != 2) return de_UnsupportedMode; | ||||
|      | ||||
| 
 | ||||
|     lock; | ||||
| 
 | ||||
|     const uint32_t frame_size = (sample_rate * frame_duration / 1000); | ||||
|      | ||||
| 
 | ||||
|     uint32_t i; | ||||
|     for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; i ++); | ||||
|      | ||||
|     if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; } | ||||
|     else *device_idx = i; | ||||
|      | ||||
| 
 | ||||
|     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][*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 *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],  | ||||
|         device->dhndl = alcCaptureOpenDevice(devices_names[type][selection], | ||||
|                                              sample_rate, device->sound_mode, frame_size * 2); | ||||
|     #ifdef AUDIO | ||||
| #ifdef AUDIO | ||||
|         device->VAD_treshold = user_settings->VAD_treshold; | ||||
|     #endif | ||||
|     } | ||||
|     else {  | ||||
| #endif | ||||
|     } else { | ||||
|         device->dhndl = alcOpenDevice(devices_names[type][selection]); | ||||
|         if ( !device->dhndl ) {  | ||||
| 
 | ||||
|         if ( !device->dhndl ) { | ||||
|             free(device); | ||||
|             running[type][*device_idx] = NULL; | ||||
|             unlock; | ||||
|             return de_FailedStart; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         device->ctx = alcCreateContext(device->dhndl, NULL); | ||||
|         alcMakeContextCurrent(device->ctx); | ||||
|          | ||||
| 
 | ||||
|         alGenBuffers(OPENAL_BUFS, device->buffers); | ||||
|         alGenSources((uint32_t)1, &device->source); | ||||
|         alSourcei(device->source, AL_LOOPING, AL_FALSE); | ||||
|          | ||||
| 
 | ||||
|         uint16_t zeros[frame_size]; | ||||
|         memset(zeros, 0, frame_size*2); | ||||
|          | ||||
|         for ( i =0; i < OPENAL_BUFS; ++i) { | ||||
|             alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate); | ||||
|         memset(zeros, 0, frame_size * 2); | ||||
| 
 | ||||
|         for ( i = 0; i < OPENAL_BUFS; ++i ) { | ||||
|             alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers); | ||||
|         alSourcePlay(device->source); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     if (alcGetError(device->dhndl) != AL_NO_ERROR) { | ||||
|         free(device); | ||||
|         running[type][*device_idx] = NULL; | ||||
|         unlock; | ||||
|         return de_FailedStart; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     if (type == input) { | ||||
|         alcCaptureStart(device->dhndl); | ||||
|         thread_paused = false; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     unlock; | ||||
|     return de_None; | ||||
| } | ||||
| @@ -298,102 +320,101 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx | ||||
| DeviceError close_device(DeviceType type, uint32_t device_idx) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||
|      | ||||
| 
 | ||||
|     lock; | ||||
|     Device* device = running[type][device_idx]; | ||||
|     Device *device = running[type][device_idx]; | ||||
|     DeviceError rc = de_None; | ||||
|      | ||||
|     if (!device) {  | ||||
| 
 | ||||
|     if (!device) { | ||||
|         unlock; | ||||
|         return de_DeviceNotActive; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     running[type][device_idx] = NULL; | ||||
|      | ||||
| 
 | ||||
|     if ( !device->ref_count ) { | ||||
|          | ||||
| //         printf("Closed device ");
 | ||||
|          | ||||
|         if (type == input) { | ||||
|             if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError; | ||||
|         } | ||||
|         else {  | ||||
|         } else { | ||||
|             if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx); | ||||
|              | ||||
| 
 | ||||
|             alDeleteSources(1, &device->source); | ||||
|             alDeleteBuffers(OPENAL_BUFS, device->buffers); | ||||
|              | ||||
|             if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError; | ||||
| 
 | ||||
|             alcMakeContextCurrent(NULL); | ||||
| 
 | ||||
|             if ( device->ctx ) alcDestroyContext(device->ctx); | ||||
| 
 | ||||
|             if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         free(device); | ||||
|     } | ||||
|     else device->ref_count--; | ||||
|      | ||||
|     } else device->ref_count--; | ||||
| 
 | ||||
|     unlock; | ||||
|     return rc; | ||||
| } | ||||
| 
 | ||||
| DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD) | ||||
| {     | ||||
|     if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)  | ||||
| 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]->call_idx = call_idx; | ||||
|     running[input][device_idx]->friend_number = friend_number; | ||||
|     unlock; | ||||
|      | ||||
| 
 | ||||
|     return de_None; | ||||
| } | ||||
| 
 | ||||
| inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels) | ||||
| inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels, | ||||
|                                uint32_t sample_rate) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||
|      | ||||
|     Device* device = running[output][device_idx]; | ||||
|      | ||||
| 
 | ||||
|     Device *device = running[output][device_idx]; | ||||
| 
 | ||||
|     if (!device || device->muted) return de_DeviceNotActive; | ||||
|      | ||||
| 
 | ||||
|     pthread_mutex_lock(device->mutex); | ||||
|      | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
|     ALuint bufid; | ||||
|     ALint processed, queued; | ||||
|     alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed); | ||||
|     alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued); | ||||
|      | ||||
|     if(processed) { | ||||
| 
 | ||||
|     if (processed) { | ||||
|         ALuint bufids[processed]; | ||||
|         alSourceUnqueueBuffers(device->source, processed, bufids); | ||||
|         alDeleteBuffers(processed - 1, bufids + 1); | ||||
|         bufid = bufids[0]; | ||||
|     }  | ||||
|     else if(queued < 16) alGenBuffers(1, &bufid); | ||||
|     else {  | ||||
|     } else if (queued < 16) alGenBuffers(1, &bufid); | ||||
|     else { | ||||
|         pthread_mutex_unlock(device->mutex); | ||||
|         return de_Busy; | ||||
|     } | ||||
|      | ||||
|      | ||||
|     alBufferData(bufid, device->sound_mode, data, lenght * 2 * channels, device->sample_rate); | ||||
| 
 | ||||
| 
 | ||||
|     alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels, | ||||
|                  sample_rate); | ||||
|     alSourceQueueBuffers(device->source, 1, &bufid); | ||||
|      | ||||
| 
 | ||||
|     ALint state; | ||||
|     alGetSourcei(device->source, AL_SOURCE_STATE, &state); | ||||
|      | ||||
|     if(state != AL_PLAYING) alSourcePlay(device->source); | ||||
|      | ||||
|      | ||||
| 
 | ||||
|     if (state != AL_PLAYING) alSourcePlay(device->source); | ||||
| 
 | ||||
| 
 | ||||
|     pthread_mutex_unlock(device->mutex); | ||||
|     return de_None; | ||||
| } | ||||
| 
 | ||||
| void* thread_poll (void* arg) // TODO: maybe use thread for every input source
 | ||||
| void *thread_poll (void *arg) // TODO: maybe use thread for every input source
 | ||||
| { | ||||
|     /*
 | ||||
|      * NOTE: We only need to poll input devices for data. | ||||
| @@ -401,53 +422,66 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source | ||||
|     (void)arg; | ||||
|     uint32_t i; | ||||
|     int32_t sample = 0; | ||||
|      | ||||
|      | ||||
|     while (thread_running) | ||||
|     { | ||||
|         if (thread_paused) usleep(10000); /* Wait for unpause. */ | ||||
|         else | ||||
|         { | ||||
|             for (i = 0; i < size[input]; i ++)  | ||||
|             { | ||||
| 
 | ||||
| 
 | ||||
|     while (1) { | ||||
|         lock; | ||||
| 
 | ||||
|         if (!thread_running) { | ||||
|             unlock; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         bool paused = thread_paused; | ||||
|         unlock; | ||||
| 
 | ||||
|         /* Wait for unpause. */ | ||||
|         if (paused) { | ||||
|             usleep(10000); | ||||
|         } | ||||
| 
 | ||||
|         else { | ||||
|             for (i = 0; i < size[input]; ++i) { | ||||
|                 lock; | ||||
|                 if (running[input][i] != NULL)  | ||||
|                 { | ||||
| 
 | ||||
|                 if (running[input][i] != NULL) { | ||||
|                     alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample); | ||||
|                      | ||||
| 
 | ||||
|                     int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000); | ||||
|                      | ||||
|                     if (sample < f_size) {  | ||||
| 
 | ||||
|                     if (sample < f_size) { | ||||
|                         unlock; | ||||
|                         continue; | ||||
|                     } | ||||
|                     Device* device = running[input][i]; | ||||
|                      | ||||
| 
 | ||||
|                     Device *device = running[input][i]; | ||||
| 
 | ||||
|                     int16_t frame[16000]; | ||||
|                     alcCaptureSamples(device->dhndl, frame, f_size); | ||||
|                      | ||||
|                     if ( device->muted  | ||||
|                     #ifdef AUDIO | ||||
|                         || (device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold)) | ||||
|                     #endif /* AUDIO */ | ||||
|                         ) | ||||
|                         { unlock; continue; } /* Skip if no voice activity */ | ||||
|                      | ||||
| 
 | ||||
|                     if (device->muted) { | ||||
|                         unlock; | ||||
|                         continue; | ||||
|                     } | ||||
| 
 | ||||
|                     if ( device->cb ) device->cb(frame, f_size, device->cb_data); | ||||
|                 }  | ||||
|                 } | ||||
| 
 | ||||
|                 unlock; | ||||
|             } | ||||
| 
 | ||||
|             usleep(5000); | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     pthread_exit(NULL); | ||||
| } | ||||
| 
 | ||||
| void print_devices(ToxWindow* self, DeviceType type) | ||||
| void print_devices(ToxWindow *self, DeviceType type) | ||||
| { | ||||
|     int i = 0; | ||||
|     for ( ; i < size[type]; i ++) | ||||
|     int i; | ||||
| 
 | ||||
|     for (i = 0; i < size[type]; ++i) | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]); | ||||
| 
 | ||||
|     return; | ||||
| @@ -458,10 +492,10 @@ DeviceError selection_valid(DeviceType type, int32_t selection) | ||||
|     return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None; | ||||
| } | ||||
| 
 | ||||
| void* get_device_callback_data(uint32_t device_idx) | ||||
| void *get_device_callback_data(uint32_t device_idx) | ||||
| { | ||||
|     if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)  | ||||
|     if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) | ||||
|         return NULL; | ||||
|          | ||||
| 
 | ||||
|     return running[input][device_idx]->cb_data; | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| /*  device.h
 | ||||
| /*  audio_device.h
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
| @@ -22,13 +22,14 @@ | ||||
| 
 | ||||
| /*
 | ||||
|  * You can have multiple sources (Input devices) but only one output device. | ||||
|  * Pass buffers to output device via write();  | ||||
|  * Pass buffers to output device via write(); | ||||
|  * Read from running input device(s) via select()/callback combo. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef DEVICE_H | ||||
| #define DEVICE_H | ||||
| #ifndef AUDIO_DEVICE_H | ||||
| #define AUDIO_DEVICE_H | ||||
| 
 | ||||
| #define OPENAL_BUFS 5 | ||||
| #define MAX_DEVICES 32 | ||||
| #include <inttypes.h> | ||||
| #include "windows.h" | ||||
| @@ -51,11 +52,11 @@ typedef enum DeviceError { | ||||
|     de_AlError = -9, | ||||
| } DeviceError; | ||||
| 
 | ||||
| typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data); | ||||
| typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data); | ||||
| 
 | ||||
| 
 | ||||
| #ifdef AUDIO | ||||
| DeviceError init_devices(ToxAv* av); | ||||
| DeviceError init_devices(ToxAV *av); | ||||
| #else | ||||
| DeviceError init_devices(); | ||||
| #endif /* AUDIO */ | ||||
| @@ -63,8 +64,9 @@ DeviceError init_devices(); | ||||
| DeviceError terminate_devices(); | ||||
| 
 | ||||
| /* Callback handles ready data from INPUT device */ | ||||
| DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD); | ||||
| void* get_device_callback_data(uint32_t device_idx); | ||||
| DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, | ||||
|                                      void *data, bool enable_VAD); | ||||
| void *get_device_callback_data(uint32_t device_idx); | ||||
| 
 | ||||
| /* toggle device mute */ | ||||
| DeviceError device_mute(DeviceType type, uint32_t device_idx); | ||||
| @@ -74,16 +76,20 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value); | ||||
| #endif | ||||
| 
 | ||||
| DeviceError set_primary_device(DeviceType type, int32_t selection); | ||||
| DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); | ||||
| DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration, | ||||
|                                 uint8_t channels); | ||||
| /* Start device */ | ||||
| DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); | ||||
| DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate, | ||||
|                         uint32_t frame_duration, uint8_t channels); | ||||
| /* Stop device */ | ||||
| DeviceError close_device(DeviceType type, uint32_t device_idx); | ||||
| 
 | ||||
| /* Write data to device */ | ||||
| DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels); | ||||
| DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels, | ||||
|                       uint32_t sample_rate); | ||||
| 
 | ||||
| void print_devices(ToxWindow* self, DeviceType type); | ||||
| void print_devices(ToxWindow *self, DeviceType type); | ||||
| void get_primary_device_name(DeviceType type, char *buf, int size); | ||||
| 
 | ||||
| DeviceError selection_valid(DeviceType type, int32_t selection); | ||||
| #endif /* DEVICE_H */ | ||||
| #endif /* AUDIO_DEVICE_H */ | ||||
| @@ -25,10 +25,10 @@ | ||||
| #include <limits.h> | ||||
|  | ||||
| #ifdef __APPLE__ | ||||
|     #include <sys/types.h> | ||||
|     #include <sys/dir.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/dir.h> | ||||
| #else | ||||
|     #include <dirent.h> | ||||
| #include <dirent.h> | ||||
| #endif /* ifdef __APPLE__ */ | ||||
|  | ||||
| #include "windows.h" | ||||
| @@ -53,12 +53,14 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items | ||||
| } | ||||
|  | ||||
| /* puts match in match buffer. if more than one match, add first n chars that are identical. | ||||
|    e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */ | ||||
| static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n) | ||||
|  * e.g. if matches contains: [foo, foobar, foe] we put fo in match. | ||||
|  * | ||||
|  * Returns the length of the match. | ||||
|  */ | ||||
| static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n) | ||||
| { | ||||
|     if (n == 1) { | ||||
|         strcpy(match, matches[0]); | ||||
|         return; | ||||
|         return snprintf(match, match_sz, "%s", matches[0]); | ||||
|     } | ||||
|  | ||||
|     int i; | ||||
| @@ -71,14 +73,14 @@ static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_ | ||||
|             char ch2 = matches[j][i]; | ||||
|  | ||||
|             if (ch1 != ch2 || !ch1) { | ||||
|                 strcpy(match, matches[0]); | ||||
|                 snprintf(match, match_sz, "%s", matches[0]); | ||||
|                 match[i] = '\0'; | ||||
|                 return; | ||||
|                 return i; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     strcpy(match, matches[0]); | ||||
|     return snprintf(match, match_sz, "%s", matches[0]); | ||||
| } | ||||
|  | ||||
| /* looks for all instances in list that begin with the last entered word in line according to pos, | ||||
| @@ -93,7 +95,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) | ||||
|     if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) | ||||
|         return -1; | ||||
|  | ||||
|     const char *L = (char *) list; | ||||
| @@ -106,7 +108,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|  | ||||
|     /* TODO: generalize this */ | ||||
|     bool dir_search =    !strncmp(ubuf, "/sendfile", strlen("/sendfile")) | ||||
|                       || !strncmp(ubuf, "/avatar", strlen("/avatar")); | ||||
|                          || !strncmp(ubuf, "/avatar", strlen("/avatar")); | ||||
|  | ||||
|     /* isolate substring from space behind pos to pos */ | ||||
|     char tmp[MAX_STR_SIZE]; | ||||
| @@ -114,7 +116,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|     tmp[ctx->pos] = '\0'; | ||||
|  | ||||
|     const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' '); | ||||
|     char *sub = malloc(strlen(ubuf) + 1); | ||||
|     char *sub = calloc(1, strlen(ubuf) + 1); | ||||
|  | ||||
|     if (sub == NULL) | ||||
|         exit_toxic_err("failed in complete_line", FATALERR_MEMORY); | ||||
| @@ -136,20 +138,20 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (string_is_empty(sub)) { | ||||
|     if (!sub[0]) { | ||||
|         free(sub); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int s_len = strlen(sub); | ||||
|     const char *str; | ||||
|     int n_matches = 0; | ||||
|     char matches[n_items][MAX_STR_SIZE]; | ||||
|     int i = 0; | ||||
|  | ||||
|     /* put all list matches in matches array */ | ||||
|     for (i = 0; i < n_items; ++i) { | ||||
|         str = &L[i * size]; | ||||
|         char str[MAX_CMDNAME_SIZE + 1]; | ||||
|         snprintf(str, sizeof(str), "%s", &L[i * size]); | ||||
|  | ||||
|         if (strncasecmp(str, sub, s_len) == 0) | ||||
|             strcpy(matches[n_matches++], str); | ||||
| @@ -164,11 +166,15 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|         print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); | ||||
|  | ||||
|     char match[MAX_STR_SIZE]; | ||||
|     get_str_match(self, match, matches, n_matches); | ||||
|     size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches); | ||||
|  | ||||
|     if (match_len == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (dir_search) { | ||||
|         if (n_matches == 1) | ||||
|             endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/"; | ||||
|             endchrs = char_rfind(match, '.', match_len) ? "\"" : "/"; | ||||
|         else | ||||
|             endchrs = ""; | ||||
|     } else if (n_matches > 1) { | ||||
| @@ -177,24 +183,29 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|  | ||||
|     /* put match in correct spot in buf and append endchars */ | ||||
|     int n_endchrs = strlen(endchrs); | ||||
|     int m_len = strlen(match); | ||||
|     int strt = ctx->pos - s_len; | ||||
|     int diff = m_len - s_len + n_endchrs; | ||||
|     int diff = match_len - s_len + n_endchrs; | ||||
|  | ||||
|     if (ctx->len + diff >= MAX_STR_SIZE) | ||||
|         return -1; | ||||
|  | ||||
|     char tmpend[MAX_STR_SIZE]; | ||||
|     strcpy(tmpend, &ubuf[ctx->pos]); | ||||
|     snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]); | ||||
|  | ||||
|     if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     strcpy(&ubuf[strt], match); | ||||
|     strcpy(&ubuf[strt + m_len], endchrs); | ||||
|     strcpy(&ubuf[strt + m_len + n_endchrs], tmpend); | ||||
|     strcpy(&ubuf[strt + match_len], endchrs); | ||||
|     strcpy(&ubuf[strt + match_len + n_endchrs], tmpend); | ||||
|  | ||||
|     /* convert to widechar and copy back to original buf */ | ||||
|     wchar_t newbuf[MAX_STR_SIZE]; | ||||
|  | ||||
|     if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1) | ||||
|     if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     wcscpy(ctx->line, newbuf); | ||||
|  | ||||
| @@ -209,7 +220,7 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const cha | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     char homedir[MAX_STR_SIZE]; | ||||
|     char homedir[MAX_STR_SIZE] = {0}; | ||||
|     get_home_dir(homedir, sizeof(homedir)); | ||||
|  | ||||
|     char newline[MAX_STR_SIZE]; | ||||
| @@ -218,7 +229,7 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const cha | ||||
|  | ||||
|     wchar_t wline[MAX_STR_SIZE]; | ||||
|  | ||||
|     if (mbs_to_wcs_buf(wline, newline, sizeof(wline)) == -1) | ||||
|     if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) | ||||
|         return; | ||||
|  | ||||
|     int newlen = wcslen(wline); | ||||
| @@ -245,7 +256,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||
|     const wchar_t *tmpline = &line[wcslen(cmd) + 2];   /* start after "/command \"" */ | ||||
|  | ||||
|     if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) | ||||
|         return -1;  | ||||
|         return -1; | ||||
|  | ||||
|     if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) | ||||
|         return -1; | ||||
| @@ -257,14 +268,14 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||
|  | ||||
|     if (!b_path[0]) {    /* list everything in pwd */ | ||||
|         b_path[0] = '.'; | ||||
|         b_path[1] = '\0';  | ||||
|         b_path[1] = '\0'; | ||||
|     } else if (!si && b_path[0] != '/') {    /* look for matches in pwd */ | ||||
|         char tmp[MAX_STR_SIZE]; | ||||
|         snprintf(tmp, sizeof(tmp), ".%s", b_path); | ||||
|         strcpy(b_path, tmp);   | ||||
|         snprintf(b_path, sizeof(b_path), "%s", tmp); | ||||
|     } | ||||
|  | ||||
|     strcpy(b_name, &b_path[si + 1]); | ||||
|     snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); | ||||
|     b_path[si + 1] = '\0'; | ||||
|     int b_name_len = strlen(b_name); | ||||
|     DIR *dp = opendir(b_path); | ||||
| @@ -277,13 +288,15 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||
|     int dircount = 0; | ||||
|  | ||||
|     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { | ||||
|         if (strncmp(entry->d_name, b_name, b_name_len) == 0  | ||||
|                                 && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { | ||||
|         if (strncmp(entry->d_name, b_name, b_name_len) == 0 | ||||
|                 && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { | ||||
|             snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name); | ||||
|             ++dircount; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     closedir(dp); | ||||
|  | ||||
|     if (dircount == 0) | ||||
|         return -1; | ||||
|  | ||||
|   | ||||
							
								
								
									
										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 */ | ||||
							
								
								
									
										1019
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										1019
									
								
								src/chat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -31,15 +31,11 @@ | ||||
| #include "line_info.h" | ||||
| #include "groupchat.h" | ||||
| #include "chat.h" | ||||
| #include "file_senders.h" | ||||
| #include "file_transfers.h" | ||||
|  | ||||
| extern ToxWindow *prompt; | ||||
| extern FriendsList Friends; | ||||
|  | ||||
| extern FileSender file_senders[MAX_FILES]; | ||||
| extern uint8_t max_file_senders_index; | ||||
| extern uint8_t num_active_file_senders; | ||||
|  | ||||
| void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (argc < 2) { | ||||
| @@ -47,51 +43,80 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|     const char *inoutstr = argv[1]; | ||||
|     int filenum = atoi(argv[2]); | ||||
|     long int idx = strtol(argv[2], NULL, 10); | ||||
|  | ||||
|     if (filenum >= MAX_FILES || filenum < 0) { | ||||
|     if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (strcasecmp(inoutstr, "in") == 0) {    /* cancel an incoming file transfer */ | ||||
|         if (!Friends.list[self->num].file_receiver[filenum].active) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|             return;   | ||||
|         } | ||||
|     struct FileTransfer *ft = NULL; | ||||
|  | ||||
|         const char *filepath = Friends.list[self->num].file_receiver[filenum].filename; | ||||
|         char name[MAX_STR_SIZE]; | ||||
|         get_file_name(name, sizeof(name), filepath); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer for '%s' canceled.", name); | ||||
|         chat_close_file_receiver(m, filenum, self->num, TOX_FILECONTROL_KILL); | ||||
|         return; | ||||
|     } else if (strcasecmp(inoutstr, "out") == 0) {    /* cancel an outgoing file transfer */ | ||||
|         int i; | ||||
|         bool match = false; | ||||
|  | ||||
|         for (i = 0; i < MAX_FILES; ++i) { | ||||
|             if (file_senders[i].active && file_senders[i].filenum == filenum) { | ||||
|                 match = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!match) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|             return;   | ||||
|         } | ||||
|  | ||||
|         const char *filename = file_senders[i].filename; | ||||
|         char msg[MAX_STR_SIZE]; | ||||
|         snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", filename); | ||||
|         close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, self->num); | ||||
|         return; | ||||
|     /* cancel an incoming file transfer */ | ||||
|     if (strcasecmp(inoutstr, "in") == 0) { | ||||
|         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||
|     } else if (strcasecmp(inoutstr, "out") == 0) { | ||||
|         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); | ||||
|     } else { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!ft) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name); | ||||
|     close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent); | ||||
| } | ||||
|  | ||||
| void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (Friends.list[self->num].group_invite.length == 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group invite"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *passwd = NULL; | ||||
|     uint16_t passwd_len = 0; | ||||
|  | ||||
|     if (argc > 0) { | ||||
|         passwd = argv[1]; | ||||
|         passwd_len = strlen(passwd); | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_GROUP_INVITE_ACCEPT err; | ||||
|     uint32_t groupnumber = tox_group_invite_accept(m, Friends.list[self->num].group_invite.data, | ||||
|                            Friends.list[self->num].group_invite.length, | ||||
|                            (uint8_t *) passwd, passwd_len, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_GROUP_INVITE_ACCEPT_OK) { | ||||
|         if (err == TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG) | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group: Password too long."); | ||||
|         else | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group (error %d).", err); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (init_groupchat_win(m, groupnumber, NULL, 0) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||
|         tox_group_leave(m, groupnumber, NULL, 0, NULL); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| @@ -103,48 +128,22 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a | ||||
|  | ||||
|     int groupnum = atoi(argv[1]); | ||||
|  | ||||
|  | ||||
|     if (groupnum == 0 && strcmp(argv[1], "0")) {    /* atoi returns 0 value on invalid input */ | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (tox_invite_friend(m, self->num, groupnum) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group."); | ||||
|     TOX_ERR_GROUP_INVITE_FRIEND err; | ||||
|  | ||||
|     if (!tox_group_invite_friend(m, groupnum, self->num, &err)) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum); | ||||
| } | ||||
|  | ||||
| void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *groupkey = Friends.list[self->num].group_invite.key; | ||||
|     uint16_t length = Friends.list[self->num].group_invite.length; | ||||
|  | ||||
|     if (!Friends.list[self->num].group_invite.pending) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length); | ||||
|  | ||||
|     if (groupnum == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (init_groupchat_win(prompt, m, groupnum) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||
|         tox_del_groupchat(m, groupnum); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (argc < 1) { | ||||
| @@ -152,50 +151,77 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     uint8_t filenum = atoi(argv[1]); | ||||
|     long int idx = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) { | ||||
|     if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!Friends.list[self->num].file_receiver[filenum].pending) { | ||||
|     struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||
|  | ||||
|     if (!ft) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *filename = Friends.list[self->num].file_receiver[filenum].filename; | ||||
|  | ||||
|     if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", filenum, filename); | ||||
|  | ||||
|         /* prep progress bar line */ | ||||
|         char progline[MAX_STR_SIZE]; | ||||
|         prep_prog_line(progline); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||
|         Friends.list[self->num].file_receiver[filenum].line_id = self->chatwin->hst->line_end->id + 2; | ||||
|  | ||||
|         if ((Friends.list[self->num].file_receiver[filenum].file = fopen(filename, "a")) == NULL) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "* Error writing to file."); | ||||
|             tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); | ||||
|         } else { | ||||
|             Friends.list[self->num].file_receiver[filenum].active = true; | ||||
|             ++Friends.list[self->num].active_file_receivers; | ||||
|         } | ||||
|     } else { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed."); | ||||
|     if (ft->state != FILE_TRANSFER_PENDING) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Friends.list[self->num].file_receiver[filenum].pending = false; | ||||
|     if ((ft->file = fopen(ft->file_path, "a")) == NULL) { | ||||
|         const char *msg =  "File transfer failed: Invalid file path."; | ||||
|         close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_FILE_CONTROL err; | ||||
|     tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_FILE_CONTROL_OK) | ||||
|         goto on_recv_error; | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path); | ||||
|  | ||||
|     /* prep progress bar line */ | ||||
|     char progline[MAX_STR_SIZE]; | ||||
|     init_progress_bar(progline); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||
|  | ||||
|     ft->line_id = self->chatwin->hst->line_end->id + 2; | ||||
|     ft->state = FILE_TRANSFER_STARTED; | ||||
|  | ||||
|     return; | ||||
|  | ||||
| on_recv_error: | ||||
|  | ||||
|     switch (err) { | ||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); | ||||
|             return; | ||||
|  | ||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online."); | ||||
|             return; | ||||
|  | ||||
|         case TOX_ERR_FILE_CONTROL_NOT_FOUND: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber."); | ||||
|             return; | ||||
|  | ||||
|         case TOX_ERR_FILE_CONTROL_SENDQ: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error."); | ||||
|             return; | ||||
|  | ||||
|         default: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err); | ||||
|             return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (max_file_senders_index >= (MAX_FILES - 1)) { | ||||
|         const char *errmsg = "Please wait for some of your outgoing file transfers to complete."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         return; | ||||
|     } | ||||
|     const char *errmsg = NULL; | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required."); | ||||
| @@ -227,50 +253,65 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | ||||
|  | ||||
|     off_t filesize = file_size(path); | ||||
|  | ||||
|     if (filesize == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File corrupt."); | ||||
|     if (filesize == 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file."); | ||||
|         fclose(file_to_send); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char filename[MAX_STR_SIZE] = {0}; | ||||
|     get_file_name(filename, sizeof(filename), path); | ||||
|     int namelen = strlen(filename); | ||||
|     int filenum = tox_new_file_sender(m, self->num, filesize, (const uint8_t *) filename, namelen); | ||||
|     char file_name[TOX_MAX_FILENAME_LENGTH]; | ||||
|     size_t namelen = get_file_name(file_name, sizeof(file_name), path); | ||||
|  | ||||
|     if (filenum == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error sending file."); | ||||
|         fclose(file_to_send); | ||||
|         return; | ||||
|     TOX_ERR_FILE_SEND err; | ||||
|     uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL, | ||||
|                                      (uint8_t *) file_name, namelen, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_FILE_SEND_OK) | ||||
|         goto on_send_error; | ||||
|  | ||||
|     struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA); | ||||
|  | ||||
|     if (!ft) { | ||||
|         err = TOX_ERR_FILE_SEND_TOO_MANY; | ||||
|         goto on_send_error; | ||||
|     } | ||||
|  | ||||
|     int i; | ||||
|     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); | ||||
|  | ||||
|     for (i = 0; i < MAX_FILES; ++i) { | ||||
|         if (!file_senders[i].active) { | ||||
|             memcpy(file_senders[i].filename, filename, namelen + 1); | ||||
|             file_senders[i].active = true; | ||||
|             file_senders[i].toxwin = self; | ||||
|             file_senders[i].file = file_to_send; | ||||
|             file_senders[i].filenum = filenum; | ||||
|             file_senders[i].friendnum = self->num; | ||||
|             file_senders[i].timestamp = get_unix_time(); | ||||
|             file_senders[i].size = filesize; | ||||
|             file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, | ||||
|                                              tox_file_data_size(m, self->num), file_to_send); | ||||
|     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); | ||||
|  | ||||
|             char sizestr[32]; | ||||
|             bytes_convert_str(sizestr, sizeof(sizestr), filesize); | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  | ||||
|                           "Sending file [%d]: '%s' (%s)", filenum, filename, sizestr); | ||||
|     return; | ||||
|  | ||||
|             ++num_active_file_senders; | ||||
| on_send_error: | ||||
|  | ||||
|             if (i == max_file_senders_index) | ||||
|                 ++max_file_senders_index; | ||||
|     switch (err) { | ||||
|         case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: | ||||
|             errmsg = "File transfer failed: Invalid friend."; | ||||
|             break; | ||||
|  | ||||
|             reset_file_sender_queue(); | ||||
|             return; | ||||
|         } | ||||
|         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); | ||||
| } | ||||
|   | ||||
| @@ -26,9 +26,9 @@ | ||||
| #include "windows.h" | ||||
| #include "toxic.h" | ||||
|  | ||||
| void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| @@ -43,4 +43,9 @@ 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 */ | ||||
|  | ||||
| #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 */ | ||||
|   | ||||
| @@ -31,8 +31,9 @@ | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "configdir.h" | ||||
| #include "misc_tools.h" | ||||
|  | ||||
| /* get the user's home directory */ | ||||
| /* get the user's home directory. */ | ||||
| void get_home_dir(char *home, int size) | ||||
| { | ||||
|     struct passwd pwd; | ||||
| @@ -66,11 +67,11 @@ void get_home_dir(char *home, int size) | ||||
|  */ | ||||
| char *get_user_config_dir(void) | ||||
| { | ||||
|     char home[NSS_BUFLEN_PASSWD]; | ||||
|     char home[NSS_BUFLEN_PASSWD] = {0}; | ||||
|     get_home_dir(home, sizeof(home)); | ||||
|  | ||||
|     char *user_config_dir; | ||||
|     size_t len; | ||||
|     char *user_config_dir = NULL; | ||||
|     size_t len = 0; | ||||
|  | ||||
| # if defined(__APPLE__) | ||||
|     len = strlen(home) + strlen("/Library/Application Support") + 1; | ||||
| @@ -82,9 +83,9 @@ char *get_user_config_dir(void) | ||||
|     snprintf(user_config_dir, len, "%s/Library/Application Support", home); | ||||
| # else /* __APPLE__ */ | ||||
|  | ||||
|     const char *tmp; | ||||
|     const char *tmp = getenv("XDG_CONFIG_HOME"); | ||||
|  | ||||
|     if (!(tmp = getenv("XDG_CONFIG_HOME"))) { | ||||
|     if (tmp == NULL) { | ||||
|         len = strlen(home) + strlen("/.config") + 1; | ||||
|         user_config_dir = malloc(len); | ||||
|  | ||||
| @@ -101,8 +102,10 @@ char *get_user_config_dir(void) | ||||
|     return user_config_dir; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Creates the config and chatlog directories. | ||||
| /* Creates the config and chatlog directories. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int create_user_config_dirs(char *path) | ||||
| { | ||||
|   | ||||
| @@ -34,8 +34,23 @@ | ||||
| #define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR) | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * @brief Get the user's config directory. | ||||
|  * | ||||
|  * This is without a trailing slash. Resulting string must be freed. | ||||
|  * | ||||
|  * @return The users config dir or NULL on error. | ||||
|  */ | ||||
| char *get_user_config_dir(void); | ||||
|  | ||||
| /* get the user's home directory. */ | ||||
| void get_home_dir(char *home, int size); | ||||
|  | ||||
| /* Creates the config and chatlog directories. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int create_user_config_dirs(char *path); | ||||
|  | ||||
| #endif /* #define CONFIGDIR_H */ | ||||
|   | ||||
							
								
								
									
										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 */ | ||||
							
								
								
									
										418
									
								
								src/dns.c
									
									
									
									
									
								
							
							
						
						
									
										418
									
								
								src/dns.c
									
									
									
									
									
								
							| @@ -1,418 +0,0 @@ | ||||
| /*  dns.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/types.h> /* for u_char */ | ||||
| #include <netinet/in.h> | ||||
| #include <resolv.h> | ||||
|  | ||||
| #ifdef __APPLE__ | ||||
|     #include <arpa/nameser_compat.h> | ||||
| #else | ||||
|     #include <arpa/nameser.h> | ||||
| #endif  /* ifdef __APPLE__ */ | ||||
|  | ||||
| #include <tox/toxdns.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "line_info.h" | ||||
| #include "dns.h" | ||||
| #include "global_commands.h" | ||||
| #include "misc_tools.h" | ||||
| #include "configdir.h" | ||||
|  | ||||
| #define DNS3_KEY_SIZE 32 | ||||
| #define MAX_DNS_REQST_SIZE 255 | ||||
| #define TOX_DNS3_TXT_PREFIX "v=tox3;id=" | ||||
|  | ||||
| extern struct Winthread Winthread; | ||||
| extern struct _dns3_servers dns3_servers; | ||||
| extern struct arg_opts arg_opts; | ||||
|  | ||||
| #define NUM_DNS3_BACKUP_SERVERS 2 | ||||
|  | ||||
| /* Hardcoded backup in case domain list is not loaded */ | ||||
| static struct dns3_server_backup { | ||||
|     const char *name; | ||||
|     char key[DNS3_KEY_SIZE]; | ||||
| } dns3_servers_backup[] = { | ||||
|     { | ||||
|         "utox.org", | ||||
|         { | ||||
|           0xD3, 0x15, 0x4F, 0x65, 0xD2, 0x8A, 0x5B, 0x41, 0xA0, 0x5D, 0x4A, 0xC7, 0xE4, 0xB3, 0x9C, 0x6B, | ||||
|           0x1C, 0x23, 0x3C, 0xC8, 0x57, 0xFB, 0x36, 0x5C, 0x56, 0xE8, 0x39, 0x27, 0x37, 0x46, 0x2A, 0x12 | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         "toxme.se", | ||||
|         { | ||||
|           0x5D, 0x72, 0xC5, 0x17, 0xDF, 0x6A, 0xEC, 0x54, 0xF1, 0xE9, 0x77, 0xA6, 0xB6, 0xF2, 0x59, 0x14, | ||||
|           0xEA, 0x4C, 0xF7, 0x27, 0x7A, 0x85, 0x02, 0x7C, 0xD9, 0xF5, 0x19, 0x6D, 0xF1, 0x7E, 0x0B, 0x13 | ||||
|         } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static struct _thread_data { | ||||
|     ToxWindow *self; | ||||
|     char id_bin[TOX_FRIEND_ADDRESS_SIZE]; | ||||
|     char addr[MAX_STR_SIZE]; | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|     uint8_t busy; | ||||
|     Tox *m; | ||||
| } t_data; | ||||
|  | ||||
| static struct _dns_thread { | ||||
|     pthread_t tid; | ||||
|     pthread_attr_t attr; | ||||
| } dns_thread; | ||||
|  | ||||
|  | ||||
| #define MAX_DNS_SERVERS 50 | ||||
| #define MAX_DOMAIN_SIZE 32 | ||||
| #define MAX_DNS_LINE MAX_DOMAIN_SIZE + (DNS3_KEY_SIZE * 2) + 3 | ||||
|  | ||||
| struct _dns3_servers { | ||||
|     bool loaded; | ||||
|     int lines; | ||||
|     char names[MAX_DNS_SERVERS][MAX_DOMAIN_SIZE]; | ||||
|     char keys[MAX_DNS_SERVERS][DNS3_KEY_SIZE]; | ||||
| } dns3_servers; | ||||
|  | ||||
| static int load_dns_domainlist(const char *path) | ||||
| { | ||||
|     FILE *fp = fopen(path, "r"); | ||||
|  | ||||
|     if (fp == NULL) | ||||
|         return -1; | ||||
|  | ||||
|     char line[MAX_DNS_LINE]; | ||||
|  | ||||
|     while (fgets(line, sizeof(line), fp) && dns3_servers.lines < MAX_DNS_SERVERS) { | ||||
|         int linelen = strlen(line); | ||||
|  | ||||
|         if (linelen < DNS3_KEY_SIZE * 2 + 5) | ||||
|             continue; | ||||
|  | ||||
|         if (line[linelen - 1] == '\n') | ||||
|             line[--linelen] = '\0'; | ||||
|  | ||||
|         const char *name = strtok(line, " "); | ||||
|         const char *keystr = strtok(NULL, " "); | ||||
|  | ||||
|         if (name == NULL || keystr == NULL) | ||||
|             continue; | ||||
|  | ||||
|         if (strlen(keystr) != DNS3_KEY_SIZE * 2) | ||||
|             continue; | ||||
|  | ||||
|         snprintf(dns3_servers.names[dns3_servers.lines], sizeof(dns3_servers.names[dns3_servers.lines]), "%s", name); | ||||
|         int res = hex_string_to_bytes(dns3_servers.keys[dns3_servers.lines], DNS3_KEY_SIZE, keystr); | ||||
|  | ||||
|         if (res == -1) | ||||
|             continue; | ||||
|  | ||||
|         ++dns3_servers.lines; | ||||
|     } | ||||
|  | ||||
|     fclose(fp); | ||||
|  | ||||
|     if (dns3_servers.lines < 1) | ||||
|         return -2; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int dns_error(ToxWindow *self, const char *errmsg) | ||||
| { | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "User lookup failed: %s", errmsg); | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void kill_dns_thread(void *dns_obj) | ||||
| { | ||||
|     if (dns_obj) | ||||
|         tox_dns3_kill(dns_obj); | ||||
|  | ||||
|     memset(&t_data, 0, sizeof(struct _thread_data)); | ||||
|     pthread_attr_destroy(&dns_thread.attr); | ||||
|     pthread_exit(NULL); | ||||
| } | ||||
|  | ||||
| /* puts TXT from dns response in buf. Returns length of TXT on success, -1 on fail.*/ | ||||
| static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char *buf) | ||||
| { | ||||
|     uint8_t *ans_pt = answer + sizeof(HEADER); | ||||
|     uint8_t *ans_end = answer + ans_len; | ||||
|     char exp_ans[PACKETSZ]; | ||||
|      | ||||
|     int len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans)); | ||||
|  | ||||
|     if (len == -1) | ||||
|         return dns_error(self, "dn_expand failed.");  | ||||
|  | ||||
|     ans_pt += len; | ||||
|  | ||||
|     if (ans_pt > ans_end - 4) | ||||
|          return dns_error(self, "DNS reply was too short.");  | ||||
|  | ||||
|     int type; | ||||
|     GETSHORT(type, ans_pt); | ||||
|  | ||||
|     if (type != T_TXT) | ||||
|         return dns_error(self, "Broken DNS reply.");  | ||||
|   | ||||
|  | ||||
|     ans_pt += INT16SZ;    /* class */ | ||||
|     uint32_t size = 0; | ||||
|  | ||||
|     /* recurse through CNAME rr's */ | ||||
|     do {  | ||||
|         ans_pt += size; | ||||
|         len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans)); | ||||
|  | ||||
|         if (len == -1) | ||||
|             return dns_error(self, "Second dn_expand failed.");  | ||||
|  | ||||
|         ans_pt += len; | ||||
|  | ||||
|         if (ans_pt > ans_end - 10) | ||||
|             return dns_error(self, "DNS reply was too short.");  | ||||
|  | ||||
|         GETSHORT(type, ans_pt); | ||||
|         ans_pt += INT16SZ; | ||||
|         ans_pt += 4; | ||||
|         GETSHORT(size, ans_pt); | ||||
|  | ||||
|         if (ans_pt + size < answer || ans_pt + size > ans_end) | ||||
|             return dns_error(self, "RR overflow.");  | ||||
|  | ||||
|     } while (type == T_CNAME); | ||||
|  | ||||
|     if (type != T_TXT) | ||||
|         return dns_error(self, "DNS response failed.");  | ||||
|  | ||||
|     uint32_t txt_len = *ans_pt; | ||||
|  | ||||
|     if (!size || txt_len >= size || !txt_len) | ||||
|         return dns_error(self, "No record found."); | ||||
|  | ||||
|     if (txt_len > MAX_DNS_REQST_SIZE) | ||||
|         return dns_error(self, "Invalid DNS response."); | ||||
|  | ||||
|     ans_pt++; | ||||
|     ans_pt[txt_len] = '\0'; | ||||
|     memcpy(buf, ans_pt, txt_len + 1); | ||||
|  | ||||
|     return txt_len; | ||||
| } | ||||
|  | ||||
| /* Takes address addr in the form "username@domain", puts the username in namebuf,  | ||||
|    and the domain in dombuf. | ||||
|  | ||||
|    return length of username on success, -1 on failure */ | ||||
| static int parse_addr(const char *addr, char *namebuf, char *dombuf) | ||||
| { | ||||
|     char tmpaddr[MAX_STR_SIZE]; | ||||
|     char *tmpname, *tmpdom; | ||||
|  | ||||
|     strcpy(tmpaddr, addr); | ||||
|     tmpname = strtok(tmpaddr, "@"); | ||||
|     tmpdom = strtok(NULL, ""); | ||||
|  | ||||
|     if (tmpname == NULL || tmpdom == NULL) | ||||
|         return -1; | ||||
|  | ||||
|     str_to_lower(tmpdom); | ||||
|     strcpy(namebuf, tmpname); | ||||
|     strcpy(dombuf, tmpdom); | ||||
|  | ||||
|     return strlen(namebuf); | ||||
| } | ||||
|  | ||||
| /* matches input domain name with domains in list and obtains key. Return 0 on success, -1 on failure */ | ||||
| static int get_domain_match(char *pubkey, char *domain, const char *inputdomain) | ||||
| { | ||||
|     /* check server list first */ | ||||
|     int i; | ||||
|     bool match = false; | ||||
|  | ||||
|     for (i = 0; i < dns3_servers.lines; ++i) { | ||||
|         if (strcmp(dns3_servers.names[i], inputdomain) == 0) { | ||||
|             memcpy(pubkey, dns3_servers.keys[i], DNS3_KEY_SIZE); | ||||
|             snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers.names[i]); | ||||
|             match = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* fall back to hard-coded domains on server list failure */ | ||||
|     if (!match) { | ||||
|         for (i = 0; i < NUM_DNS3_BACKUP_SERVERS; ++i) { | ||||
|             if (strcmp(dns3_servers_backup[i].name, inputdomain) == 0) { | ||||
|                 memcpy(pubkey, dns3_servers_backup[i].key, DNS3_KEY_SIZE); | ||||
|                 snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers_backup[i].name); | ||||
|                 match = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!match) | ||||
|             return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Does DNS lookup for addr and puts resulting tox id in id_bin. */ | ||||
| void *dns3_lookup_thread(void *data) | ||||
| { | ||||
|     ToxWindow *self = t_data.self; | ||||
|  | ||||
|     char inputdomain[MAX_STR_SIZE]; | ||||
|     char name[MAX_STR_SIZE]; | ||||
|  | ||||
|     int namelen = parse_addr(t_data.addr, name, inputdomain); | ||||
|  | ||||
|     if (namelen == -1) { | ||||
|         dns_error(self, "Must be a Tox ID or an address in the form username@domain"); | ||||
|         kill_dns_thread(NULL); | ||||
|     } | ||||
|  | ||||
|     char DNS_pubkey[DNS3_KEY_SIZE]; | ||||
|     char domain[MAX_DOMAIN_SIZE]; | ||||
|  | ||||
|     int match = get_domain_match(DNS_pubkey, domain, inputdomain); | ||||
|  | ||||
|     if (match == -1) { | ||||
|         dns_error(self, "Domain not found."); | ||||
|         kill_dns_thread(NULL); | ||||
|     } | ||||
|  | ||||
|     void *dns_obj = tox_dns3_new((uint8_t *) DNS_pubkey); | ||||
|  | ||||
|     if (dns_obj == NULL) { | ||||
|         dns_error(self, "Core failed to create DNS object."); | ||||
|         kill_dns_thread(NULL); | ||||
|     } | ||||
|  | ||||
|     char string[MAX_DNS_REQST_SIZE + 1]; | ||||
|     uint32_t request_id; | ||||
|  | ||||
|     int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,  | ||||
|                                            (uint8_t *) name, namelen); | ||||
|  | ||||
|     if (str_len == -1) { | ||||
|         dns_error(self, "Core failed to generate DNS3 string."); | ||||
|         kill_dns_thread(dns_obj); | ||||
|     } | ||||
|  | ||||
|     string[str_len] = '\0'; | ||||
|  | ||||
|     u_char answer[PACKETSZ]; | ||||
|     char d_string[MAX_DOMAIN_SIZE + MAX_DNS_REQST_SIZE + 10]; | ||||
|  | ||||
|     /* format string and create dns query */ | ||||
|     snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domain); | ||||
|     int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer)); | ||||
|  | ||||
|     if (ans_len <= 0) { | ||||
|         dns_error(self, "DNS query failed."); | ||||
|         kill_dns_thread(dns_obj); | ||||
|     } | ||||
|  | ||||
|     char ans_id[MAX_DNS_REQST_SIZE + 1]; | ||||
|  | ||||
|     /* extract TXT from DNS response */ | ||||
|     if (parse_dns_response(self, answer, ans_len, ans_id) == -1) | ||||
|         kill_dns_thread(dns_obj); | ||||
|  | ||||
|     char encrypted_id[MAX_DNS_REQST_SIZE + 1]; | ||||
|     int prfx_len = strlen(TOX_DNS3_TXT_PREFIX); | ||||
|  | ||||
|     /* extract the encrypted ID from TXT response */ | ||||
|     if (strncmp(ans_id, TOX_DNS3_TXT_PREFIX, prfx_len) != 0) { | ||||
|         dns_error(self, "Bad DNS3 TXT response."); | ||||
|         kill_dns_thread(dns_obj); | ||||
|     } | ||||
|  | ||||
|     memcpy(encrypted_id, ans_id + prfx_len, ans_len - prfx_len); | ||||
|  | ||||
|     if (tox_decrypt_dns3_TXT(dns_obj, (uint8_t *) t_data.id_bin, (uint8_t *) encrypted_id,  | ||||
|                              strlen(encrypted_id), request_id) == -1) { | ||||
|         dns_error(self, "Core failed to decrypt DNS response."); | ||||
|         kill_dns_thread(dns_obj); | ||||
|     } | ||||
|  | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg); | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     kill_dns_thread(dns_obj); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* creates new thread for dns3 lookup. Only allows one lookup at a time. */ | ||||
| void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg) | ||||
| { | ||||
|     if (arg_opts.use_proxy && arg_opts.force_tcp) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "DNS lookups are disabled."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (t_data.busy) { | ||||
|         const char *err = "Please wait for previous user lookup to finish."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, err); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!dns3_servers.loaded) { | ||||
|         const char *path = arg_opts.dns_path[0] ? arg_opts.dns_path : PACKAGE_DATADIR "/DNSservers"; | ||||
|         dns3_servers.loaded = true; | ||||
|         int ret = load_dns_domainlist(path); | ||||
|  | ||||
|         if (ret < 0) { | ||||
|             const char *errmsg = "DNS server list failed to load with error code %d. Falling back to hard-coded list."; | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg, ret); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin); | ||||
|     snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr); | ||||
|     snprintf(t_data.msg, sizeof(t_data.msg), "%s", msg); | ||||
|     t_data.self = self; | ||||
|     t_data.m = m; | ||||
|     t_data.busy = 1; | ||||
|  | ||||
|     if (pthread_attr_init(&dns_thread.attr) != 0) | ||||
|         exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR); | ||||
|  | ||||
|     if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0) | ||||
|         exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR); | ||||
|  | ||||
|     if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0) | ||||
|         exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_CREATE); | ||||
| } | ||||
							
								
								
									
										127
									
								
								src/execute.c
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								src/execute.c
									
									
									
									
									
								
							| @@ -29,10 +29,13 @@ | ||||
| #include "execute.h" | ||||
| #include "chat_commands.h" | ||||
| #include "global_commands.h" | ||||
| #include "group_commands.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
|  | ||||
| #define MAX_NUM_ARGS 10     /* Includes command */ | ||||
|  | ||||
| struct cmd_func { | ||||
|     const char *name; | ||||
|     void (*func)(WINDOW *w, ToxWindow *, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| @@ -46,12 +49,15 @@ static struct cmd_func global_commands[] = { | ||||
|     { "/connect",   cmd_connect       }, | ||||
|     { "/decline",   cmd_decline       }, | ||||
|     { "/exit",      cmd_quit          }, | ||||
|     { "/groupchat", cmd_groupchat     }, | ||||
|     { "/group",     cmd_groupchat     }, | ||||
|     { "/help",      cmd_prompt_help   }, | ||||
|     { "/join",      cmd_join          }, | ||||
|     { "/log",       cmd_log           }, | ||||
|     { "/myid",      cmd_myid          }, | ||||
|     { "/myqr",      cmd_myqr          }, | ||||
|     { "/nick",      cmd_nick          }, | ||||
|     { "/note",      cmd_note          }, | ||||
|     { "/nospam",    cmd_nospam        }, | ||||
|     { "/q",         cmd_quit          }, | ||||
|     { "/quit",      cmd_quit          }, | ||||
|     { "/requests",  cmd_requests      }, | ||||
| @@ -60,13 +66,17 @@ static struct cmd_func global_commands[] = { | ||||
|     { "/lsdev",     cmd_list_devices  }, | ||||
|     { "/sdev",      cmd_change_device }, | ||||
| #endif /* AUDIO */ | ||||
| #ifdef VIDEO | ||||
|     { "/lsvdev",    cmd_list_video_devices }, | ||||
|     { "/svdev" ,    cmd_change_video_device }, | ||||
| #endif /* VIDEO */ | ||||
|     { NULL,         NULL              }, | ||||
| }; | ||||
|  | ||||
| static struct cmd_func chat_commands[] = { | ||||
|     { "/cancel",    cmd_cancelfile  }, | ||||
|     { "/gaccept",   cmd_groupaccept }, | ||||
|     { "/invite",    cmd_groupinvite }, | ||||
|     { "/join",      cmd_join_group  }, | ||||
|     { "/savefile",  cmd_savefile    }, | ||||
|     { "/sendfile",  cmd_sendfile    }, | ||||
| #ifdef AUDIO | ||||
| @@ -74,17 +84,109 @@ static struct cmd_func chat_commands[] = { | ||||
|     { "/answer",    cmd_answer      }, | ||||
|     { "/reject",    cmd_reject      }, | ||||
|     { "/hangup",    cmd_hangup      }, | ||||
|     { "/sdev",      cmd_ccur_device }, | ||||
|     { "/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. | ||||
|    Returns number of arguments on success, -1 on failure. */ | ||||
| static struct cmd_func group_commands[] = { | ||||
|     { "/ban",       cmd_ban            }, | ||||
|     { "/chatid",    cmd_chatid         }, | ||||
|     { "/ignore",    cmd_ignore         }, | ||||
|     { "/kick",      cmd_kick           }, | ||||
|     { "/mod",       cmd_mod            }, | ||||
|     { "/mykey",     cmd_mykey          }, | ||||
|     { "/passwd",    cmd_set_passwd     }, | ||||
|     { "/peerlimit", cmd_set_peerlimit  }, | ||||
|     { "/privacy",   cmd_set_privacy    }, | ||||
|     { "/rejoin",    cmd_rejoin         }, | ||||
|     { "/silence",   cmd_silence        }, | ||||
|     { "/topic",     cmd_set_topic      }, | ||||
|     { "/unban",     cmd_unban          }, | ||||
|     { "/unignore",  cmd_unignore       }, | ||||
|     { "/unmod",     cmd_unmod          }, | ||||
|     { "/unsilence", cmd_unsilence      }, | ||||
|     { "/whois",     cmd_whois          }, | ||||
| #ifdef AUDIO | ||||
|     { "/mute",      cmd_mute           }, | ||||
|     { "/sense",     cmd_sense          }, | ||||
| #endif /* AUDIO */ | ||||
|     { NULL,         NULL               }, | ||||
| }; | ||||
|  | ||||
| #define NUM_SPECIAL_COMMANDS 15 | ||||
| static const char special_commands[NUM_SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = { | ||||
|     "/ban", | ||||
|     "/gaccept", | ||||
|     "/group", | ||||
|     "/ignore", | ||||
|     "/kick", | ||||
|     "/mod", | ||||
|     "/nick", | ||||
|     "/note", | ||||
|     "/passwd", | ||||
|     "/silence", | ||||
|     "/topic", | ||||
|     "/unignore", | ||||
|     "/unmod", | ||||
|     "/unsilence", | ||||
|     "/whois", | ||||
| }; | ||||
|  | ||||
| /* return true if input command is in the special_commands array. False otherwise.*/ | ||||
| static bool is_special_command(const char *input) | ||||
| { | ||||
|     int s = char_find(0, input, ' '); | ||||
|  | ||||
|     if (s == strlen(input)) | ||||
|         return false; | ||||
|  | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < NUM_SPECIAL_COMMANDS; ++i) { | ||||
|         if (strncmp(input, special_commands[i], s) == 0) | ||||
|             return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| /* Parses commands in the special_commands array which take exactly one argument that may contain spaces. | ||||
|  * Unlike parse_command, this function does not split the input string at spaces. | ||||
|  * | ||||
|  * Returns number of arguments on success | ||||
|  * Returns -1 on failure | ||||
|  */ | ||||
| static int parse_special_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     int len = strlen(input); | ||||
|     int s = char_find(0, input, ' '); | ||||
|  | ||||
|     if (s + 1 >= len) | ||||
|         return -1; | ||||
|  | ||||
|     memcpy(args[0], input, s); | ||||
|     args[0][s++] = '\0';    /* increment to remove space after /command */ | ||||
|     memcpy(args[1], input + s, len - s); | ||||
|     args[1][len - s] = '\0'; | ||||
|  | ||||
|     return 2; | ||||
| } | ||||
|  | ||||
| /* Parses input command and puts args (split by spaces) into args array. | ||||
|  * | ||||
|  * Returns number of arguments on success | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (is_special_command(input)) | ||||
|         return parse_special_command(w, self, input, args); | ||||
|  | ||||
|     char *cmd = strdup(input); | ||||
|  | ||||
|     if (cmd == NULL) | ||||
| @@ -122,11 +224,19 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a | ||||
|         strcpy(cmd, tmp);    /* tmp will always fit inside cmd */ | ||||
|     } | ||||
|  | ||||
|     /* Ugly special case concatinates all args after arg1 for multi-word group passwords */ | ||||
|     if (num_args > 2 && strcmp(args[0], "/join") == 0) | ||||
|         strcpy(args[2], input + strlen(args[0]) + 1 + strlen(args[1]) + 1); | ||||
|  | ||||
|     free(cmd); | ||||
|     return num_args; | ||||
| } | ||||
|  | ||||
| /* Matches command to respective function. Returns 0 on match, 1 on no match */ | ||||
| /* Matches command to respective function. | ||||
|  * | ||||
|  * Returns 0 on match, | ||||
|  * Returns -1 on no match | ||||
|  */ | ||||
| static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, | ||||
|                       char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
| @@ -139,7 +249,7 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 1; | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | ||||
| @@ -165,6 +275,9 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | ||||
|             break; | ||||
|  | ||||
|         case GROUPCHAT_COMMAND_MODE: | ||||
|             if (do_command(w, self, m, num_args, group_commands, args) == 0) | ||||
|                 return; | ||||
|  | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -26,13 +26,11 @@ | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define MAX_NUM_ARGS 4     /* Includes command */ | ||||
|  | ||||
| enum { | ||||
|     GLOBAL_COMMAND_MODE, | ||||
|     CHAT_COMMAND_MODE, | ||||
|     GROUPCHAT_COMMAND_MODE, | ||||
| }; | ||||
| } COMMAND_MODE; | ||||
|  | ||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | ||||
|  | ||||
|   | ||||
| @@ -1,291 +0,0 @@ | ||||
| /*  file_senders.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "friendlist.h" | ||||
| #include "file_senders.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
|  | ||||
| FileSender file_senders[MAX_FILES]; | ||||
| uint8_t max_file_senders_index; | ||||
| uint8_t num_active_file_senders; | ||||
| extern FriendsList Friends; | ||||
|  | ||||
| /* creates initial progress line that will be updated during file transfer. | ||||
|    Assumes progline is of size MAX_STR_SIZE */ | ||||
| void prep_prog_line(char *progline) | ||||
| { | ||||
|     strcpy(progline, "0.0 B/s ["); | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < NUM_PROG_MARKS; ++i) | ||||
|         strcat(progline, "-"); | ||||
|  | ||||
|     strcat(progline, "] 0%"); | ||||
| } | ||||
|  | ||||
| /* prints a progress bar for file transfers.  | ||||
|    if friendnum is -1 we're sending the file, otherwise we're receiving.  */ | ||||
| void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_done) | ||||
| { | ||||
|     double bps; | ||||
|     uint32_t line_id; | ||||
|  | ||||
|     if (friendnum < 0) { | ||||
|         bps = file_senders[idx].bps; | ||||
|         line_id = file_senders[idx].line_id; | ||||
|     } else { | ||||
|         bps = Friends.list[friendnum].file_receiver[idx].bps; | ||||
|         line_id = Friends.list[friendnum].file_receiver[idx].line_id; | ||||
|     } | ||||
|  | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|     bytes_convert_str(msg, sizeof(msg), bps); | ||||
|     strcat(msg, "/s ["); | ||||
|  | ||||
|     int n = pct_done / (100 / NUM_PROG_MARKS); | ||||
|     int i, j; | ||||
|  | ||||
|     for (i = 0; i < n; ++i) | ||||
|         strcat(msg, "#"); | ||||
|  | ||||
|     for (j = i; j < NUM_PROG_MARKS; ++j) | ||||
|         strcat(msg, "-"); | ||||
|  | ||||
|     strcat(msg, "] "); | ||||
|  | ||||
|     char pctstr[16]; | ||||
|     const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%"; | ||||
|     snprintf(pctstr, sizeof(pctstr), frmt, pct_done); | ||||
|     strcat(msg, pctstr); | ||||
|  | ||||
|     line_info_set(self, line_id, msg); | ||||
| } | ||||
|  | ||||
| /* refreshes active file receiver status bars */ | ||||
| static void refresh_recv_prog(Tox *m) | ||||
| { | ||||
|     int i; | ||||
|     uint64_t curtime = get_unix_time(); | ||||
|  | ||||
|     for (i = 2; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         ToxWindow *toxwin = get_window_ptr(i); | ||||
|  | ||||
|         if (toxwin == NULL || !toxwin->is_chat) | ||||
|             continue; | ||||
|  | ||||
|         int fnum = toxwin->num; | ||||
|         int j; | ||||
|  | ||||
|         for (j = 0; j < MAX_FILES; ++j) { | ||||
|             if (!Friends.list[fnum].file_receiver[j].active) | ||||
|                 continue; | ||||
|  | ||||
|             int filenum = Friends.list[fnum].file_receiver[j].filenum; | ||||
|             double remain = (double) tox_file_data_remaining(m, fnum, filenum, 1); | ||||
|  | ||||
|             /* must be called once per second */ | ||||
|             if (timed_out(Friends.list[fnum].file_receiver[filenum].last_progress, curtime, 1)) { | ||||
|                 Friends.list[fnum].file_receiver[filenum].last_progress = curtime; | ||||
|                 uint64_t size = Friends.list[fnum].file_receiver[filenum].size; | ||||
|                 double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100; | ||||
|                 print_progress_bar(toxwin, filenum, fnum, pct_done); | ||||
|                 Friends.list[fnum].file_receiver[filenum].bps = 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* refreshes active file sender status bars */ | ||||
| static void refresh_sender_prog(Tox *m) | ||||
| { | ||||
|     int i; | ||||
|     uint64_t curtime = get_unix_time(); | ||||
|  | ||||
|     for (i = 0; i < max_file_senders_index; ++i) { | ||||
|         if (!file_senders[i].active || file_senders[i].finished) | ||||
|             continue; | ||||
|  | ||||
|         int filenum = file_senders[i].filenum; | ||||
|         int32_t friendnum = file_senders[i].friendnum; | ||||
|         double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0); | ||||
|  | ||||
|         /* must be called once per second */ | ||||
|         if (timed_out(file_senders[i].last_progress, curtime, 1)) { | ||||
|             file_senders[i].last_progress = curtime; | ||||
|             double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100; | ||||
|             print_progress_bar(file_senders[i].toxwin, i, -1, pct_done); | ||||
|             file_senders[i].bps = 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void set_max_file_senders_index(void) | ||||
| { | ||||
|     int j; | ||||
|  | ||||
|     for (j = max_file_senders_index; j > 0; --j) { | ||||
|         if (file_senders[j - 1].active) | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     max_file_senders_index = j; | ||||
| } | ||||
|  | ||||
| /* called whenever a file sender is opened or closed */ | ||||
| void reset_file_sender_queue(void) | ||||
| { | ||||
|     int i; | ||||
|     int pos = 0; | ||||
|  | ||||
|     for (i = 0; i < max_file_senders_index; ++i) { | ||||
|         if (file_senders[i].active) | ||||
|             file_senders[i].queue_pos = pos++; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* set CTRL to -1 if we don't want to send a control signal. | ||||
|    set msg to NULL if we don't want to display a message */ | ||||
| void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum) | ||||
| { | ||||
|     if (msg != NULL)  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg); | ||||
|      | ||||
|     if (CTRL > 0) | ||||
|         tox_file_send_control(m, friendnum, 0, filenum, CTRL, 0, 0); | ||||
|  | ||||
|     fclose(file_senders[i].file); | ||||
|     memset(&file_senders[i], 0, sizeof(FileSender)); | ||||
|     set_max_file_senders_index(); | ||||
|     reset_file_sender_queue(); | ||||
|     --num_active_file_senders; | ||||
| } | ||||
|  | ||||
| void close_all_file_senders(Tox *m) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < max_file_senders_index; ++i) { | ||||
|         if (file_senders[i].active) { | ||||
|             fclose(file_senders[i].file); | ||||
|             tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenum, | ||||
|                                   TOX_FILECONTROL_KILL, 0, 0); | ||||
|             memset(&file_senders[i], 0, sizeof(FileSender)); | ||||
|         } | ||||
|  | ||||
|         set_max_file_senders_index(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void send_file_data(ToxWindow *self, Tox *m, int i, int32_t friendnum, int filenum, const char *filename) | ||||
| { | ||||
|     FILE *fp = file_senders[i].file; | ||||
|  | ||||
|     while (true) { | ||||
|         if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece, | ||||
|                                file_senders[i].piecelen) == -1) | ||||
|             return; | ||||
|  | ||||
|         file_senders[i].timestamp = get_unix_time(); | ||||
|         file_senders[i].bps += file_senders[i].piecelen;             | ||||
|         file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, | ||||
|                                          tox_file_data_size(m, friendnum), fp); | ||||
|  | ||||
|         /* note: file sender is closed in chat_onFileControl callback after receiving reply */ | ||||
|         if (file_senders[i].piecelen == 0) { | ||||
|             if (feof(fp) != 0) {   /* make sure we're really at eof */ | ||||
|                 print_progress_bar(self, i, -1, 100.0); | ||||
|                 tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0); | ||||
|                 file_senders[i].finished = true; | ||||
|             } else { | ||||
|                 char msg[MAX_STR_SIZE]; | ||||
|                 snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read error.", file_senders[i].filename); | ||||
|                 close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum); | ||||
|                 sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL); | ||||
|  | ||||
|                 if (self->active_box != -1) | ||||
|                     box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg); | ||||
|                 else | ||||
|                     box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg); | ||||
|             } | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void do_file_senders(Tox *m) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < max_file_senders_index; ++i) { | ||||
|         if (!file_senders[i].active) | ||||
|             continue; | ||||
|  | ||||
|         if (file_senders[i].queue_pos > 0) { | ||||
|             --file_senders[i].queue_pos; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         ToxWindow *self = file_senders[i].toxwin; | ||||
|         char *filename = file_senders[i].filename; | ||||
|         int filenum = file_senders[i].filenum; | ||||
|         int32_t friendnum = file_senders[i].friendnum; | ||||
|  | ||||
|         /* kill file transfer if chatwindow is closed */ | ||||
|         if (self->chatwin == NULL) { | ||||
|             close_file_sender(self, m, i, NULL, TOX_FILECONTROL_KILL, filenum, friendnum); | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         /* If file transfer has timed out kill transfer and send kill control */ | ||||
|         if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)) { | ||||
|             char msg[MAX_STR_SIZE]; | ||||
|             snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", filename); | ||||
|             close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum); | ||||
|             sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL); | ||||
|              | ||||
|             if (self->active_box != -1) | ||||
|                 box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg); | ||||
|             else | ||||
|                 box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg); | ||||
|  | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (!file_senders[i].noconnection && !file_senders[i].finished) | ||||
|             send_file_data(self, m, i, friendnum, filenum, filename); | ||||
|  | ||||
|         file_senders[i].queue_pos = num_active_file_senders - 1; | ||||
|     } | ||||
|  | ||||
|     refresh_sender_prog(m); | ||||
|     refresh_recv_prog(m); | ||||
| } | ||||
| @@ -1,75 +0,0 @@ | ||||
| /*  file_senders.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef FILESENDERS_H | ||||
| #define FILESENDERS_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define KiB 1024 | ||||
| #define MiB 1048576       /* 1024 ^ 2 */ | ||||
| #define GiB 1073741824    /* 1024 ^ 3 */ | ||||
|  | ||||
| #define FILE_PIECE_SIZE 2048    /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */ | ||||
| #define MAX_FILES 32 | ||||
| #define TIMEOUT_FILESENDER 120 | ||||
| #define NUM_PROG_MARKS 50    /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | ||||
|  | ||||
| typedef struct { | ||||
|     FILE *file; | ||||
|     ToxWindow *toxwin; | ||||
|     int32_t friendnum; | ||||
|     bool active; | ||||
|     bool noconnection; | ||||
|     bool finished; | ||||
|     int filenum; | ||||
|     char nextpiece[FILE_PIECE_SIZE]; | ||||
|     uint16_t piecelen; | ||||
|     char filename[MAX_STR_SIZE]; | ||||
|     uint64_t timestamp; | ||||
|     uint64_t last_progress; | ||||
|     double bps; | ||||
|     uint64_t size; | ||||
|     uint32_t line_id; | ||||
|     uint8_t queue_pos; | ||||
| } FileSender; | ||||
|  | ||||
| /* creates initial progress line that will be updated during file transfer. | ||||
|    Assumes progline is of size MAX_STR_SIZE */ | ||||
| void prep_prog_line(char *progline); | ||||
|  | ||||
| /* prints a progress bar for file transfers.  | ||||
|    if friendnum is -1 we're sending the file, otherwise we're receiving.  */ | ||||
| void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_remain); | ||||
|  | ||||
| /* set CTRL to -1 if we don't want to send a control signal. | ||||
|    set msg to NULL if we don't want to display a message */ | ||||
| void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum); | ||||
|  | ||||
| /* called whenever a file sender is opened or closed */ | ||||
| void reset_file_sender_queue(void); | ||||
|  | ||||
| void close_all_file_senders(Tox *m); | ||||
| void do_file_senders(Tox *m); | ||||
|  | ||||
| #endif  /* #define FILESENDERS_H */ | ||||
							
								
								
									
										277
									
								
								src/file_transfers.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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 */ | ||||
							
								
								
									
										566
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										566
									
								
								src/friendlist.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -27,20 +27,7 @@ | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "file_senders.h" | ||||
|  | ||||
| struct FileReceiver { | ||||
|     char filename[MAX_STR_SIZE]; | ||||
|     int filenum; | ||||
|     FILE *file; | ||||
|     bool pending; | ||||
|     bool active; | ||||
|     uint64_t size; | ||||
|     uint64_t bytes_recv; | ||||
|     double bps; | ||||
|     uint64_t last_progress;   /* unix-time when we last updated progress */ | ||||
|     uint32_t line_id; | ||||
| }; | ||||
| #include "file_transfers.h" | ||||
|  | ||||
| struct LastOnline { | ||||
|     uint64_t last_on; | ||||
| @@ -48,54 +35,56 @@ struct LastOnline { | ||||
|     char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */ | ||||
| }; | ||||
|  | ||||
| struct GroupChatInvite { | ||||
|     char *key; | ||||
| struct GroupInvite { | ||||
|     uint8_t *data; | ||||
|     uint16_t length; | ||||
|     bool pending; | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     char name[TOXIC_MAX_NAME_LENGTH]; | ||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|     int namelength; | ||||
|     char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH]; | ||||
|     uint16_t statusmsg_len; | ||||
|     char pub_key[TOX_CLIENT_ID_SIZE]; | ||||
|     int32_t num; | ||||
|     char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; | ||||
|     size_t statusmsg_len; | ||||
|     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint32_t num; | ||||
|     int chatwin; | ||||
|     bool active; | ||||
|     bool online; | ||||
|     uint8_t is_typing; | ||||
|     TOX_CONNECTION connection_status; | ||||
|     bool is_typing; | ||||
|     bool logging_on;    /* saves preference for friend irrespective of global settings */ | ||||
|     uint8_t status; | ||||
|  | ||||
|     struct LastOnline last_online; | ||||
|     struct FileReceiver file_receiver[MAX_FILES]; | ||||
|     struct GroupChatInvite group_invite; | ||||
|     uint8_t active_file_receivers; | ||||
|     struct GroupInvite group_invite; | ||||
|  | ||||
|     struct FileTransfer file_receiver[MAX_FILES]; | ||||
|     struct FileTransfer file_sender[MAX_FILES]; | ||||
| } ToxicFriend; | ||||
|  | ||||
| typedef struct { | ||||
|     char name[TOXIC_MAX_NAME_LENGTH]; | ||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|     int namelength; | ||||
|     char pub_key[TOX_CLIENT_ID_SIZE]; | ||||
|     int32_t num; | ||||
|     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint32_t num; | ||||
|     bool active; | ||||
|     uint64_t last_on; | ||||
| } BlockedFriend; | ||||
|  | ||||
| typedef struct { | ||||
|     int num_selected; | ||||
|     int max_idx;    /* 1 + the index of the last friend in list */ | ||||
|     int num_friends; | ||||
|     int *index; | ||||
|     size_t num_friends; | ||||
|     size_t num_online; | ||||
|     size_t max_idx;    /* 1 + the index of the last friend in list */ | ||||
|     uint32_t *index; | ||||
|     ToxicFriend *list; | ||||
| } FriendsList; | ||||
|  | ||||
| ToxWindow new_friendlist(void); | ||||
| void disable_chatwin(int32_t f_num); | ||||
| void disable_chatwin(uint32_t f_num); | ||||
| int get_friendnum(uint8_t *name); | ||||
| int load_blocklist(char *data); | ||||
| void kill_friendlist(void); | ||||
| void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort); | ||||
| void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort); | ||||
|  | ||||
| /* sorts friendlist_index first by connection status then alphabetically */ | ||||
| void sort_friendlist_index(void); | ||||
|   | ||||
| @@ -22,7 +22,6 @@ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| @@ -30,10 +29,14 @@ | ||||
| #include "friendlist.h" | ||||
| #include "log.h" | ||||
| #include "line_info.h" | ||||
| #include "dns.h" | ||||
| #include "groupchat.h" | ||||
| #include "prompt.h" | ||||
| #include "help.h" | ||||
| #include "term_mplex.h" | ||||
| #include "avatars.h" | ||||
| #include "name_lookup.h" | ||||
| #include "qr_code.h" | ||||
| #include "toxic_strings.h" | ||||
|  | ||||
| extern char *DATA_FILE; | ||||
| extern ToxWindow *prompt; | ||||
| @@ -48,9 +51,9 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int req = atoi(argv[1]); | ||||
|     long int req = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) { | ||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
| @@ -60,13 +63,14 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *msg; | ||||
|     int32_t friendnum = tox_add_friend_norequest(m, FrndRequests.request[req].key); | ||||
|     TOX_ERR_FRIEND_ADD err; | ||||
|     uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err); | ||||
|  | ||||
|     if (friendnum == -1) | ||||
|         msg = "Failed to add friend."; | ||||
|     else { | ||||
|         msg = "Friend request accepted."; | ||||
|     if (err != TOX_ERR_FRIEND_ADD_OK) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err); | ||||
|         return; | ||||
|     } else { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); | ||||
|         on_friendadded(m, friendnum, true); | ||||
|     } | ||||
|  | ||||
| @@ -81,47 +85,56 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
|  | ||||
|     FrndRequests.max_idx = i; | ||||
|     --FrndRequests.num_requests; | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg); | ||||
|  | ||||
| } | ||||
|  | ||||
| void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg) | ||||
| void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg) | ||||
| { | ||||
|     const char *errmsg; | ||||
|     int32_t f_num = tox_add_friend(m, (uint8_t *) id_bin, (uint8_t *) msg, (uint16_t) strlen(msg)); | ||||
|  | ||||
|     switch (f_num) { | ||||
|         case TOX_FAERR_TOOLONG: | ||||
|     TOX_ERR_FRIEND_ADD err; | ||||
|     uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err); | ||||
|  | ||||
|     switch (err) { | ||||
|         case TOX_ERR_FRIEND_ADD_TOO_LONG: | ||||
|             errmsg = "Message is too long."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_FAERR_NOMESSAGE: | ||||
|         case TOX_ERR_FRIEND_ADD_NO_MESSAGE: | ||||
|             errmsg = "Please add a message to your request."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_FAERR_OWNKEY: | ||||
|         case TOX_ERR_FRIEND_ADD_OWN_KEY: | ||||
|             errmsg = "That appears to be your own ID."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_FAERR_ALREADYSENT: | ||||
|         case TOX_ERR_FRIEND_ADD_ALREADY_SENT: | ||||
|             errmsg = "Friend request has already been sent."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_FAERR_UNKNOWN: | ||||
|             errmsg = "Undefined error when adding friend."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_FAERR_BADCHECKSUM: | ||||
|         case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM: | ||||
|             errmsg = "Bad checksum in address."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_FAERR_SETNEWNOSPAM: | ||||
|         case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM: | ||||
|             errmsg = "Nospam was different."; | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|         case TOX_ERR_FRIEND_ADD_MALLOC: | ||||
|             errmsg = "Core memory allocation failed."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_OK: | ||||
|             errmsg = "Friend request sent."; | ||||
|             on_friendadded(m, f_num, true); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_NULL: | ||||
|  | ||||
|         /* fallthrough */ | ||||
|         default: | ||||
|             errmsg = "Faile to add friend: Unknown error."; | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
| @@ -151,21 +164,23 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|         snprintf(msg, sizeof(msg), "%s", tmp); | ||||
|     } else { | ||||
|         char selfname[TOX_MAX_NAME_LENGTH]; | ||||
|         uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfname); | ||||
|         tox_self_get_name(m, (uint8_t *) selfname); | ||||
|  | ||||
|         size_t n_len = tox_self_get_name_size(m); | ||||
|         selfname[n_len] = '\0'; | ||||
|         snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname); | ||||
|     } | ||||
|  | ||||
|     char id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0}; | ||||
|     char id_bin[TOX_ADDRESS_SIZE] = {0}; | ||||
|     uint16_t id_len = (uint16_t) strlen(id); | ||||
|  | ||||
|     /* try to add tox ID */ | ||||
|     if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) { | ||||
|     if (id_len == 2 * TOX_ADDRESS_SIZE) { | ||||
|         size_t i; | ||||
|         char xx[3]; | ||||
|         uint32_t x; | ||||
|  | ||||
|         for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) { | ||||
|         for (i = 0; i < TOX_ADDRESS_SIZE; ++i) { | ||||
|             xx[0] = id[2 * i]; | ||||
|             xx[1] = id[2 * i + 1]; | ||||
|             xx[2] = '\0'; | ||||
| @@ -179,22 +194,16 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|         } | ||||
|  | ||||
|         cmd_add_helper(self, m, id_bin, msg); | ||||
|     } else {    /* assume id is a username@domain address and do DNS lookup */ | ||||
|         dns3_lookup(self, m, id_bin, id, msg); | ||||
|     } else {    /* assume id is a username@domain address and do http name server lookup */ | ||||
|         name_lookup(self, m, id_bin, id, msg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (argc < 2) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: No file path supplied."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* turns the avatar off */ | ||||
|     if (strlen(argv[1]) < 3) { | ||||
|         tox_unset_avatar(m); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No avatar set."); | ||||
|     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; | ||||
|     } | ||||
|  | ||||
| @@ -207,64 +216,30 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
|     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'; | ||||
|  | ||||
|     off_t sz = file_size(path); | ||||
|  | ||||
|     if (sz <= 8) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Invalid file."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (sz > TOX_AVATAR_MAX_DATA_LENGTH) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: File is too large."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     FILE *fp = fopen(path, "rb"); | ||||
|  | ||||
|     if (fp == NULL) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Could not open file."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: File type not supported."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *avatar = malloc(sz); | ||||
|  | ||||
|     if (avatar == NULL) | ||||
|         exit_toxic_err("Failed in set_avatar", FATALERR_MEMORY); | ||||
|  | ||||
|     if (fread(avatar, sz, 1, fp) != 1) { | ||||
|         fclose(fp); | ||||
|         free(avatar); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Read fail."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (tox_set_avatar(m, TOX_AVATAR_FORMAT_PNG, (const uint8_t *) avatar, (uint32_t) sz) == -1) | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Core error."); | ||||
|  | ||||
|     char filename[MAX_STR_SIZE]; | ||||
|     get_file_name(filename, sizeof(filename), path); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename); | ||||
|  | ||||
|     fclose(fp); | ||||
|     free(avatar); | ||||
|     if (avatar_set(m, path, len) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||
|                       "Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.", | ||||
|                       MAX_AVATAR_FILE_SIZE); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename); | ||||
| } | ||||
|  | ||||
| void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     line_info_clear(self->chatwin->hst); | ||||
|     wclear(window); | ||||
|     endwin(); | ||||
|     refresh(); | ||||
|     force_refresh(window); | ||||
| } | ||||
|  | ||||
| void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| @@ -275,17 +250,43 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | ||||
|     } | ||||
|  | ||||
|     const char *ip = argv[1]; | ||||
|     const char *port = argv[2]; | ||||
|     const char *key = argv[3]; | ||||
|     const char *port_str = argv[2]; | ||||
|     const char *ascii_key = argv[3]; | ||||
|  | ||||
|     if (atoi(port) == 0) { | ||||
|     long int port = strtol(port_str, NULL, 10); | ||||
|  | ||||
|     if (port <= 0 || port > MAX_PORT_RANGE) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *binary_string = hex_string_to_bin(key); | ||||
|     tox_bootstrap_from_address(m, ip, atoi(port), (uint8_t *) binary_string); | ||||
|     free(binary_string); | ||||
|     char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; | ||||
|  | ||||
|     if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_BOOTSTRAP err; | ||||
|     tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err); | ||||
|     tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err); | ||||
|  | ||||
|     switch (err) { | ||||
|         case TOX_ERR_BOOTSTRAP_BAD_HOST: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP."); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_BOOTSTRAP_BAD_PORT: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port."); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_BOOTSTRAP_NULL: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| @@ -295,9 +296,9 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int req = atoi(argv[1]); | ||||
|     long int req = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) { | ||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
| @@ -327,20 +328,134 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int groupnum = tox_add_groupchat(m); | ||||
|  | ||||
|     if (groupnum == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize."); | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name required"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (init_groupchat_win(prompt, m, groupnum) == -1) { | ||||
|     const char *tmp_name = argv[1]; | ||||
|     int len = strlen(tmp_name); | ||||
|  | ||||
|     if (len == 0 || len > TOX_GROUP_MAX_GROUP_NAME_LENGTH) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group name."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char name[TOX_GROUP_MAX_GROUP_NAME_LENGTH]; | ||||
|  | ||||
|     if (argv[1][0] == '\"') {    /* remove opening and closing quotes */ | ||||
|         snprintf(name, sizeof(name), "%s", &argv[1][1]); | ||||
|         len -= 2; | ||||
|         name[len] = '\0'; | ||||
|     } else { | ||||
|         snprintf(name, sizeof(name), "%s", argv[1]); | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_GROUP_NEW err; | ||||
|     uint32_t groupnum = tox_group_new(m, TOX_GROUP_PRIVACY_STATE_PUBLIC, (uint8_t *) name, len, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_GROUP_NEW_OK) { | ||||
|         switch (err) { | ||||
|             case TOX_ERR_GROUP_NEW_TOO_LONG: { | ||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name length cannot exceed %d.", | ||||
|                               TOX_GROUP_MAX_GROUP_NAME_LENGTH); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case TOX_ERR_GROUP_NEW_EMPTY: { | ||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name cannot be empty."); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             default: { | ||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d).", err); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int init = init_groupchat_win(m, groupnum, name, len); | ||||
|  | ||||
|     if (init == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||
|         tox_del_groupchat(m, groupnum); | ||||
|         tox_group_leave(m, groupnum, NULL, 0, NULL); | ||||
|     } else if (init == -2) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||
|                       "You have been kicked from a group. Close the window and try again."); | ||||
|         tox_group_leave(m, groupnum, NULL, 0, NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum); | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Chat ID is required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *chat_id = argv[1]; | ||||
|  | ||||
|     if (strlen(chat_id) != TOX_GROUP_CHAT_ID_SIZE * 2) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char id_bin[TOX_GROUP_CHAT_ID_SIZE] = {0}; | ||||
|  | ||||
|     size_t i; | ||||
|     char xch[3]; | ||||
|     uint32_t x; | ||||
|  | ||||
|     for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) { | ||||
|         xch[0] = chat_id[2 * i]; | ||||
|         xch[1] = chat_id[2 * i + 1]; | ||||
|         xch[2] = '\0'; | ||||
|  | ||||
|         if (sscanf(xch, "%02x", &x) != 1) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         id_bin[i] = x; | ||||
|     } | ||||
|  | ||||
|     const char *passwd = NULL; | ||||
|     uint16_t passwd_len = 0; | ||||
|  | ||||
|     if (argc > 1) { | ||||
|         passwd = argv[2]; | ||||
|         passwd_len = strlen(passwd); | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_GROUP_JOIN err; | ||||
|     uint32_t groupnum = tox_group_join(m, (uint8_t *) id_bin, (uint8_t *) passwd, passwd_len, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_GROUP_JOIN_OK) { | ||||
|         if (err == TOX_ERR_GROUP_JOIN_TOO_LONG) | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length cannot exceed %d.", TOX_GROUP_MAX_PASSWORD_SIZE); | ||||
|         else | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int init = init_groupchat_win(m, groupnum, NULL, 0); | ||||
|  | ||||
|     if (init == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||
|         tox_group_leave(m, groupnum, NULL, 0, NULL); | ||||
|     } else if (init == -2) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||
|                       "You have been kicked from a group. Close the window and try again."); | ||||
|         tox_group_leave(m, groupnum, NULL, 0, NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| @@ -350,9 +465,9 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         if (log->log_on) | ||||
|             msg = "Logging for this window is ON. Type \"/log off\" to disable."; | ||||
|             msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)"; | ||||
|         else | ||||
|             msg = "Logging for this window is OFF. Type \"/log on\" to enable."; | ||||
|             msg = "Logging for this window is OFF; type \"/log on\" to enable."; | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
| @@ -361,19 +476,21 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|     const char *swch = argv[1]; | ||||
|  | ||||
|     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { | ||||
|         char myid[TOX_FRIEND_ADDRESS_SIZE]; | ||||
|         tox_get_address(m, (uint8_t *) myid); | ||||
|         char myid[TOX_ADDRESS_SIZE]; | ||||
|         tox_self_get_address(m, (uint8_t *) myid); | ||||
|  | ||||
|         int log_ret = -1; | ||||
|  | ||||
|         if (self->is_chat) { | ||||
|             Friends.list[self->num].logging_on = true; | ||||
|             log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT); | ||||
|             log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT); | ||||
|         } else if (self->is_prompt) { | ||||
|             log_enable(self->name, myid, NULL, log, LOG_PROMPT); | ||||
|             log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT); | ||||
|         } else if (self->is_groupchat) { | ||||
|             log_enable(self->name, myid, NULL, log, LOG_GROUP); | ||||
|             log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP); | ||||
|         } | ||||
|  | ||||
|         msg = "Logging enabled"; | ||||
|         msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
|     } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { | ||||
| @@ -382,7 +499,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|  | ||||
|         log_disable(log); | ||||
|  | ||||
|         msg = "Logging disabled"; | ||||
|         msg = "Logging disabled."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
|     } | ||||
| @@ -393,19 +510,74 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|  | ||||
| void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0}; | ||||
|     char address[TOX_FRIEND_ADDRESS_SIZE]; | ||||
|     tox_get_address(m, (uint8_t *) address); | ||||
|     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||
|     char bin_id[TOX_ADDRESS_SIZE]; | ||||
|     tox_self_get_address(m, (uint8_t *) bin_id); | ||||
|  | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) { | ||||
|         char xx[3]; | ||||
|         snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff); | ||||
|         strcat(id, xx); | ||||
|     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string); | ||||
| } | ||||
|  | ||||
| void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||
|     char bin_id[TOX_ADDRESS_SIZE]; | ||||
|     tox_self_get_address(m, (uint8_t *) bin_id); | ||||
|  | ||||
|     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char nick[TOX_MAX_NAME_LENGTH]; | ||||
|     tox_self_get_name(m, (uint8_t *) nick); | ||||
|     size_t nick_len = tox_self_get_name_size(m); | ||||
|     nick[nick_len] = '\0'; | ||||
|  | ||||
|     size_t data_file_len = strlen(DATA_FILE); | ||||
|     char dir[data_file_len + 1]; | ||||
|     size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir); | ||||
|  | ||||
| #ifdef QRPNG | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'"); | ||||
|         return; | ||||
|     } else if (!strcmp(argv[1], "txt")) { | ||||
|  | ||||
| #endif /* QRPNG */ | ||||
|         char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1]; | ||||
|         snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); | ||||
|  | ||||
|         if (ID_to_QRcode_txt(id_string, qr_path) == -1) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||
|  | ||||
| #ifdef QRPNG | ||||
|     } else if (!strcmp(argv[1], "png")) { | ||||
|         char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1]; | ||||
|         snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); | ||||
|  | ||||
|         if (ID_to_QRcode_png(id_string, qr_path) == -1) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||
|  | ||||
|     } else { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #endif /* QRPNG */ | ||||
| } | ||||
|  | ||||
| void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| @@ -416,7 +588,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | ||||
|     } | ||||
|  | ||||
|     char nick[MAX_STR_SIZE]; | ||||
|     int len = 0; | ||||
|     size_t len = 0; | ||||
|  | ||||
|     if (argv[1][0] == '\"') {    /* remove opening and closing quotes */ | ||||
|         snprintf(nick, sizeof(nick), "%s", &argv[1][1]); | ||||
| @@ -435,8 +607,9 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | ||||
|     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||
|     nick[len] = '\0'; | ||||
|  | ||||
|     tox_set_name(m, (uint8_t *) nick, (uint16_t) len); | ||||
|     tox_self_set_name(m, (uint8_t *) nick, len, NULL); | ||||
|     prompt_update_nick(prompt, nick); | ||||
|     set_nick_all_groups(m, nick, len); | ||||
|  | ||||
|     store_data(m, DATA_FILE); | ||||
| } | ||||
| @@ -448,18 +621,32 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argv[1][0] != '\"') { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes."); | ||||
|         return; | ||||
|     prompt_update_statusmessage(prompt, m, argv[1]); | ||||
| } | ||||
|  | ||||
| void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     long int nospam = rand(); | ||||
|  | ||||
|     if (argc > 0) { | ||||
|         nospam = strtol(argv[1], NULL, 16); | ||||
|  | ||||
|         if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value."); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* remove opening and closing quotes */ | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|     snprintf(msg, sizeof(msg), "%s", &argv[1][1]); | ||||
|     int len = strlen(msg) - 1; | ||||
|     msg[len] = '\0'; | ||||
|     uint32_t old_nospam = tox_self_get_nospam(m); | ||||
|     tox_self_set_nospam(m, (uint32_t) nospam); | ||||
|  | ||||
|     prompt_update_statusmessage(prompt, m, msg); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:"); | ||||
|     cmd_myid(window, self, m, 0, NULL); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||
|                   "Any services that relied on your old ID will need to be updated manually."); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'", | ||||
|                   old_nospam); | ||||
| } | ||||
|  | ||||
| void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| @@ -486,9 +673,9 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | ||||
|         if (!FrndRequests.request[i].active) | ||||
|             continue; | ||||
|  | ||||
|         char id[TOX_CLIENT_ID_SIZE * 2 + 1] = {0}; | ||||
|         char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0}; | ||||
|  | ||||
|         for (j = 0; j < TOX_CLIENT_ID_SIZE; ++j) { | ||||
|         for (j = 0; j < TOX_PUBLIC_KEY_SIZE; ++j) { | ||||
|             char d[3]; | ||||
|             snprintf(d, sizeof(d), "%02X", FrndRequests.request[i].key[j] & 0xff); | ||||
|             strcat(id, d); | ||||
| @@ -503,43 +690,43 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | ||||
| } | ||||
|  | ||||
| void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| {    | ||||
| { | ||||
|     bool have_note = false; | ||||
|     const char *errmsg; | ||||
|  | ||||
|     lock_status (); | ||||
|  | ||||
|     if (argc >= 2) { | ||||
|         have_note = true; | ||||
|     } else if (argc < 1) { | ||||
|         errmsg = "Require a status. Statuses are: online, busy and away."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         return; | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     char status[MAX_STR_SIZE]; | ||||
|     snprintf(status, sizeof(status), "%s", argv[1]); | ||||
|     str_to_lower(status); | ||||
|     const char *status_str = argv[1]; | ||||
|     TOX_USER_STATUS status; | ||||
|  | ||||
|     TOX_USERSTATUS status_kind; | ||||
|  | ||||
|     if (!strcmp(status, "online")) | ||||
|         status_kind = TOX_USERSTATUS_NONE; | ||||
|     else if (!strcmp(status, "away")) | ||||
|         status_kind = TOX_USERSTATUS_AWAY; | ||||
|     else if (!strcmp(status, "busy")) | ||||
|         status_kind = TOX_USERSTATUS_BUSY; | ||||
|     if (!strcasecmp(status_str, "online")) | ||||
|         status = TOX_USER_STATUS_NONE; | ||||
|     else if (!strcasecmp(status_str, "away")) | ||||
|         status = TOX_USER_STATUS_AWAY; | ||||
|     else if (!strcasecmp(status_str, "busy")) | ||||
|         status = TOX_USER_STATUS_BUSY; | ||||
|     else { | ||||
|         errmsg = "Invalid status. Valid statuses are: online, busy and away."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         return; | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     tox_set_user_status(m, status_kind); | ||||
|     prompt_update_status(prompt, status_kind); | ||||
|     tox_self_set_status(m, status); | ||||
|     prompt_update_status(prompt, status); | ||||
|     set_status_all_groups(m, status); | ||||
|  | ||||
|     if (have_note) { | ||||
|         if (argv[2][0] != '\"') { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes."); | ||||
|             return; | ||||
|             goto finish; | ||||
|         } | ||||
|  | ||||
|         /* remove opening and closing quotes */ | ||||
| @@ -550,4 +737,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
|  | ||||
|         prompt_update_statusmessage(prompt, m, msg); | ||||
|     } | ||||
|  | ||||
| finish: | ||||
|     unlock_status (); | ||||
| } | ||||
|   | ||||
| @@ -33,20 +33,28 @@ void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE | ||||
| void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg); | ||||
| void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg); | ||||
|  | ||||
| #ifdef AUDIO | ||||
| void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #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 */ | ||||
							
								
								
									
										1355
									
								
								src/groupchat.c
									
									
									
									
									
								
							
							
						
						
									
										1355
									
								
								src/groupchat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -29,20 +29,39 @@ | ||||
| #define SIDEBAR_WIDTH 16 | ||||
| #define SDBAR_OFST 2    /* Offset for the peer number box at the top of the statusbar */ | ||||
| #define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2 | ||||
| #define GROUP_EVENT_WAIT 3 | ||||
|  | ||||
| struct GroupPeer { | ||||
|     bool       active; | ||||
|     char       name[TOX_MAX_NAME_LENGTH]; | ||||
|     size_t     name_length; | ||||
|     uint32_t   peer_id; | ||||
|     uint8_t    public_key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE]; | ||||
|     TOX_USER_STATUS status; | ||||
|     TOX_GROUP_ROLE  role; | ||||
|     uint64_t   last_active; | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     int chatwin; | ||||
|     bool active; | ||||
|     int num_peers; | ||||
|     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ | ||||
|     uint8_t  *peer_names; | ||||
|     uint8_t  *oldpeer_names; | ||||
|     uint16_t *peer_name_lengths; | ||||
|     uint16_t *oldpeer_name_lengths; | ||||
|     struct GroupPeer *peer_list; | ||||
|     char       *name_list;    /* List of peer names, needed for tab completion */ | ||||
|     uint32_t   num_peers;     /* Number of peers in the chat/name_list array */ | ||||
|     uint32_t   max_idx;       /* Maximum peer list index - 1 */ | ||||
|     uint32_t   groupnumber; | ||||
|     int        chatwin; | ||||
|     bool       active; | ||||
|     uint64_t   time_connected;    /* The time we successfully connected to the group */ | ||||
|     int        side_pos;     /* current position of the sidebar - used for scrolling up and down */ | ||||
| } GroupChat; | ||||
|  | ||||
| void kill_groupchat_window(ToxWindow *self); | ||||
| int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum); | ||||
| ToxWindow new_group_chat(Tox *m, int groupnum); | ||||
| void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum); | ||||
| int init_groupchat_win(Tox *m, uint32_t groupnum, const char *groupname, size_t length); | ||||
| void set_nick_all_groups(Tox *m, const char *nick, size_t length); | ||||
| void set_status_all_groups(Tox *m, uint8_t status); | ||||
| int group_get_nick_peer_id(uint32_t groupnum, const char *nick, uint32_t *peer_id); | ||||
| int get_peer_index(uint32_t groupnum, uint32_t peer_id); | ||||
|  | ||||
| /* destroys and re-creates groupchat window */ | ||||
| void redraw_groupchat_win(ToxWindow *self); | ||||
|  | ||||
| #endif /* #define GROUPCHAT_H */ | ||||
|   | ||||
							
								
								
									
										121
									
								
								src/help.c
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								src/help.c
									
									
									
									
									
								
							| @@ -27,7 +27,7 @@ | ||||
| #include "help.h" | ||||
| #include "misc_tools.h" | ||||
|  | ||||
| #define HELP_MENU_HEIGHT 8 | ||||
| #define HELP_MENU_HEIGHT 9 | ||||
| #define HELP_MENU_WIDTH 26 | ||||
|  | ||||
| void help_init_menu(ToxWindow *self) | ||||
| @@ -60,6 +60,9 @@ static void help_init_window(ToxWindow *self, int height, int width) | ||||
|     int y2, x2; | ||||
|     getmaxyx(stdscr, y2, x2); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) | ||||
|         return; | ||||
|  | ||||
|     height = MIN(height, y2); | ||||
|     width = MIN(width, x2); | ||||
|  | ||||
| @@ -86,6 +89,12 @@ static void help_draw_menu(ToxWindow *self) | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "hat commands\n"); | ||||
|  | ||||
|     wprintw(win, " g"); | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "r"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "oup commands\n"); | ||||
|  | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, " f"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
| @@ -138,16 +147,23 @@ static void help_draw_global(ToxWindow *self) | ||||
|  | ||||
|     wprintw(win, "  /add <addr> <msg>          : Add contact with optional message\n"); | ||||
|     wprintw(win, "  /accept <id>               : Accept friend request\n"); | ||||
|     wprintw(win, "  /avatar <path>             : Set a personal avatar\n"); | ||||
|     wprintw(win, "  /avatar <path>             : Set an avatar (leave path empty to unset)\n"); | ||||
|     wprintw(win, "  /decline <id>              : Decline friend request\n"); | ||||
|     wprintw(win, "  /requests                  : List pending friend requests\n"); | ||||
|     wprintw(win, "  /connect <ip> <port> <key> : Manually connect to a DHT node\n"); | ||||
|     wprintw(win, "  /status <type> <msg>       : Set status with optional note\n"); | ||||
|     wprintw(win, "  /note <msg>                : Set a personal note\n"); | ||||
|     wprintw(win, "  /group                     : Create a group chat\n"); | ||||
|     wprintw(win, "  /join <chat id> <passwd>   : Join a group chat with optional password\n"); | ||||
|     wprintw(win, "  /nick <nick>               : Set your nickname\n"); | ||||
|     wprintw(win, "  /nospam <value>            : Change part of your Tox ID to stop spam\n"); | ||||
|     wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n"); | ||||
|     wprintw(win, "  /groupchat                 : Create a group chat\n"); | ||||
|     wprintw(win, "  /myid                      : Print your Tox ID\n"); | ||||
| #ifdef QRPNG | ||||
|     wprintw(win, "  /myqr <txt> or <png>       : Print your Tox ID's QR code to a file.\n"); | ||||
| #else | ||||
|     wprintw(win, "  /myqr                      : Print your Tox ID's QR code to a file.\n"); | ||||
| #endif /* QRPNG */ | ||||
|     wprintw(win, "  /clear                     : Clear window history\n"); | ||||
|     wprintw(win, "  /close                     : Close the current chat window\n"); | ||||
|     wprintw(win, "  /quit or /exit             : Exit Toxic\n"); | ||||
| @@ -161,6 +177,15 @@ static void help_draw_global(ToxWindow *self) | ||||
|     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); | ||||
| @@ -177,8 +202,7 @@ static void help_draw_chat(ToxWindow *self) | ||||
|     wprintw(win, "Chat Commands:\n"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|  | ||||
|     wprintw(win, "  /invite <n>                : Invite contact to a group chat\n"); | ||||
|     wprintw(win, "  /join                      : Join a pending group chat\n"); | ||||
|     wprintw(win, "  /gaccept <password>        : Accept a group invite with optional password\n"); | ||||
|     wprintw(win, "  /sendfile <path>           : Send a file\n"); | ||||
|     wprintw(win, "  /savefile <id>             : Receive a file\n"); | ||||
|     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); | ||||
| @@ -194,9 +218,16 @@ static void help_draw_chat(ToxWindow *self) | ||||
|     wprintw(win, "  /hangup                    : Hangup active call\n"); | ||||
|     wprintw(win, "  /sdev <type> <id>          : Change active device\n"); | ||||
|     wprintw(win, "  /mute <type>               : Mute active device if in call\n"); | ||||
|     wprintw(win, "  /sense <n>                 : VAD sensitivity treshold\n"); | ||||
|     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); | ||||
| @@ -213,11 +244,14 @@ static void help_draw_keys(ToxWindow *self) | ||||
|     wprintw(win, "Key bindings:\n"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|  | ||||
|     wprintw(win, "  Ctrl+O and Ctrl+P         : Navigate through the tabs\n");  | ||||
|     wprintw(win, "  Ctrl+O and Ctrl+P         : Navigate through the tabs\n"); | ||||
|     wprintw(win, "  Page Up and Page Down     : Scroll window history one line\n"); | ||||
|     wprintw(win, "  Ctrl+F and Ctrl+V         : Scroll window history half a page\n"); | ||||
|     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); | ||||
|     wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n\n"); | ||||
|     wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n"); | ||||
|     wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n"); | ||||
|     wprintw(win, "  Ctrl+J                    : Insert new line\n"); | ||||
|     wprintw(win, "  Ctrl+T                    : Toggle paste mode\n\n"); | ||||
|     wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n"); | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
| @@ -226,6 +260,49 @@ static void help_draw_keys(ToxWindow *self) | ||||
|     wrefresh(win); | ||||
| } | ||||
|  | ||||
| static void help_draw_group(ToxWindow *self) | ||||
| { | ||||
|     WINDOW *win = self->help->win; | ||||
|  | ||||
|     wmove(win, 1, 1); | ||||
|  | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|     wprintw(win, "Group commands:\n"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|  | ||||
|     wprintw(win, "  /chatid                    : Print the group chat id to share with others\n"); | ||||
|     wprintw(win, "  /mykey                     : Print your group public key\n"); | ||||
|     wprintw(win, "  /ignore <nick>             : Ignore peer\n"); | ||||
|     wprintw(win, "  /unignore <nick>           : Unignore peer \n"); | ||||
|     wprintw(win, "  /rejoin                    : Rejoin the group\n"); | ||||
|     wprintw(win, "  /topic <msg>               : Set group topic (show current topic if no msg)\n"); | ||||
|     wprintw(win, "  /whisper <nick> <msg>      : Send private message to nick\n"); | ||||
|     wprintw(win, "  /whois <nick>              : Display info about nick.\n"); | ||||
|  | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, " Moderator commands:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|     wprintw(win, "  /kick <nick>               : Kick peer\n"); | ||||
|     wprintw(win, "  /ban <nick>                : Ban peer (leave nick blank to see ban list)\n"); | ||||
|     wprintw(win, "  /unban <Ban ID>            : Unban entry\n"); | ||||
|     wprintw(win, "  /silence <nick>            : Silences peer for the entire group\n"); | ||||
|     wprintw(win, "  /unsilence <nick>          : Unsilences peer\n"); | ||||
|  | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, " Founder commands:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|     wprintw(win, "  /mod <nick>                : Promote peer to moderator\n"); | ||||
|     wprintw(win, "  /unmod <nick>              : Demote moderator to normal user\n"); | ||||
|     wprintw(win, "  /passwd <password>         : Set group password (leave blank to unset)\n"); | ||||
|     wprintw(win, "  /peerlimit <num>           : Set group peer limit\n"); | ||||
|     wprintw(win, "  /privacy <state>           : Set group privacy state: private|public\n"); | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wrefresh(win); | ||||
| } | ||||
|  | ||||
| static void help_draw_contacts(ToxWindow *self) | ||||
| { | ||||
|     WINDOW *win = self->help->win; | ||||
| @@ -250,37 +327,46 @@ static void help_draw_contacts(ToxWindow *self) | ||||
|  | ||||
| void help_onKey(ToxWindow *self, wint_t key) | ||||
| { | ||||
|     switch(key) { | ||||
|     switch (key) { | ||||
|         case 'x': | ||||
|         case T_KEY_ESC: | ||||
|             help_exit(self); | ||||
|             break; | ||||
|  | ||||
|         case 'c': | ||||
| #ifdef AUDIO | ||||
|             help_init_window(self, 19, 80); | ||||
| #ifdef VIDEO | ||||
|             help_init_window(self, 21, 80); | ||||
| #elif AUDIO | ||||
|             help_init_window(self, 18, 80); | ||||
| #else | ||||
|             help_init_window(self, 9, 80); | ||||
|             help_init_window(self, 10, 80); | ||||
| #endif | ||||
|             self->help->type = HELP_CHAT; | ||||
|             break; | ||||
|  | ||||
|         case 'g': | ||||
| #ifdef AUDIO | ||||
|             help_init_window(self, 24, 80); | ||||
| #ifdef VIDEO | ||||
|             help_init_window(self, 30, 80); | ||||
| #elif AUDIO | ||||
|             help_init_window(self, 26, 80); | ||||
| #else | ||||
|             help_init_window(self, 20, 80); | ||||
|             help_init_window(self, 22, 80); | ||||
| #endif | ||||
|             self->help->type = HELP_GLOBAL; | ||||
|             break; | ||||
|  | ||||
|         case 'r': | ||||
|             help_init_window(self, 25, 80); | ||||
|             self->help->type = HELP_GROUP; | ||||
|             break; | ||||
|  | ||||
|         case 'f': | ||||
|             help_init_window(self, 10, 80); | ||||
|             self->help->type = HELP_CONTACTS; | ||||
|             break; | ||||
|  | ||||
|         case 'k': | ||||
|             help_init_window(self, 12, 80); | ||||
|             help_init_window(self, 15, 80); | ||||
|             self->help->type = HELP_KEYS; | ||||
|             break; | ||||
|  | ||||
| @@ -295,7 +381,7 @@ void help_onDraw(ToxWindow *self) | ||||
| { | ||||
|     curs_set(0); | ||||
|  | ||||
|     switch(self->help->type) { | ||||
|     switch (self->help->type) { | ||||
|         case HELP_MENU: | ||||
|             help_draw_menu(self); | ||||
|             return; | ||||
| @@ -317,6 +403,7 @@ void help_onDraw(ToxWindow *self) | ||||
|             break; | ||||
|  | ||||
|         case HELP_GROUP: | ||||
|             help_draw_group(self); | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										64
									
								
								src/input.c
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								src/input.c
									
									
									
									
									
								
							| @@ -32,22 +32,29 @@ | ||||
| #include "toxic_strings.h" | ||||
| #include "line_info.h" | ||||
| #include "notify.h" | ||||
| #include "groupchat.h" | ||||
| #include "settings.h" | ||||
|  | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| /* add a char to input field and buffer */ | ||||
| void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     /* this is the only place we need to do this check */ | ||||
|     if (key == '\n') | ||||
|         key = L'¶'; | ||||
|  | ||||
|     int cur_len = wcwidth(key); | ||||
|  | ||||
|     /* this is the only place we need to do this check */ | ||||
|     if (cur_len == -1) { | ||||
|         sound_notify(self, error, 0, NULL); | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (add_char_to_buf(ctx, key) == -1) { | ||||
|         sound_notify(self, error, 0, NULL); | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -63,7 +70,7 @@ static void input_backspace(ToxWindow *self, int x, int mx_x) | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (del_char_buf_bck(ctx) == -1) { | ||||
|         sound_notify(self, error, 0, NULL); | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -80,21 +87,32 @@ static void input_backspace(ToxWindow *self, int x, int mx_x) | ||||
| static void input_delete(ToxWindow *self) | ||||
| { | ||||
|     if (del_char_buf_frnt(self->chatwin) == -1) | ||||
|         sound_notify(self, error, 0, NULL); | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
| } | ||||
|  | ||||
| /* delete last typed word */ | ||||
| static void input_del_word(ToxWindow *self, int x, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (del_word_buf(ctx) == -1) { | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* deletes entire line before cursor from input field and buffer */ | ||||
| static void input_discard(ToxWindow *self) | ||||
| { | ||||
|     if (discard_buf(self->chatwin) == -1) | ||||
|         sound_notify(self, error, 0, NULL); | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
| } | ||||
|  | ||||
| /* deletes entire line after cursor from input field and buffer */ | ||||
| static void input_kill(ChatContext *ctx) | ||||
| { | ||||
|     if (kill_buf(ctx) == -1) | ||||
|         sound_notify(NULL, error, NT_ALWAYS, NULL); | ||||
|         sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||
| } | ||||
|  | ||||
| static void input_yank(ToxWindow *self, int x, int mx_x) | ||||
| @@ -102,7 +120,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x) | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (yank_buf(ctx) == -1) { | ||||
|         sound_notify(self, error, 0, NULL); | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -110,7 +128,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x) | ||||
|  | ||||
|     if (x + yank_cols >= mx_x) { | ||||
|         int rmdr = MAX(0, (x + yank_cols) - mx_x); | ||||
|         int s_len = wcswidth(&ctx->line[ctx->start], rmdr); | ||||
|         int s_len = MAX(0, wcswidth(&ctx->line[ctx->start], rmdr)); | ||||
|         ctx->start += s_len + 1; | ||||
|     } | ||||
| } | ||||
| @@ -122,7 +140,7 @@ static void input_mv_end(ToxWindow *self, int y, int mx_x) | ||||
|  | ||||
|     ctx->pos = ctx->len; | ||||
|  | ||||
|     int wlen = wcswidth(ctx->line, sizeof(ctx->line)); | ||||
|     int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); | ||||
|     ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x)); | ||||
| } | ||||
|  | ||||
| @@ -181,7 +199,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x) | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     fetch_hist_item(ctx, key); | ||||
|     int wlen = wcswidth(ctx->line, sizeof(ctx->line)); | ||||
|     int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); | ||||
|     ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1; | ||||
| } | ||||
|  | ||||
| @@ -213,6 +231,10 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
|             input_yank(self, x, mx_x); | ||||
|             break; | ||||
|  | ||||
|         case T_KEY_C_W: | ||||
|             input_del_word(self, x, mx_x); | ||||
|             break; | ||||
|  | ||||
|         case KEY_HOME: | ||||
|         case T_KEY_C_A: | ||||
|             input_mv_home(self); | ||||
| @@ -236,10 +258,30 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
|             input_history(self, key, mx_x); | ||||
|             break; | ||||
|  | ||||
|         case T_KEY_C_L: | ||||
|             force_refresh(self->chatwin->history); | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|             match = false; | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     /* TODO: this special case is ugly. | ||||
|        maybe convert entire function to if/else and make them all customizable keys? */ | ||||
|     if (!match) { | ||||
|         if (key == user_settings->key_toggle_peerlist) { | ||||
|             if (self->is_groupchat) { | ||||
|                 self->show_peerlist ^= 1; | ||||
|                 redraw_groupchat_win(self); | ||||
|             } | ||||
|  | ||||
|             match = true; | ||||
|         } else if (key == user_settings->key_toggle_pastemode) { | ||||
|             self->chatwin->pastemode ^= 1; | ||||
|             match = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return match; | ||||
| } | ||||
|   | ||||
							
								
								
									
										151
									
								
								src/line_info.c
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								src/line_info.c
									
									
									
									
									
								
							| @@ -59,7 +59,7 @@ void line_info_reset_start(ToxWindow *self, struct history *hst) | ||||
|     int y2, x2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; | ||||
|     int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; | ||||
|     int top_offst = self->is_chat || self->is_prompt ? 2 : 0; | ||||
|     int max_y = (y2 - CHATBOX_HEIGHT - top_offst); | ||||
|  | ||||
| @@ -130,9 +130,12 @@ static struct line_info *line_info_ret_queue(struct history *hst) | ||||
| } | ||||
|  | ||||
| /* creates new line_info line and puts it in the queue. */ | ||||
| void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uint8_t type, uint8_t bold,  | ||||
|                    uint8_t colour, const char *msg, ...) | ||||
| void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, | ||||
|                    uint8_t bold, uint8_t colour, const char *msg, ...) | ||||
| { | ||||
|     if (!self) | ||||
|         return; | ||||
|  | ||||
|     struct history *hst = self->chatwin->hst; | ||||
|  | ||||
|     if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) | ||||
| @@ -155,24 +158,37 @@ void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uin | ||||
|     /* for type-specific formatting in print function */ | ||||
|     switch (type) { | ||||
|         case IN_ACTION: | ||||
|  | ||||
|         /* fallthrough */ | ||||
|         case OUT_ACTION: | ||||
|             len += 5; | ||||
|             len += strlen(user_settings->line_normal) + 2; | ||||
|             break; | ||||
|  | ||||
|         case IN_MSG: | ||||
|  | ||||
|         /* fallthrough */ | ||||
|         case OUT_MSG: | ||||
|             len += 6; | ||||
|             len += strlen(user_settings->line_normal) + 3; | ||||
|             break; | ||||
|  | ||||
|         case IN_PRVT_MSG: | ||||
|         case OUT_PRVT_MSG: | ||||
|             len += strlen(user_settings->line_special) + 3; | ||||
|             break; | ||||
|  | ||||
|         case CONNECTION: | ||||
|             len += 5; | ||||
|             len += strlen(user_settings->line_join) + 2; | ||||
|             break; | ||||
|  | ||||
|         case DISCONNECTION: | ||||
|             len += strlen(user_settings->line_quit) + 2; | ||||
|             break; | ||||
|  | ||||
|         case SYS_MSG: | ||||
|             break; | ||||
|  | ||||
|         case NAME_CHANGE: | ||||
|             len += 4; | ||||
|             len += strlen(user_settings->line_alert) + 1; | ||||
|             break; | ||||
|  | ||||
|         case PROMPT: | ||||
| @@ -198,7 +214,7 @@ void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uin | ||||
|  | ||||
|     if (timestr) { | ||||
|         snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr); | ||||
|         len += strlen(new_line->timestr); | ||||
|         len += strlen(new_line->timestr) + 1; | ||||
|     } | ||||
|  | ||||
|     if (name1) { | ||||
| @@ -222,7 +238,7 @@ void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uin | ||||
| } | ||||
|  | ||||
| /* adds a single queue item to hst if possible. only called once per call to line_info_print() */ | ||||
| static void line_info_check_queue(ToxWindow *self)  | ||||
| static void line_info_check_queue(ToxWindow *self) | ||||
| { | ||||
|     struct history *hst = self->chatwin->hst; | ||||
|     struct line_info *line = line_info_ret_queue(hst); | ||||
| @@ -246,7 +262,7 @@ static void line_info_check_queue(ToxWindow *self) | ||||
|     if (x2 <= SIDEBAR_WIDTH) | ||||
|         return; | ||||
|  | ||||
|     int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;   /* offset width of groupchat sidebar */ | ||||
|     int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;   /* offset width of groupchat sidebar */ | ||||
|     int lines = 1 + line->newlines + (line->len / (x2 - offst)); | ||||
|     int max_y = y2 - CHATBOX_HEIGHT; | ||||
|  | ||||
| @@ -258,10 +274,6 @@ static void line_info_check_queue(ToxWindow *self) | ||||
|             ++hst->start_id; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* force move to bottom of history when we print an outgoing message */ | ||||
|     if (line->type == OUT_MSG) | ||||
|         line_info_reset_start(self, hst); | ||||
| } | ||||
|  | ||||
| #define NOREAD_FLAG_TIMEOUT 5    /* seconds before a sent message with no read receipt is flagged as unread */ | ||||
| @@ -299,10 +311,16 @@ void line_info_print(ToxWindow *self) | ||||
|  | ||||
|         switch (type) { | ||||
|             case OUT_MSG: | ||||
|  | ||||
|             /* fallthrough */ | ||||
|             case OUT_MSG_READ: | ||||
|  | ||||
|             /* fallthrough */ | ||||
|             case IN_MSG: | ||||
|             case IN_PRVT_MSG: | ||||
|             case OUT_PRVT_MSG: | ||||
|                 wattron(win, COLOR_PAIR(BLUE)); | ||||
|                 wprintw(win, "%s", line->timestr); | ||||
|                 wprintw(win, "%s ", line->timestr); | ||||
|                 wattroff(win, COLOR_PAIR(BLUE)); | ||||
|  | ||||
|                 int nameclr = GREEN; | ||||
| @@ -313,18 +331,35 @@ void line_info_print(ToxWindow *self) | ||||
|                     nameclr = CYAN; | ||||
|  | ||||
|                 wattron(win, COLOR_PAIR(nameclr)); | ||||
|                 wprintw(win, "--- %s: ", line->name1); | ||||
|                 wprintw(win, "%s %s: ", (type != OUT_PRVT_MSG && type != IN_PRVT_MSG) ? | ||||
|                         user_settings->line_normal : | ||||
|                         user_settings->line_special, | ||||
|                         line->name1); | ||||
|                 wattroff(win, COLOR_PAIR(nameclr)); | ||||
|  | ||||
|                 if (line->msg[0] == '>') | ||||
|                     wattron(win, COLOR_PAIR(GREEN)); | ||||
|                 char *msg = line->msg; | ||||
|  | ||||
|                 wprintw(win, "%s", line->msg); | ||||
|                 while (msg) { | ||||
|                     char *line = strsep(&msg, "\n"); | ||||
|  | ||||
|                 if (line->msg[0] == '>') | ||||
|                     wattroff(win, COLOR_PAIR(GREEN)); | ||||
|                     if (line[0] == '>') | ||||
|                         wattron(win, COLOR_PAIR(GREEN)); | ||||
|                     else if (line[0] == '<') | ||||
|                         wattron(win, COLOR_PAIR(RED)); | ||||
|  | ||||
|                 if (type == OUT_MSG && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) { | ||||
|                     wprintw(win, "%s%c", line, msg ? '\n' : '\0'); | ||||
|  | ||||
|                     if (line[0] == '>') | ||||
|                         wattroff(win, COLOR_PAIR(GREEN)); | ||||
|                     else if (line[0] == '<') | ||||
|                         wattroff(win, COLOR_PAIR(RED)); | ||||
|  | ||||
|                     // change the \0 set by strsep back to \n | ||||
|                     if (msg) | ||||
|                         msg[-1] = '\n'; | ||||
|                 } | ||||
|  | ||||
|                 if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||
|                     wattron(win, COLOR_PAIR(RED)); | ||||
|                     wprintw(win, " x", line->msg); | ||||
|                     wattroff(win, COLOR_PAIR(RED)); | ||||
| @@ -339,17 +374,21 @@ void line_info_print(ToxWindow *self) | ||||
|                 break; | ||||
|  | ||||
|             case OUT_ACTION_READ: | ||||
|  | ||||
|             /* fallthrough */ | ||||
|             case OUT_ACTION: | ||||
|  | ||||
|             /* fallthrough */ | ||||
|             case IN_ACTION: | ||||
|                 wattron(win, COLOR_PAIR(BLUE)); | ||||
|                 wprintw(win, "%s", line->timestr); | ||||
|                 wprintw(win, "%s ", line->timestr); | ||||
|                 wattroff(win, COLOR_PAIR(BLUE)); | ||||
|  | ||||
|                 wattron(win, COLOR_PAIR(YELLOW)); | ||||
|                 wprintw(win, "-*- %s %s", line->name1, line->msg); | ||||
|                 wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg); | ||||
|                 wattroff(win, COLOR_PAIR(YELLOW)); | ||||
|  | ||||
|                 if (type == OUT_ACTION && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) { | ||||
|                 if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||
|                     wattron(win, COLOR_PAIR(RED)); | ||||
|                     wprintw(win, " x", line->msg); | ||||
|                     wattroff(win, COLOR_PAIR(RED)); | ||||
| @@ -366,7 +405,7 @@ void line_info_print(ToxWindow *self) | ||||
|             case SYS_MSG: | ||||
|                 if (line->timestr[0]) { | ||||
|                     wattron(win, COLOR_PAIR(BLUE)); | ||||
|                     wprintw(win, "%s", line->timestr); | ||||
|                     wprintw(win, "%s ", line->timestr); | ||||
|                     wattroff(win, COLOR_PAIR(BLUE)); | ||||
|                 } | ||||
|  | ||||
| @@ -399,11 +438,28 @@ void line_info_print(ToxWindow *self) | ||||
|  | ||||
|             case CONNECTION: | ||||
|                 wattron(win, COLOR_PAIR(BLUE)); | ||||
|                 wprintw(win, "%s", line->timestr); | ||||
|                 wprintw(win, "%s ", line->timestr); | ||||
|                 wattroff(win, COLOR_PAIR(BLUE)); | ||||
|  | ||||
|                 wattron(win, COLOR_PAIR(line->colour)); | ||||
|                 wprintw(win, "%s ", line->colour == RED ? "<--" : "-->"); | ||||
|                 wprintw(win, "%s ", user_settings->line_join); | ||||
|  | ||||
|                 wattron(win, A_BOLD); | ||||
|                 wprintw(win, "%s ", line->name1); | ||||
|                 wattroff(win, A_BOLD); | ||||
|  | ||||
|                 wprintw(win, "%s\n", line->msg); | ||||
|                 wattroff(win, COLOR_PAIR(line->colour)); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             case 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); | ||||
| @@ -416,11 +472,11 @@ void line_info_print(ToxWindow *self) | ||||
|  | ||||
|             case NAME_CHANGE: | ||||
|                 wattron(win, COLOR_PAIR(BLUE)); | ||||
|                 wprintw(win, "%s", line->timestr); | ||||
|                 wprintw(win, "%s ", line->timestr); | ||||
|                 wattroff(win, COLOR_PAIR(BLUE)); | ||||
|  | ||||
|                 wattron(win, COLOR_PAIR(MAGENTA)); | ||||
|                 wprintw(win, "-!- "); | ||||
|                 wprintw(win, "%s ", user_settings->line_alert); | ||||
|                 wattron(win, A_BOLD); | ||||
|                 wprintw(win, "%s", line->name1); | ||||
|                 wattroff(win, A_BOLD); | ||||
| @@ -467,14 +523,14 @@ static void line_info_scroll_up(struct history *hst) | ||||
| { | ||||
|     if (hst->line_start->prev) | ||||
|         hst->line_start = hst->line_start->prev; | ||||
|     else sound_notify(NULL, error, NT_ALWAYS, NULL); | ||||
|     else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||
| } | ||||
|  | ||||
| static void line_info_scroll_down(struct history *hst) | ||||
| { | ||||
|     if (hst->line_start->next) | ||||
|         hst->line_start = hst->line_start->next; | ||||
|     else sound_notify(NULL, error, NT_ALWAYS, NULL); | ||||
|     else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||
| } | ||||
|  | ||||
| static void line_info_page_up(ToxWindow *self, struct history *hst) | ||||
| @@ -506,24 +562,19 @@ bool line_info_onKey(ToxWindow *self, wint_t key) | ||||
|     struct history *hst = self->chatwin->hst; | ||||
|     bool match = true; | ||||
|  | ||||
| 	if (key == user_settings->key_half_page_up) { | ||||
| 		line_info_page_up(self, hst); | ||||
| 	} | ||||
| 	else if (key == user_settings->key_half_page_down) { | ||||
| 		line_info_page_down(self, hst); | ||||
| 	} | ||||
| 	else if (key == user_settings->key_scroll_line_up) { | ||||
| 		line_info_scroll_up(hst); | ||||
| 	} | ||||
| 	else if (key == user_settings->key_scroll_line_down) { | ||||
| 		line_info_scroll_down(hst); | ||||
| 	} | ||||
| 	else if (key == user_settings->key_page_bottom) { | ||||
| 		line_info_reset_start(self, hst); | ||||
| 	} | ||||
| 	else { | ||||
| 		match = false; | ||||
| 	} | ||||
|     if (key == user_settings->key_half_page_up) { | ||||
|         line_info_page_up(self, hst); | ||||
|     } else if (key == user_settings->key_half_page_down) { | ||||
|         line_info_page_down(self, hst); | ||||
|     } else if (key == user_settings->key_scroll_line_up) { | ||||
|         line_info_scroll_up(hst); | ||||
|     } else if (key == user_settings->key_scroll_line_down) { | ||||
|         line_info_scroll_down(hst); | ||||
|     } else if (key == user_settings->key_page_bottom) { | ||||
|         line_info_reset_start(self, hst); | ||||
|     } else { | ||||
|         match = false; | ||||
|     } | ||||
|  | ||||
|     return match; | ||||
| } | ||||
|   | ||||
| @@ -35,21 +35,24 @@ enum { | ||||
|     SYS_MSG, | ||||
|     IN_MSG, | ||||
|     OUT_MSG, | ||||
|     OUT_MSG_READ,    /* for sent messages that have received a read reply. don't set this with line_info_add */ | ||||
|     OUT_MSG_READ,    /* for sent messages that have received a read reply. */ | ||||
|     IN_ACTION, | ||||
|     OUT_ACTION, | ||||
|     OUT_ACTION_READ,     /* same as OUT_MSG_READ but for actions */ | ||||
|     IN_PRVT_MSG,   /* PRVT should only be used for groups */ | ||||
|     OUT_PRVT_MSG, | ||||
|     PROMPT, | ||||
|     CONNECTION, | ||||
|     DISCONNECTION, | ||||
|     NAME_CHANGE, | ||||
| } LINE_TYPE; | ||||
|  | ||||
| struct line_info { | ||||
|     char timestr[TIME_STR_SIZE]; | ||||
|     char name1[TOXIC_MAX_NAME_LENGTH]; | ||||
|     char name2[TOXIC_MAX_NAME_LENGTH]; | ||||
|     char name1[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|     char name2[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|     char msg[MAX_LINE_INFO_MSG_SIZE]; | ||||
|     uint64_t timestamp; | ||||
|     time_t timestamp; | ||||
|     uint8_t type; | ||||
|     uint8_t bold; | ||||
|     uint8_t colour; | ||||
| @@ -74,8 +77,8 @@ struct history { | ||||
| }; | ||||
|  | ||||
| /* creates new line_info line and puts it in the queue. */ | ||||
| void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uint8_t type, uint8_t bold,  | ||||
|                    uint8_t colour, const char *msg, ...); | ||||
| void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, | ||||
|                    uint8_t bold, uint8_t colour, const char *msg, ...); | ||||
|  | ||||
| /* Prints a section of history starting at line_start */ | ||||
| void line_info_print(ToxWindow *self); | ||||
|   | ||||
							
								
								
									
										23
									
								
								src/log.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/log.c
									
									
									
									
									
								
							| @@ -134,16 +134,14 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e | ||||
|     else | ||||
|         snprintf(name_frmt, sizeof(name_frmt), "%s:", name); | ||||
|  | ||||
|     const char *t = user_settings->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]"; | ||||
|     const char *t = user_settings->log_timestamp_format; | ||||
|     char s[MAX_STR_SIZE]; | ||||
|     strftime(s, MAX_STR_SIZE, t, get_time()); | ||||
|     fprintf(log->file, "%s %s %s\n", s, name_frmt, msg); | ||||
|  | ||||
|     uint64_t curtime = get_unix_time(); | ||||
|  | ||||
|     if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) { | ||||
|     if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) { | ||||
|         fflush(log->file); | ||||
|         log->lastwrite = curtime; | ||||
|         log->lastwrite = get_unix_time(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -155,15 +153,19 @@ void log_disable(struct chatlog *log) | ||||
|     memset(log, 0, sizeof(struct chatlog)); | ||||
| } | ||||
|  | ||||
| void log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) | ||||
| 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; | ||||
|         return 0; | ||||
|  | ||||
|     if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) | ||||
|     if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) { | ||||
|         log_disable(log); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Loads previous history from chat log */ | ||||
| @@ -177,7 +179,7 @@ void load_chat_history(ToxWindow *self, struct chatlog *log) | ||||
|     if (sz <= 0) | ||||
|         return; | ||||
|  | ||||
|     char *hstbuf = malloc(sz); | ||||
|     char *hstbuf = malloc(sz + 1); | ||||
|  | ||||
|     if (hstbuf == NULL) | ||||
|         exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY); | ||||
| @@ -194,6 +196,8 @@ void load_chat_history(ToxWindow *self, struct chatlog *log) | ||||
|         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; | ||||
| @@ -258,6 +262,7 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other | ||||
|     return 0; | ||||
|  | ||||
| on_error: | ||||
|  | ||||
|     if (log_on) | ||||
|         log_enable(src, selfkey, otherkey, log, LOG_CHAT); | ||||
|  | ||||
|   | ||||
							
								
								
									
										10
									
								
								src/log.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/log.h
									
									
									
									
									
								
							| @@ -25,7 +25,7 @@ | ||||
|  | ||||
| struct chatlog { | ||||
|     FILE *file; | ||||
|     uint64_t lastwrite; | ||||
|     time_t lastwrite; | ||||
|     char path[MAX_STR_SIZE]; | ||||
|     bool log_on;    /* specific to current chat window */ | ||||
| }; | ||||
| @@ -39,8 +39,12 @@ enum { | ||||
| /* formats/writes line to log file */ | ||||
| void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event); | ||||
|  | ||||
| /* enables logging for specified log and creates/fetches file if necessary */ | ||||
| void log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype); | ||||
| /* enables logging for specified log and creates/fetches file if necessary. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype); | ||||
|  | ||||
| /* disables logging for specified log and closes file */ | ||||
| void log_disable(struct chatlog *log); | ||||
|   | ||||
| @@ -42,7 +42,7 @@ void cqueue_cleanup(struct chat_queue *q) | ||||
|     free(q); | ||||
| } | ||||
|  | ||||
| void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id) | ||||
| void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id) | ||||
| { | ||||
|     struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg)); | ||||
|  | ||||
| @@ -103,7 +103,9 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | ||||
|         } | ||||
|  | ||||
|         char selfname[TOX_MAX_NAME_LENGTH]; | ||||
|         uint16_t len = tox_get_self_name(m, (uint8_t *) selfname); | ||||
|         tox_self_get_name(m, (uint8_t *) selfname); | ||||
|  | ||||
|         size_t len = tox_self_get_name_size(m); | ||||
|         selfname[len] = '\0'; | ||||
|  | ||||
|         write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION); | ||||
| @@ -127,30 +129,26 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #define CQUEUE_TRY_SEND_INTERVAL 10 | ||||
| #define CQUEUE_TRY_SEND_INTERVAL 60 | ||||
|  | ||||
| /* Tries to send the oldest unsent message in queue. */ | ||||
| void cqueue_try_send(ToxWindow *self, Tox *m) | ||||
| { | ||||
|     struct chat_queue *q = self->chatwin->cqueue; | ||||
|     struct cqueue_msg *msg = q->root; | ||||
|     uint64_t curtime = get_unix_time(); | ||||
|  | ||||
|     while (msg) { | ||||
|         if (msg->receipt != 0 && !timed_out(msg->last_send_try, curtime, CQUEUE_TRY_SEND_INTERVAL)) { | ||||
|             msg = msg->next; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         uint32_t receipt = 0; | ||||
|  | ||||
|         if (msg->type == OUT_MSG) | ||||
|             receipt = tox_send_message(m, self->num, (uint8_t *) msg->message, msg->len); | ||||
|         else | ||||
|             receipt = tox_send_action(m, self->num, (uint8_t *) msg->message, msg->len); | ||||
|  | ||||
|         msg->last_send_try = curtime; | ||||
|         msg->receipt = receipt; | ||||
|     if (!msg) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) | ||||
|         return; | ||||
|  | ||||
|     uint32_t receipt = 0; | ||||
|  | ||||
|     TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION; | ||||
|     receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL); | ||||
|  | ||||
|     msg->last_send_try = get_unix_time(); | ||||
|     msg->receipt = receipt; | ||||
|     return; | ||||
| } | ||||
|   | ||||
| @@ -25,11 +25,11 @@ | ||||
|  | ||||
| struct cqueue_msg { | ||||
|     char message[MAX_STR_SIZE]; | ||||
|     int len; | ||||
|     size_t len; | ||||
|     int line_id; | ||||
|     uint8_t type; | ||||
|     uint32_t receipt; | ||||
|     uint64_t last_send_try; | ||||
|     time_t last_send_try; | ||||
|     struct cqueue_msg *next; | ||||
|     struct cqueue_msg *prev; | ||||
| }; | ||||
| @@ -40,7 +40,7 @@ struct chat_queue { | ||||
| }; | ||||
|  | ||||
| void cqueue_cleanup(struct chat_queue *q); | ||||
| void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id); | ||||
| void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id); | ||||
|  | ||||
| /* Tries to send the oldest unsent message in queue. */ | ||||
| void cqueue_try_send(ToxWindow *self, Tox *m); | ||||
| @@ -48,4 +48,4 @@ 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 */ | ||||
| #endif  /* #define MESSAGE_QUEUE_H */ | ||||
|   | ||||
							
								
								
									
										293
									
								
								src/misc_tools.c
									
									
									
									
									
								
							
							
						
						
									
										293
									
								
								src/misc_tools.c
									
									
									
									
									
								
							| @@ -26,19 +26,23 @@ | ||||
| #include <time.h> | ||||
| #include <limits.h> | ||||
| #include <dirent.h> | ||||
| #if defined(__FreeBSD__) | ||||
| #include <netinet/in.h> | ||||
| #include <sys/socket.h> | ||||
| #else | ||||
| #include <arpa/inet.h> | ||||
| #endif | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "misc_tools.h" | ||||
| #include "settings.h" | ||||
| #include "file_senders.h" | ||||
| #include "file_transfers.h" | ||||
|  | ||||
| extern ToxWindow *prompt; | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| static uint64_t current_unix_time; | ||||
|  | ||||
| void hst_to_net(uint8_t *num, uint16_t numbytes) | ||||
| { | ||||
| #ifndef WORDS_BIGENDIAN | ||||
| @@ -54,32 +58,27 @@ void hst_to_net(uint8_t *num, uint16_t numbytes) | ||||
|     return; | ||||
| } | ||||
|  | ||||
| void update_unix_time(void) | ||||
| time_t get_unix_time(void) | ||||
| { | ||||
|     current_unix_time = (uint64_t) time(NULL); | ||||
| } | ||||
|  | ||||
| uint64_t get_unix_time(void) | ||||
| { | ||||
|     return current_unix_time; | ||||
|     return time(NULL); | ||||
| } | ||||
|  | ||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | ||||
| int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout) | ||||
| int timed_out(time_t timestamp, time_t timeout) | ||||
| { | ||||
|     return timestamp + timeout <= curtime; | ||||
|     return timestamp + timeout <= get_unix_time(); | ||||
| } | ||||
|  | ||||
| /* Get the current local time */ | ||||
| struct tm *get_time(void) | ||||
| { | ||||
|     struct tm *timeinfo; | ||||
|     uint64_t t = get_unix_time(); | ||||
|     timeinfo = localtime((const time_t*) &t); | ||||
|     time_t t = get_unix_time(); | ||||
|     timeinfo = localtime((const time_t *) &t); | ||||
|     return timeinfo; | ||||
| } | ||||
|  | ||||
| /*Puts the current time in buf in the format of [HH:mm:ss] */ | ||||
| /* Puts the current time in buf in the format of [HH:mm:ss] */ | ||||
| void get_time_str(char *buf, int bufsize) | ||||
| { | ||||
|     if (user_settings->timestamps == TIMESTAMPS_OFF) { | ||||
| @@ -87,12 +86,12 @@ void get_time_str(char *buf, int bufsize) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *t = user_settings->time == TIME_12 ? "%I:%M:%S " : "%H:%M:%S "; | ||||
|     const char *t = user_settings->timestamp_format; | ||||
|     strftime(buf, bufsize, t, get_time()); | ||||
| } | ||||
|  | ||||
| /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||
| void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs) | ||||
| void get_elapsed_time_str(char *buf, int bufsize, time_t secs) | ||||
| { | ||||
|     if (!secs) | ||||
|         return; | ||||
| @@ -109,20 +108,42 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs) | ||||
|         snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); | ||||
| } | ||||
|  | ||||
| char *hex_string_to_bin(const char *hex_string) | ||||
| /* Converts seconds to string in format H hours, m minutes, s seconds */ | ||||
| void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs) | ||||
| { | ||||
|     size_t len = strlen(hex_string); | ||||
|     char *val = malloc(len); | ||||
|     if (!secs) | ||||
|         return; | ||||
|  | ||||
|     if (val == NULL) | ||||
|         exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY); | ||||
|     long int seconds = secs % 60; | ||||
|     long int minutes = (secs % 3600) / 60; | ||||
|     long int hours = secs / 3600; | ||||
|  | ||||
|     size_t i; | ||||
|     if (!minutes && !hours) | ||||
|         snprintf(buf, bufsize, "%ld seconds", seconds); | ||||
|     else if (!hours) | ||||
|         snprintf(buf, bufsize, "%ld minutes, %ld seconds", minutes, seconds); | ||||
|     else | ||||
|         snprintf(buf, bufsize, "%ld hours, %ld minutes, %ld seconds", hours, minutes, seconds); | ||||
| } | ||||
|  | ||||
|     for (i = 0; i < len; ++i, hex_string += 2) | ||||
|         sscanf(hex_string, "%2hhx", &val[i]); | ||||
| /* | ||||
|  * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. | ||||
|  * output_size must be exactly half of hex_len. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size) | ||||
| { | ||||
|     if (output_size == 0 || hex_len != output_size * 2) | ||||
|         return -1; | ||||
|  | ||||
|     return val; | ||||
|     for (size_t i = 0; i < output_size; ++i) { | ||||
|         sscanf(hex_string, "%2hhx", &output[i]); | ||||
|         hex_string += 2; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int hex_string_to_bytes(char *buf, int size, const char *keystr) | ||||
| @@ -144,12 +165,42 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Converts a binary representation of a Tox ID into a string. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size) | ||||
| { | ||||
|     if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) | ||||
|         return -1; | ||||
|  | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < TOX_ADDRESS_SIZE; ++i) | ||||
|         snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Returns 1 if the string is empty, 0 otherwise */ | ||||
| int string_is_empty(const char *string) | ||||
| { | ||||
|     if (!string) | ||||
|         return true; | ||||
|  | ||||
|     return string[0] == '\0'; | ||||
| } | ||||
|  | ||||
| /* Returns 1 if the string is empty, 0 otherwise */ | ||||
| int wstring_is_empty(const wchar_t *string) | ||||
| { | ||||
|     if (!string) | ||||
|         return true; | ||||
|  | ||||
|     return string[0] == L'\0'; | ||||
| } | ||||
|  | ||||
| /* convert a multibyte string to a wide character string and puts in buf. */ | ||||
| int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | ||||
| { | ||||
| @@ -158,7 +209,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | ||||
|     if (n < len) | ||||
|         return -1; | ||||
|  | ||||
|     if ((len = mbstowcs(buf, string, n)) == (size_t) -1) | ||||
|     if ((len = mbstowcs(buf, string, n)) == (size_t) - 1) | ||||
|         return -1; | ||||
|  | ||||
|     return len; | ||||
| @@ -172,7 +223,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n) | ||||
|     if (n < len) | ||||
|         return -1; | ||||
|  | ||||
|     if ((len = wcstombs(buf, string, n)) == (size_t) -1) | ||||
|     if ((len = wcstombs(buf, string, n)) == (size_t) - 1) | ||||
|         return -1; | ||||
|  | ||||
|     return len; | ||||
| @@ -188,8 +239,9 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2) | ||||
|       - cannot be empty | ||||
|       - cannot start with a space | ||||
|       - must not contain a forward slash (for logfile naming purposes) | ||||
|       - must not contain contiguous spaces */ | ||||
| int valid_nick(char *nick) | ||||
|       - must not contain contiguous spaces | ||||
|       - must not contain a newline or tab seqeunce */ | ||||
| int valid_nick(const char *nick) | ||||
| { | ||||
|     if (!nick[0] || nick[0] == ' ') | ||||
|         return 0; | ||||
| @@ -197,27 +249,43 @@ int valid_nick(char *nick) | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; nick[i]; ++i) { | ||||
|         if (nick[i] == ' ' && nick[i + 1] == ' ') | ||||
|             return 0; | ||||
|         if ((nick[i] == ' ' && nick[i + 1] == ' ') | ||||
|                 || nick[i] == '/' | ||||
|                 || nick[i] == '\n' | ||||
|                 || nick[i] == '\t' | ||||
|                 || nick[i] == '\v' | ||||
|                 || nick[i] == '\r') | ||||
|  | ||||
|         if (nick[i] == '/') | ||||
|             return 0; | ||||
|     } | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| /* gets base file name from path or original file name if no path is supplied */ | ||||
| void get_file_name(char *namebuf, int bufsize, const char *pathname) | ||||
| /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | ||||
| void filter_str(char *str, size_t len) | ||||
| { | ||||
|     int idx = strlen(pathname) - 1; | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < len; ++i) { | ||||
|         if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') | ||||
|             str[i] = ' '; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* gets base file name from path or original file name if no path is supplied. | ||||
|  * Returns the file name length | ||||
|  */ | ||||
| size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname) | ||||
| { | ||||
|     int len = strlen(pathname) - 1; | ||||
|     char *path = strdup(pathname); | ||||
|  | ||||
|     if (path == NULL) | ||||
|         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); | ||||
|  | ||||
|     while (idx >= 0 && pathname[idx] == '/') | ||||
|         path[idx--] = '\0'; | ||||
|     while (len >= 0 && pathname[len] == '/') | ||||
|         path[len--] = '\0'; | ||||
|  | ||||
|     char *finalname = strdup(path); | ||||
|  | ||||
| @@ -234,6 +302,29 @@ void get_file_name(char *namebuf, int bufsize, const char *pathname) | ||||
|     snprintf(namebuf, bufsize, "%s", finalname); | ||||
|     free(finalname); | ||||
|     free(path); | ||||
|  | ||||
|     return strlen(namebuf); | ||||
| } | ||||
|  | ||||
| /* Gets the base directory of path and puts it in dir. | ||||
|  * dir must have at least as much space as path_len + 1. | ||||
|  * | ||||
|  * Returns the length of the base directory. | ||||
|  */ | ||||
| size_t get_base_dir(const char *path, size_t path_len, char *dir) | ||||
| { | ||||
|     if (path_len == 0 || path == NULL) | ||||
|         return 0; | ||||
|  | ||||
|     size_t dir_len = char_rfind(path, '/', path_len); | ||||
|  | ||||
|     if (dir_len != 0 && dir_len < path_len) | ||||
|         ++dir_len;  /* Leave trailing slash */ | ||||
|  | ||||
|     memcpy(dir, path, dir_len); | ||||
|     dir[dir_len] = '\0'; | ||||
|  | ||||
|     return dir_len; | ||||
| } | ||||
|  | ||||
| /* converts str to all lowercase */ | ||||
| @@ -246,19 +337,72 @@ void str_to_lower(char *str) | ||||
| } | ||||
|  | ||||
| /* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary. | ||||
|    Returns nick len on success, -1 on failure */ | ||||
| int get_nick_truncate(Tox *m, char *buf, int friendnum) | ||||
|    if toxcore API call fails, put UNKNOWN_NAME in buf | ||||
|    Returns nick len */ | ||||
| size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum) | ||||
| { | ||||
|     int len = tox_get_name(m, friendnum, (uint8_t *) buf); | ||||
|     size_t len = tox_friend_get_name_size(m, friendnum, NULL); | ||||
|  | ||||
|     if (len == 0) { | ||||
|         strcpy(buf, UNKNOWN_NAME); | ||||
|         len = strlen(UNKNOWN_NAME); | ||||
|     } else { | ||||
|         tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL); | ||||
|     } | ||||
|  | ||||
|     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||
|     buf[len] = '\0'; | ||||
|     filter_str(buf, len); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| /* same as get_nick_truncate but for groupchats */ | ||||
| int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum) | ||||
| { | ||||
|     TOX_ERR_GROUP_PEER_QUERY err; | ||||
|     size_t len = tox_group_peer_get_name_size(m, groupnum, peer_id, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_GROUP_PEER_QUERY_OK) { | ||||
|         strcpy(buf, UNKNOWN_NAME); | ||||
|         len = strlen(UNKNOWN_NAME); | ||||
|     } else { | ||||
|         tox_group_peer_get_name(m, groupnum, peer_id, (uint8_t *) buf, &err); | ||||
|  | ||||
|         if (err != TOX_ERR_GROUP_PEER_QUERY_OK) { | ||||
|             strcpy(buf, UNKNOWN_NAME); | ||||
|             len = strlen(UNKNOWN_NAME); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||
|     buf[len] = '\0'; | ||||
|     filter_str(buf, len); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| /* copies data to msg buffer. | ||||
|    returns length of msg. | ||||
|    returns 0 and nulls msg if length is too big for buffer size */ | ||||
| size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length) | ||||
| { | ||||
|     if (length > size - 1) { | ||||
|         msg[0] = '\0'; | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     memcpy(msg, data, length); | ||||
|     msg[length] = '\0'; | ||||
|     return length; | ||||
| } | ||||
|  | ||||
| /* returns index of the first instance of ch in s starting at idx. | ||||
|    returns length of s if char not found */ | ||||
|    returns length of s if char not found or 0 if s is NULL. */ | ||||
| int char_find(int idx, const char *s, char ch) | ||||
| { | ||||
|     if (!s) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     int i = idx; | ||||
|  | ||||
|     for (i = idx; s[i]; ++i) { | ||||
| @@ -269,10 +413,14 @@ int char_find(int idx, const char *s, char ch) | ||||
|     return i; | ||||
| } | ||||
|  | ||||
| /* returns index of the last instance of ch in s starting at len | ||||
|    returns 0 if char not found (skips 0th index) */ | ||||
| /* returns index of the last instance of ch in s starting at len. | ||||
|    returns 0 if char not found or s is NULL (skips 0th index). */ | ||||
| int char_rfind(const char *s, char ch, int len) | ||||
| { | ||||
|     if (!s) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     int i = 0; | ||||
|  | ||||
|     for (i = len; i > 0; --i) { | ||||
| @@ -312,18 +460,18 @@ bool file_exists(const char *path) | ||||
|     return stat(path, &s) == 0; | ||||
| } | ||||
|  | ||||
| /* returns file size or -1 on error */ | ||||
| /* 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 -1; | ||||
|         return 0; | ||||
|  | ||||
|     return st.st_size; | ||||
| } | ||||
|  | ||||
| /* compares the first size bytes of fp to signature.  | ||||
| /* 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 */ | ||||
| @@ -341,3 +489,54 @@ int check_file_signature(const char *signature, size_t size, FILE *fp) | ||||
|  | ||||
|     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; | ||||
| } | ||||
|   | ||||
| @@ -41,30 +41,46 @@ | ||||
|  | ||||
| void hst_to_net(uint8_t *num, uint16_t numbytes); | ||||
|  | ||||
| /* convert a hex string to binary */ | ||||
| char *hex_string_to_bin(const char *hex_string); | ||||
| /* | ||||
|  * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. | ||||
|  * output_size must be exactly half of hex_len. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size); | ||||
|  | ||||
| /* convert a hex string to bytes. returns 0 on success, -1 on failure */ | ||||
| int hex_string_to_bytes(char *buf, int size, const char *keystr); | ||||
|  | ||||
| /* get the current unix time */ | ||||
| uint64_t get_unix_time(void); | ||||
| /* Converts a binary representation of a Tox ID into a string. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size); | ||||
|  | ||||
| /* Puts the current time in buf in the format of [HH:mm:ss] */ | ||||
| /* get the current unix time (not thread safe) */ | ||||
| time_t get_unix_time(void); | ||||
|  | ||||
| /* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */ | ||||
| void get_time_str(char *buf, int bufsize); | ||||
|  | ||||
| /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||
| void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs); | ||||
| void get_elapsed_time_str(char *buf, int bufsize, time_t secs); | ||||
|  | ||||
| /* get the current local time */ | ||||
| /* Converts seconds to string in format H hours, m minutes, s seconds */ | ||||
| void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs); | ||||
|  | ||||
| /* get the current local time (not thread safe) */ | ||||
| struct tm *get_time(void); | ||||
|  | ||||
| /* updates current unix time (should be run once per do_toxic loop) */ | ||||
| void update_unix_time(void); | ||||
|  | ||||
| /* Returns 1 if the string is empty, 0 otherwise */ | ||||
| int string_is_empty(const char *string); | ||||
|  | ||||
| /* Same as above but for wide character strings */ | ||||
| int wstring_is_empty(const wchar_t *string); | ||||
|  | ||||
| /* convert a multibyte string to a wide character string (must provide buffer) */ | ||||
| int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||
|  | ||||
| @@ -75,7 +91,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n); | ||||
| int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||
|  | ||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | ||||
| int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime); | ||||
| int timed_out(time_t timestamp, time_t timeout); | ||||
|  | ||||
| /* Colours the window tab according to type. Beeps if is_beep is true */ | ||||
| void alert_window(ToxWindow *self, int type, bool is_beep); | ||||
| @@ -87,25 +103,45 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2); | ||||
|       - cannot be empty | ||||
|       - cannot start with a space | ||||
|       - must not contain a forward slash (for logfile naming purposes) | ||||
|       - must not contain contiguous spaces */ | ||||
| int valid_nick(char *nick); | ||||
|       - must not contain contiguous spaces | ||||
|       - must not contain a newline or tab seqeunce */ | ||||
| int valid_nick(const char *nick); | ||||
|  | ||||
| /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | ||||
| void filter_str(char *str, size_t len); | ||||
|  | ||||
| /* gets base file name from path or original file name if no path is supplied */ | ||||
| void get_file_name(char *namebuf, int bufsize, const char *pathname); | ||||
| size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname); | ||||
|  | ||||
| /* Gets the base directory of path and puts it in dir. | ||||
|  * dir must have at least as much space as path_len. | ||||
|  * | ||||
|  * Returns the length of the base directory on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| size_t get_base_dir(const char *path, size_t path_len, char *dir); | ||||
|  | ||||
| /* converts str to all lowercase */ | ||||
| void str_to_lower(char *str); | ||||
|  | ||||
| /* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary. | ||||
|    Returns nick len on success, -1 on failure */ | ||||
| int get_nick_truncate(Tox *m, char *buf, int friendnum); | ||||
| size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | ||||
|  | ||||
| /* same as get_nick_truncate but for groupchats */ | ||||
| int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum); | ||||
|  | ||||
| /* copies data to msg buffer. | ||||
|    returns length of msg. | ||||
|    returns 0 and nulls msg if length is too big for buffer size */ | ||||
| size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length); | ||||
|  | ||||
| /* returns index of the first instance of ch in s starting at idx. | ||||
|    returns length of s if char not found */ | ||||
|    returns length of s if char not found or 0 if s is NULL. */ | ||||
| int char_find(int idx, const char *s, char ch); | ||||
|  | ||||
| /* returns index of the last instance of ch in s | ||||
|    returns 0 if char not found */ | ||||
| /* returns index of the last instance of ch in s starting at len. | ||||
|    returns 0 if char not found or s is NULL (skips 0th index). */ | ||||
| int char_rfind(const char *s, char ch, int len); | ||||
|  | ||||
| /* Converts bytes to appropriate unit and puts in buf as a string */ | ||||
| @@ -114,13 +150,28 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes); | ||||
| /* checks if a file exists. Returns true or false */ | ||||
| bool file_exists(const char *path); | ||||
|  | ||||
| /* returns file size or -1 on error */ | ||||
| /* 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.  | ||||
| /* 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 */ | ||||
							
								
								
									
										456
									
								
								src/notify.c
									
									
									
									
									
								
							
							
						
						
									
										456
									
								
								src/notify.c
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| /*  notify.c | ||||
|  *  | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
| @@ -31,34 +31,31 @@ | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #include "notify.h" | ||||
| #include "device.h" | ||||
| #include "audio_device.h" | ||||
| #include "settings.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "xtra.h" | ||||
|  | ||||
| #if defined(AUDIO) || defined(SOUND_NOTIFY) | ||||
|     #ifdef __APPLE__ | ||||
|         #include <OpenAL/al.h> | ||||
|         #include <OpenAL/alc.h> | ||||
|     #else | ||||
|         #include <AL/al.h> | ||||
|         #include <AL/alc.h> | ||||
|         /* compatibility with older versions of OpenAL */ | ||||
|         #ifndef ALC_ALL_DEVICES_SPECIFIER | ||||
|             #include <AL/alext.h> | ||||
|         #endif | ||||
|     #endif | ||||
|     #ifdef SOUND_NOTIFY | ||||
|         #include <AL/alut.h> /* freealut packet */ | ||||
|     #endif | ||||
| #ifdef __APPLE__ | ||||
| #include <OpenAL/al.h> | ||||
| #include <OpenAL/alc.h> | ||||
| #else | ||||
| #include <AL/al.h> | ||||
| #include <AL/alc.h> | ||||
| /* compatibility with older versions of OpenAL */ | ||||
| #ifndef ALC_ALL_DEVICES_SPECIFIER | ||||
| #include <AL/alext.h> | ||||
| #endif | ||||
| #endif | ||||
| #ifdef SOUND_NOTIFY | ||||
| #include <AL/alut.h> /* freealut packet */ | ||||
| #endif | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #ifdef X11 | ||||
|     #include <X11/Xlib.h> | ||||
| #endif /* X11 */ | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
|     #include <libnotify/notify.h> | ||||
| #include <libnotify/notify.h> | ||||
| #endif | ||||
|  | ||||
| #define MAX_BOX_MSG_LEN 127 | ||||
| @@ -70,19 +67,15 @@ extern struct user_settings *user_settings; | ||||
| struct Control { | ||||
|     time_t cooldown; | ||||
|     time_t notif_timeout; | ||||
| #ifdef X11 | ||||
|     Display *display; | ||||
|     unsigned long this_window; | ||||
| #endif /* X11 */ | ||||
|      | ||||
|  | ||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||
|     pthread_mutex_t poll_mutex[1]; | ||||
|     bool poll_active; | ||||
| #endif | ||||
|      | ||||
|  | ||||
| #ifdef SOUND_NOTIFY | ||||
|     uint32_t device_idx; /* index of output device */ | ||||
|     char* sounds[SOUNDS_SIZE]; | ||||
|     char *sounds[SOUNDS_SIZE]; | ||||
| #endif /* SOUND_NOTIFY */ | ||||
| } Control = {0}; | ||||
|  | ||||
| @@ -95,13 +88,13 @@ struct _ActiveNotifications { | ||||
|     bool active; | ||||
|     int *id_indicator; | ||||
| #ifdef BOX_NOTIFY | ||||
|     NotifyNotification* box; | ||||
|     NotifyNotification *box; | ||||
|     char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1]; | ||||
|     char title[24]; | ||||
|     char title[64]; | ||||
|     size_t size; | ||||
|     time_t n_timeout; | ||||
| #endif | ||||
| } actives[ACTIVE_NOTIFS_MAX] = {{0}}; | ||||
| } actives[ACTIVE_NOTIFS_MAX]; | ||||
| /**********************************************************************************/ | ||||
| /**********************************************************************************/ | ||||
| /**********************************************************************************/ | ||||
| @@ -114,31 +107,22 @@ static void tab_notify(ToxWindow *self, uint64_t flags) | ||||
|     if (self == NULL) | ||||
|         return; | ||||
|  | ||||
|     if (flags & NT_WNDALERT_0)  | ||||
|     if (flags & NT_WNDALERT_0) | ||||
|         self->alert = WINDOW_ALERT_0; | ||||
|     else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) )  | ||||
|     else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) ) | ||||
|         self->alert = WINDOW_ALERT_1; | ||||
|     else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) )  | ||||
|     else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) ) | ||||
|         self->alert = WINDOW_ALERT_2; | ||||
| } | ||||
|  | ||||
| #ifdef X11 | ||||
| long unsigned int get_focused_window_id() | ||||
| { | ||||
|     if (!Control.display) return 0; | ||||
|      | ||||
|     Window focus; | ||||
|     int revert; | ||||
|     XGetInputFocus(Control.display, &focus, &revert); | ||||
|     return focus; | ||||
| } | ||||
| #endif /* X11 */ | ||||
|  | ||||
| static bool notifications_are_disabled(uint64_t flags) | ||||
| { | ||||
|     bool res = flags & NT_RESTOL && Control.cooldown > get_unix_time(); | ||||
|     if (user_settings->alerts != ALERTS_ENABLED) | ||||
|         return true; | ||||
|  | ||||
|     bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time()); | ||||
| #ifdef X11 | ||||
|     return res || (flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()); | ||||
|     return res || ((flags & NT_NOFOCUS) && is_focused()); | ||||
| #else | ||||
|     return res; | ||||
| #endif | ||||
| @@ -172,165 +156,199 @@ bool is_playing(int source) | ||||
| static bool device_opened = false; | ||||
| time_t last_opened_update = 0; | ||||
|  | ||||
| bool m_open_device() | ||||
| {     | ||||
|     last_opened_update = time(NULL); | ||||
|      | ||||
|     if (device_opened) return true; | ||||
|      | ||||
| /* Opens primary device. Returns true on succe*/ | ||||
| void m_open_device() | ||||
| { | ||||
|     last_opened_update = get_unix_time(); | ||||
|  | ||||
|     if (device_opened) return; | ||||
|  | ||||
|     /* Blah error check */ | ||||
|     open_primary_device(output, &Control.device_idx, 48000, 20, 1); | ||||
|      | ||||
|     return (device_opened = true); | ||||
|  | ||||
|     device_opened = true; | ||||
| } | ||||
|  | ||||
| bool m_close_device() | ||||
| void m_close_device() | ||||
| { | ||||
|     if (!device_opened) return true; | ||||
|      | ||||
|     if (!device_opened) return; | ||||
|  | ||||
|     close_device(output, Control.device_idx); | ||||
|      | ||||
|     return !(device_opened = false); | ||||
|  | ||||
|     device_opened = false; | ||||
| } | ||||
|  | ||||
| /* Terminate all sounds but wait for them to finish first */ | ||||
| void graceful_clear() | ||||
| { | ||||
|     int i; | ||||
|     control_lock(); | ||||
|  | ||||
|     while (1) { | ||||
|         int i; | ||||
|  | ||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||
|             if (actives[i].active) { | ||||
|             #ifdef BOX_NOTIFY | ||||
| #ifdef BOX_NOTIFY | ||||
|  | ||||
|                 if (actives[i].box) { | ||||
|                     GError* ignore; | ||||
|                     GError *ignore; | ||||
|                     notify_notification_close(actives[i].box, &ignore); | ||||
|                     actives[i].box = NULL; | ||||
|                 } | ||||
|             #endif | ||||
|              | ||||
|                 if(actives[i].id_indicator) *actives[i].id_indicator = -1; // reset indicator value | ||||
|                  | ||||
|  | ||||
| #endif | ||||
|  | ||||
|                 if (actives[i].id_indicator) | ||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|  | ||||
|                 if ( actives[i].looping ) { | ||||
|                     stop_sound(i);  | ||||
|                     stop_sound(i); | ||||
|                 } else { | ||||
|                     if (!is_playing(actives[i].source))  | ||||
|                     if (!is_playing(actives[i].source)) | ||||
|                         memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|                     else break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  | ||||
|         if (i == ACTIVE_NOTIFS_MAX) { | ||||
|             m_close_device(); /* In case it's opened */ | ||||
|             control_unlock(); | ||||
|             return; | ||||
|         } | ||||
|              | ||||
|  | ||||
|         usleep(1000); | ||||
|     } | ||||
|  | ||||
|     control_unlock(); | ||||
| } | ||||
|  | ||||
| void* do_playing(void* _p) | ||||
| void *do_playing(void *_p) | ||||
| { | ||||
|     (void)_p; | ||||
|     int i; | ||||
|      | ||||
|     bool has_looping = false; | ||||
|      | ||||
|     while(Control.poll_active) { | ||||
|  | ||||
|     while (true) { | ||||
|         control_lock(); | ||||
|          | ||||
|          | ||||
|  | ||||
|         if (!Control.poll_active) { | ||||
|             control_unlock(); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         bool has_looping = false; | ||||
|         bool test_active_notify = false; | ||||
|         int i; | ||||
|  | ||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||
|              | ||||
|  | ||||
|             if (actives[i].looping) has_looping = true; | ||||
|              | ||||
|             if (actives[i].active && !actives[i].looping | ||||
|                 #ifdef BOX_NOTIFY | ||||
|                     && !actives[i].box | ||||
|                 #endif | ||||
|             ) { | ||||
|  | ||||
|             test_active_notify = actives[i].active && !actives[i].looping; | ||||
| #ifdef BOX_NOTIFY | ||||
|             test_active_notify = test_active_notify && !actives[i].box; | ||||
| #endif | ||||
|  | ||||
|             if (test_active_notify) { | ||||
|                 if (actives[i].id_indicator) | ||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|  | ||||
|                 if (!is_playing(actives[i].source)) { | ||||
|                 /* Close */                     | ||||
|                     /* Close */ | ||||
|                     alSourceStop(actives[i].source); | ||||
|                     alDeleteSources(1, &actives[i].source); | ||||
|                     alDeleteBuffers(1,&actives[i].buffer); | ||||
|                     alDeleteBuffers(1, &actives[i].buffer); | ||||
|                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|                 } | ||||
|             } | ||||
|         #ifdef BOX_NOTIFY | ||||
|             else if (actives[i].box && time(NULL) >= actives[i].n_timeout) | ||||
|             { | ||||
|                 GError* ignore; | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
|             else if (actives[i].box && time(NULL) >= actives[i].n_timeout) { | ||||
|                 GError *ignore; | ||||
|                 notify_notification_close(actives[i].box, &ignore); | ||||
|                 actives[i].box = NULL; | ||||
|                  | ||||
|                 if(actives[i].id_indicator) *actives[i].id_indicator = -1; // reset indicator value | ||||
|  | ||||
|                 if (actives[i].id_indicator) | ||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|  | ||||
|                 if (!actives[i].looping && !is_playing(actives[i].source)) { | ||||
|                 /* stop source if not looping or playing, just terminate box */ | ||||
|                     /* stop source if not looping or playing, just terminate box */ | ||||
|                     alSourceStop(actives[i].source); | ||||
|                     alDeleteSources(1, &actives[i].source); | ||||
|                     alDeleteBuffers(1,&actives[i].buffer); | ||||
|                     alDeleteBuffers(1, &actives[i].buffer); | ||||
|                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|                 } | ||||
|             } | ||||
|         #endif | ||||
|  | ||||
| #endif | ||||
|         } | ||||
|          | ||||
|  | ||||
|         /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/ | ||||
|         if (device_opened && !has_looping &&  | ||||
|            (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) { | ||||
|         if (device_opened && !has_looping && | ||||
|                 (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) { | ||||
|             m_close_device(); | ||||
|         } | ||||
|  | ||||
|         has_looping = false; | ||||
|          | ||||
|  | ||||
|         control_unlock(); | ||||
|         usleep(10000); | ||||
|     } | ||||
|  | ||||
|     pthread_exit(NULL); | ||||
| } | ||||
|  | ||||
| int play_source(uint32_t source, uint32_t buffer, bool looping) | ||||
| {     | ||||
| { | ||||
|     int i = 0; | ||||
|  | ||||
|     for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++); | ||||
|  | ||||
|     if ( i == ACTIVE_NOTIFS_MAX ) { | ||||
|         return -1; /* Full */ | ||||
|     } | ||||
|      | ||||
|  | ||||
|     alSourcePlay(source); | ||||
|      | ||||
|  | ||||
|     actives[i].active = 1; | ||||
|     actives[i].source = source; | ||||
|     actives[i].buffer = buffer; | ||||
|     actives[i].looping = looping; | ||||
|      | ||||
|  | ||||
|     return i; | ||||
| } | ||||
|  | ||||
| #elif BOX_NOTIFY | ||||
| void* do_playing(void* _p) | ||||
| void *do_playing(void *_p) | ||||
| { | ||||
|     (void)_p; | ||||
|     int i; | ||||
|     while(Control.poll_active) { | ||||
|  | ||||
|     while (true) { | ||||
|         control_lock(); | ||||
|  | ||||
|         if (!Control.poll_active) { | ||||
|             control_unlock(); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         int i; | ||||
|  | ||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||
|             if (actives[i].box && time(NULL) >= actives[i].n_timeout) | ||||
|             { | ||||
|                 GError* ignore; | ||||
|             if (actives[i].box && time(NULL) >= actives[i].n_timeout) { | ||||
|                 GError *ignore; | ||||
|                 notify_notification_close(actives[i].box, &ignore); | ||||
|                 actives[i].box = NULL; | ||||
|                  | ||||
|                 if(actives[i].id_indicator) *actives[i].id_indicator = -1; // reset indicator value | ||||
|  | ||||
|                 if (actives[i].id_indicator) | ||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|  | ||||
|                 memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         control_unlock(); | ||||
|         usleep(10000); | ||||
|     } | ||||
|  | ||||
|     pthread_exit(NULL); | ||||
| } | ||||
|  | ||||
| @@ -338,18 +356,20 @@ void graceful_clear() | ||||
| { | ||||
|     int i; | ||||
|     control_lock(); | ||||
|      | ||||
|  | ||||
|     for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||
|         if (actives[i].box) { | ||||
|             GError* ignore; | ||||
|             GError *ignore; | ||||
|             notify_notification_close(actives[i].box, &ignore); | ||||
|             actives[i].box = NULL; | ||||
|         } | ||||
|          | ||||
|         if(actives[i].id_indicator) *actives[i].id_indicator = -1; // reset indicator value | ||||
|  | ||||
|         if (actives[i].id_indicator) | ||||
|             *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|  | ||||
|         memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|     }    | ||||
|      | ||||
|     } | ||||
|  | ||||
|     control_unlock(); | ||||
| } | ||||
| #endif | ||||
| @@ -368,59 +388,66 @@ int init_notify(int login_cooldown, int notification_timeout) | ||||
| #ifdef SOUND_NOTIFY | ||||
|     alutInitWithoutContext(NULL, NULL); | ||||
| #endif /* SOUND_NOTIFY */ | ||||
|          | ||||
|  | ||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||
|  | ||||
|     if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) | ||||
|         return -1; | ||||
|  | ||||
|     Control.poll_active = 1; | ||||
|     pthread_t thread; | ||||
|  | ||||
|     if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) { | ||||
|         pthread_mutex_destroy(Control.poll_mutex); | ||||
|         Control.poll_active = 0; | ||||
|         return -1; | ||||
|     } | ||||
|     Control.poll_active = 1; | ||||
|  | ||||
| #endif | ||||
|      | ||||
|     Control.cooldown = time(NULL) + login_cooldown; | ||||
| #ifdef X11 | ||||
|     Control.display = XOpenDisplay(NULL); | ||||
|     Control.this_window = get_focused_window_id(); | ||||
| #endif /* X11 */ | ||||
|      | ||||
|      | ||||
|  | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
|     notify_init("toxic"); | ||||
|     notify_init("Toxic"); | ||||
| #endif | ||||
|     Control.notif_timeout = notification_timeout; | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| void terminate_notify() | ||||
| {     | ||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)     | ||||
|     if ( !Control.poll_active ) return;     | ||||
| { | ||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||
|     control_lock(); | ||||
|  | ||||
|     if ( !Control.poll_active ) { | ||||
|         control_unlock(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Control.poll_active = 0; | ||||
|      | ||||
|     control_unlock(); | ||||
|  | ||||
|     graceful_clear(); | ||||
| #endif | ||||
|  | ||||
| #ifdef SOUND_NOTIFY | ||||
|     int i = 0; | ||||
|  | ||||
|     for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]); | ||||
|  | ||||
|     alutExit(); | ||||
| #endif /* SOUND_NOTIFY */ | ||||
|      | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
|     notify_uninit(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #ifdef SOUND_NOTIFY | ||||
| int set_sound(Notification sound, const char* value) | ||||
| int set_sound(Notification sound, const char *value) | ||||
| { | ||||
|     if (sound == silent) return 0; | ||||
|      | ||||
|  | ||||
|     free(Control.sounds[sound]); | ||||
|  | ||||
|     size_t len = strlen(value) + 1; | ||||
| @@ -432,26 +459,27 @@ int set_sound(Notification sound, const char* value) | ||||
| } | ||||
|  | ||||
| int play_sound_internal(Notification what, bool loop) | ||||
| {         | ||||
| { | ||||
|     uint32_t source; | ||||
|     uint32_t buffer; | ||||
|      | ||||
|  | ||||
|     m_open_device(); | ||||
|      | ||||
|  | ||||
|     alGenSources(1, &source); | ||||
|     alGenBuffers(1, &buffer); | ||||
|     buffer = alutCreateBufferFromFile(Control.sounds[what]); | ||||
|     alSourcei(source, AL_BUFFER, buffer); | ||||
|     alSourcei(source, AL_LOOPING, loop); | ||||
|      | ||||
|  | ||||
|     int rc = play_source(source, buffer, loop); | ||||
|  | ||||
|     if (rc < 0) { | ||||
|         alSourceStop(source); | ||||
|         alDeleteSources(1, &source); | ||||
|         alDeleteBuffers(1,&buffer); | ||||
|         alDeleteBuffers(1, &buffer); | ||||
|         return -1; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     return rc; | ||||
| } | ||||
|  | ||||
| @@ -460,7 +488,8 @@ int play_notify_sound(Notification notif, uint64_t flags) | ||||
|     int rc = -1; | ||||
|  | ||||
|     if (flags & NT_BEEP) beep(); | ||||
|     else if (notif != silent) { | ||||
|  | ||||
|     if (notif != silent) { | ||||
|         if ( !Control.poll_active || !Control.sounds[notif] ) | ||||
|             return -1; | ||||
|  | ||||
| @@ -475,16 +504,21 @@ void stop_sound(int id) | ||||
| { | ||||
|     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) { | ||||
| #ifdef BOX_NOTIFY | ||||
|  | ||||
|         if (actives[id].box) { | ||||
|             GError* ignore; | ||||
|             GError *ignore; | ||||
|             notify_notification_close(actives[id].box, &ignore); | ||||
|         } | ||||
|  | ||||
| #endif | ||||
|         *actives[id].id_indicator = -1; | ||||
|  | ||||
|         if (actives[id].id_indicator) | ||||
|             *actives[id].id_indicator = -1; | ||||
|  | ||||
| //         alSourcei(actives[id].source, AL_LOOPING, false); | ||||
|         alSourceStop(actives[id].source); | ||||
|         alDeleteSources(1, &actives[id].source); | ||||
|         alDeleteBuffers(1,&actives[id].buffer); | ||||
|         alDeleteBuffers(1, &actives[id].buffer); | ||||
|         memset(&actives[id], 0, sizeof(struct _ActiveNotifications)); | ||||
|     } | ||||
| } | ||||
| @@ -495,100 +529,103 @@ static int m_play_sound(Notification notif, uint64_t flags) | ||||
| #ifdef SOUND_NOTIFY | ||||
|     return play_notify_sound(notif, flags); | ||||
| #else | ||||
|  | ||||
|     if (notif != silent) | ||||
|         beep(); | ||||
|  | ||||
|     return -1; | ||||
| #endif /* SOUND_NOTIFY */    | ||||
| #endif /* SOUND_NOTIFY */ | ||||
| } | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
| void m_notify_action(NotifyNotification *box, char *action, void* data)  | ||||
| void m_notify_action(NotifyNotification *box, char *action, void *data) | ||||
| { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator) | ||||
| int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator) | ||||
| { | ||||
|     tab_notify(self, flags); | ||||
|  | ||||
|     if (notifications_are_disabled(flags)) | ||||
|         return -1; | ||||
|      | ||||
|  | ||||
|     int id = -1; | ||||
|     control_lock(); | ||||
|      | ||||
|     if (self && (!self->stb || self->stb->status != TOX_USERSTATUS_BUSY) && user_settings->alerts == ALERTS_ENABLED)  | ||||
|  | ||||
|     if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY)) | ||||
|         id = m_play_sound(notif, flags); | ||||
|      | ||||
|     else if (flags & NT_ALWAYS) | ||||
|         id = m_play_sound(notif, flags); | ||||
|      | ||||
|  | ||||
| #if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) | ||||
|  | ||||
|     if (id == -1) { | ||||
|         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id ++); | ||||
|         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++); | ||||
|  | ||||
|         if ( id == ACTIVE_NOTIFS_MAX ) { | ||||
|             control_unlock(); | ||||
|             return -1; /* Full */ | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
| #endif | ||||
|      | ||||
|     if ( id_indicator && id != -1 ) {  | ||||
|  | ||||
|     if ( id_indicator && id != -1 ) { | ||||
|         actives[id].id_indicator = id_indicator; | ||||
|         *id_indicator = id; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     control_unlock(); | ||||
|      | ||||
|  | ||||
|     return id; | ||||
| } | ||||
|  | ||||
| int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id) | ||||
| int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id) | ||||
| { | ||||
|     tab_notify(self, flags); | ||||
|      | ||||
|  | ||||
|     if (notifications_are_disabled(flags)) | ||||
|         return -1; | ||||
|      | ||||
|  | ||||
|     if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1; | ||||
| #ifdef SOUND_NOTIFY     | ||||
|  | ||||
| #ifdef SOUND_NOTIFY | ||||
|     control_lock(); | ||||
|      | ||||
|  | ||||
|     if (!actives[id].active || !Control.sounds[notif]) { | ||||
|         control_unlock(); | ||||
|         return -1; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     m_open_device(); | ||||
|      | ||||
|  | ||||
|     alSourceStop(actives[id].source); | ||||
|     alDeleteSources(1, &actives[id].source); | ||||
|     alDeleteBuffers(1,&actives[id].buffer); | ||||
|      | ||||
|      | ||||
|     alDeleteBuffers(1, &actives[id].buffer); | ||||
|  | ||||
|     alGenSources(1, &actives[id].source); | ||||
|     alGenBuffers(1, &actives[id].buffer); | ||||
|     actives[id].buffer = alutCreateBufferFromFile(Control.sounds[notif]); | ||||
|     alSourcei(actives[id].source, AL_BUFFER, actives[id].buffer); | ||||
|     alSourcei(actives[id].source, AL_LOOPING, flags & NT_LOOP); | ||||
|      | ||||
|  | ||||
|     alSourcePlay(actives[id].source); | ||||
|      | ||||
|  | ||||
|     control_unlock(); | ||||
|      | ||||
|  | ||||
|     return id; | ||||
| #else | ||||
|  | ||||
|     if (notif != silent) | ||||
|         beep(); | ||||
|      | ||||
|  | ||||
|     return 0; | ||||
| #endif /* SOUND_NOTIFY */ | ||||
| } | ||||
|  | ||||
| int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, char* title, const char* format, ...) | ||||
| int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title, | ||||
|                const char *format, ...) | ||||
| { | ||||
|     if (notifications_are_disabled(flags)) { | ||||
|         tab_notify(self, flags); | ||||
| @@ -602,34 +639,45 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi | ||||
|     control_lock(); | ||||
|  | ||||
| #ifdef SOUND_NOTIFY | ||||
|  | ||||
|     if (id == -1) { /* Could not play */ | ||||
|  | ||||
|         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); | ||||
|  | ||||
|         if ( id == ACTIVE_NOTIFS_MAX ) { | ||||
|             control_unlock(); | ||||
|             return -1; /* Full */ | ||||
|         } | ||||
|  | ||||
|         actives[id].active = 1;     | ||||
|         actives[id].active = 1; | ||||
|         actives[id].id_indicator = id_indicator; | ||||
|  | ||||
|         if (id_indicator) *id_indicator = id; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     strncpy(actives[id].title, title, 24); | ||||
| #else | ||||
|  | ||||
|     if (id == -1) | ||||
|         return -1; | ||||
|  | ||||
| #endif    /* SOUND_NOTIFY */ | ||||
|  | ||||
|     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); | ||||
|  | ||||
|     if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); | ||||
|  | ||||
|     va_list __ARGS__; va_start (__ARGS__, format); | ||||
|     va_list __ARGS__; | ||||
|     va_start (__ARGS__, format); | ||||
|     vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end (__ARGS__); | ||||
|  | ||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)  | ||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) | ||||
|         strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); | ||||
|  | ||||
|     actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); | ||||
|     actives[id].size ++; | ||||
|     actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000; | ||||
|      | ||||
|     actives[id].size++; | ||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; | ||||
|  | ||||
|     notify_notification_set_timeout(actives[id].box, Control.notif_timeout); | ||||
|     notify_notification_set_app_name(actives[id].box, "toxic"); | ||||
|     /*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/ | ||||
| @@ -639,10 +687,10 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi | ||||
|     return id; | ||||
| #else | ||||
|     return sound_notify(self, notif, flags, id_indicator); | ||||
| #endif | ||||
| #endif   /* BOX_NOTIFY */ | ||||
| } | ||||
|  | ||||
| int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...) | ||||
| int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...) | ||||
| { | ||||
|     if (notifications_are_disabled(flags)) { | ||||
|         tab_notify(self, flags); | ||||
| @@ -661,20 +709,22 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     va_list __ARGS__; va_start (__ARGS__, format); | ||||
|     va_list __ARGS__; | ||||
|     va_start (__ARGS__, format); | ||||
|     vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end (__ARGS__); | ||||
|  | ||||
|     if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) | ||||
|         strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); | ||||
|  | ||||
|     actives[id].size ++; | ||||
|     actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000; | ||||
|     actives[id].size++; | ||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; | ||||
|  | ||||
|     char formated[128 * 129] = {'\0'}; | ||||
|  | ||||
|     int i = 0; | ||||
|     for (; i <actives[id].size; i ++) { | ||||
|  | ||||
|     for (; i < actives[id].size; i ++) { | ||||
|         strcat(formated, actives[id].messages[i]); | ||||
|         strcat(formated, "\n"); | ||||
|     } | ||||
| @@ -692,7 +742,7 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...) | ||||
| int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...) | ||||
| { | ||||
|     tab_notify(self, flags); | ||||
|  | ||||
| @@ -703,8 +753,10 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const | ||||
|  | ||||
|     control_lock(); | ||||
|  | ||||
|     int id;         | ||||
|     int id; | ||||
|  | ||||
|     for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); | ||||
|  | ||||
|     if ( id == ACTIVE_NOTIFS_MAX ) { | ||||
|         control_unlock(); | ||||
|         return -1; /* Full */ | ||||
| @@ -715,20 +767,22 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const | ||||
|         *id_indicator = id; | ||||
|     } | ||||
|  | ||||
|     strncpy(actives[id].title, title, 24); | ||||
|     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); | ||||
|  | ||||
|     if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); | ||||
|  | ||||
|     va_list __ARGS__; va_start (__ARGS__, format); | ||||
|     va_list __ARGS__; | ||||
|     va_start (__ARGS__, format); | ||||
|     vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end (__ARGS__); | ||||
|  | ||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)  | ||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) | ||||
|         strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); | ||||
|  | ||||
|     actives[id].active = 1; | ||||
|     actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); | ||||
|     actives[id].size ++; | ||||
|     actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000; | ||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; | ||||
|  | ||||
|     notify_notification_set_timeout(actives[id].box, Control.notif_timeout); | ||||
|     notify_notification_set_app_name(actives[id].box, "toxic"); | ||||
| @@ -742,7 +796,7 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...) | ||||
| int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...) | ||||
| { | ||||
|     tab_notify(self, flags); | ||||
|  | ||||
| @@ -758,7 +812,8 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form | ||||
|     } | ||||
|  | ||||
|  | ||||
|     va_list __ARGS__; va_start (__ARGS__, format); | ||||
|     va_list __ARGS__; | ||||
|     va_start (__ARGS__, format); | ||||
|     vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end (__ARGS__); | ||||
|  | ||||
| @@ -766,12 +821,13 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form | ||||
|         strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); | ||||
|  | ||||
|     actives[id].size ++; | ||||
|     actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000; | ||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; | ||||
|  | ||||
|     char formated[128 * 129] = {'\0'}; | ||||
|  | ||||
|     int i = 0; | ||||
|     for (; i <actives[id].size; i ++) { | ||||
|  | ||||
|     for (; i < actives[id].size; i ++) { | ||||
|         strcat(formated, actives[id].messages[i]); | ||||
|         strcat(formated, "\n"); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										26
									
								
								src/notify.h
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								src/notify.h
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| /*  notify.h | ||||
|  *  | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
| @@ -26,10 +26,9 @@ | ||||
| #include <inttypes.h> | ||||
| #include "windows.h" | ||||
|  | ||||
| typedef enum _Notification | ||||
| { | ||||
| typedef enum _Notification { | ||||
|     silent = -1, | ||||
|     error, | ||||
|     notif_error, | ||||
|     self_log_in, | ||||
|     self_log_out, | ||||
|     user_log_in, | ||||
| @@ -49,32 +48,33 @@ typedef enum _Flags { | ||||
|     NT_LOOP = 1 << 2,       /* Loop sound. If this setting active, notify() will return id of the sound | ||||
|                              * so it could be stopped. It will return 0 if error or NT_NATIVE flag is set and play \a instead | ||||
|                              */ | ||||
|     NT_RESTOL = 1 << 3,     /* Respect tolerance. Usually used to stop flood at toxic startup  | ||||
|     NT_RESTOL = 1 << 3,     /* Respect tolerance. Usually used to stop flood at toxic startup | ||||
|                              * Only works if login_cooldown is true when calling init_notify() | ||||
|                              */ | ||||
|     NT_NOTIFWND = 1 << 4,   /* Pop notify window. NOTE: only works(/WILL WORK) if libnotify is present */ | ||||
|     NT_WNDALERT_0 = 1 << 5, /* Alert toxic */ | ||||
|     NT_WNDALERT_1 = 1 << 6, /* Alert toxic */ | ||||
|     NT_WNDALERT_2 = 1 << 7, /* Alert toxic */ | ||||
|      | ||||
|  | ||||
|     NT_ALWAYS = 1 << 8,     /* Force sound to play */ | ||||
| } Flags; | ||||
|  | ||||
| int init_notify(int login_cooldown, int notification_timeout); | ||||
| void terminate_notify(); | ||||
|  | ||||
| int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator); | ||||
| int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id); | ||||
| int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator); | ||||
| int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id); | ||||
|  | ||||
| void stop_sound(int id); | ||||
|  | ||||
| int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, char* title, const char* format, ...); | ||||
| int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...); | ||||
| int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...); | ||||
| int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...); | ||||
| int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title, | ||||
|                const char *format, ...); | ||||
| int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...); | ||||
| int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...); | ||||
| int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...); | ||||
|  | ||||
| #ifdef SOUND_NOTIFY | ||||
| int set_sound(Notification sound, const char* value); | ||||
| int set_sound(Notification sound, const char *value); | ||||
| #endif /* SOUND_NOTIFY */ | ||||
|  | ||||
| #endif /* NOTIFY_H */ | ||||
|   | ||||
							
								
								
									
										57
									
								
								src/osx_video.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/osx_video.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /*  osx_video.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef OSX_VIDEO_H | ||||
| #define OSX_VIDEO_H | ||||
|  | ||||
| #include <netinet/in.h> | ||||
|  | ||||
| #ifdef __OBJC__ | ||||
| #import <Foundation/Foundation.h> | ||||
| #import <AVFoundation/AVFoundation.h> | ||||
| #endif /* __OBJC__ */ | ||||
|  | ||||
| #define RELEASE_CHK(func, obj) if ((obj))\ | ||||
|     func((obj)); | ||||
|  | ||||
| void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height); | ||||
|  | ||||
| #ifdef __OBJC__ | ||||
| @interface OSXVideo : | ||||
|     NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> | ||||
| - (instancetype)initWithDeviceNames: | ||||
|     (char **)device_names AmtDevices: | ||||
|     (int *)size; | ||||
| @end | ||||
| #endif /* __OBJC__ */ | ||||
|  | ||||
| int osx_video_init(char **device_names, int *size); | ||||
| void osx_video_release(); | ||||
| /* Start device */ | ||||
| int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height); | ||||
| /* Stop device */ | ||||
| void osx_video_close_device(uint32_t device_idx); | ||||
| /* Read data from device */ | ||||
| int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height); | ||||
|  | ||||
|  | ||||
| #endif /* OSX_VIDEO_H */ | ||||
							
								
								
									
										342
									
								
								src/osx_video.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								src/osx_video.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,342 @@ | ||||
| /*  osx_video.m | ||||
|  * | ||||
|  * | ||||
|  *  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 __OBJC__ | ||||
| #include "osx_video.h" | ||||
|  | ||||
| #import <Foundation/Foundation.h> | ||||
| #import <AVFoundation/AVFoundation.h> | ||||
|  | ||||
| #include "line_info.h" | ||||
| #include "settings.h" | ||||
|  | ||||
| #include <errno.h> | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <string.h> | ||||
| #include <pthread.h> | ||||
| #include <unistd.h> | ||||
| #include <stdlib.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| /* | ||||
|  * Helper video format functions | ||||
|  */ | ||||
| static uint8_t rgb_to_y(int r, int g, int b) | ||||
| { | ||||
|     int y = ((9798 * r + 19235 * g + 3736 * b) >> 15); | ||||
|     return y > 255 ? 255 : y < 0 ? 0 : y; | ||||
| } | ||||
|  | ||||
| static uint8_t rgb_to_u(int r, int g, int b) | ||||
| { | ||||
|     int u = ((-5538 * r + -10846 * g + 16351 * b) >> 15) + 128; | ||||
|     return u > 255 ? 255 : u < 0 ? 0 : u; | ||||
| } | ||||
|  | ||||
| static uint8_t rgb_to_v(int r, int g, int b) | ||||
| { | ||||
|     int v = ((16351 * r + -13697 * g + -2664 * b) >> 15) + 128; | ||||
|     return v > 255 ? 255 : v < 0 ? 0 : v; | ||||
| } | ||||
|  | ||||
| void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height) | ||||
| { | ||||
|     uint16_t x, y; | ||||
|     uint8_t *p; | ||||
|     uint8_t r, g, b; | ||||
|  | ||||
|     for (y = 0; y != height; y += 2) { | ||||
|         p = rgb; | ||||
|  | ||||
|         for (x = 0; x != width; x++) { | ||||
|             b = *rgb++; | ||||
|             g = *rgb++; | ||||
|             r = *rgb++; | ||||
|             rgb++; | ||||
|  | ||||
|             *plane_y++ = rgb_to_y(r, g, b); | ||||
|         } | ||||
|  | ||||
|         for (x = 0; x != width / 2; x++) { | ||||
|             b = *rgb++; | ||||
|             g = *rgb++; | ||||
|             r = *rgb++; | ||||
|             rgb++; | ||||
|  | ||||
|             *plane_y++ = rgb_to_y(r, g, b); | ||||
|  | ||||
|             b = *rgb++; | ||||
|             g = *rgb++; | ||||
|             r = *rgb++; | ||||
|             rgb++; | ||||
|  | ||||
|             *plane_y++ = rgb_to_y(r, g, b); | ||||
|  | ||||
|             b = ((int)b + (int) * (rgb - 8) + (int) * p + (int) * (p + 4) + 2) / 4; | ||||
|             p++; | ||||
|             g = ((int)g + (int) * (rgb - 7) + (int) * p + (int) * (p + 4) + 2) / 4; | ||||
|             p++; | ||||
|             r = ((int)r + (int) * (rgb - 6) + (int) * p + (int) * (p + 4) + 2) / 4; | ||||
|             p++; | ||||
|             p++; | ||||
|  | ||||
|             *plane_u++ = rgb_to_u(r, g, b); | ||||
|             *plane_v++ = rgb_to_v(r, g, b); | ||||
|  | ||||
|             p += 4; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| /* | ||||
|  * End of helper video format functions | ||||
|  */ | ||||
|  | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Implementation for OSXVideo | ||||
|  */ | ||||
| @implementation OSXVideo { | ||||
|     dispatch_queue_t _processingQueue; | ||||
|     AVCaptureSession *_session; | ||||
|     AVCaptureVideoDataOutput *_linkerVideo; | ||||
|  | ||||
|     CVImageBufferRef _currentFrame; | ||||
|     pthread_mutex_t _frameLock; | ||||
|     BOOL _shouldMangleDimensions; | ||||
| } | ||||
|  | ||||
| - (instancetype)initWithDeviceNames: | ||||
|     (char **)device_names AmtDevices: | ||||
|     (int *)size | ||||
| { | ||||
|     _session = [[AVCaptureSession alloc] init]; | ||||
|  | ||||
|     NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < [devices count]; ++i) { | ||||
|         AVCaptureDevice *device = [devices objectAtIndex: i]; | ||||
|         char *video_input_name; | ||||
|         NSString *localizedName = [device localizedName]; | ||||
|         video_input_name = (char *)malloc(strlen([localizedName cStringUsingEncoding: NSUTF8StringEncoding]) + 1); | ||||
|         strcpy(video_input_name, (char *)[localizedName cStringUsingEncoding: NSUTF8StringEncoding]); | ||||
|         device_names[i] = video_input_name; | ||||
|     } | ||||
|  | ||||
|     if ( i <= 0 ) | ||||
|         return nil; | ||||
|  | ||||
|     *size = i; | ||||
|  | ||||
|     return self; | ||||
| } | ||||
|  | ||||
| - (void)dealloc | ||||
| { | ||||
|     pthread_mutex_destroy(&_frameLock); | ||||
|     [_session release]; | ||||
|     [_linkerVideo release]; | ||||
|     dispatch_release(_processingQueue); | ||||
|     [super dealloc]; | ||||
| } | ||||
|  | ||||
| - (int)openVideoDeviceIndex: | ||||
|     (uint32_t)device_idx Width: | ||||
|     (uint16_t *)width Height: | ||||
|     (uint16_t *)height | ||||
| { | ||||
|     pthread_mutex_init(&_frameLock, NULL); | ||||
|     pthread_mutex_lock(&_frameLock); | ||||
|     _processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL); | ||||
|     NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; | ||||
|     AVCaptureDevice *device = [devices objectAtIndex: device_idx]; | ||||
|     NSError *error = NULL; | ||||
|     AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error]; | ||||
|  | ||||
|     if ( error != NULL ) { | ||||
|         [input release]; | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     [_session beginConfiguration]; | ||||
|     [_session addInput: input]; | ||||
|     //_session.sessionPreset = AVCaptureSessionPreset640x480; | ||||
|     //*width = 640; | ||||
|     //*height = 480; | ||||
|     _shouldMangleDimensions = YES; | ||||
|     [_session commitConfiguration]; | ||||
|     [input release]; | ||||
|     [device release]; | ||||
|  | ||||
|     /* Obtain device resolution */ | ||||
|     AVCaptureInputPort *port = [input.ports objectAtIndex: 0]; | ||||
|     CMFormatDescriptionRef format_description = port.formatDescription; | ||||
|  | ||||
|     if ( format_description ) { | ||||
|         CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description); | ||||
|         *width = dimensions.width; | ||||
|         *height = dimensions.height; | ||||
|     } else { | ||||
|         *width = 0; | ||||
|         *height = 0; | ||||
|     } | ||||
|  | ||||
|     _linkerVideo = [[AVCaptureVideoDataOutput alloc] init]; | ||||
|     [_linkerVideo setSampleBufferDelegate: self queue: _processingQueue]; | ||||
|  | ||||
|     // TODO possibly get a better pixel format | ||||
|     if (_shouldMangleDimensions) { | ||||
|         [_linkerVideo setVideoSettings: @ { | ||||
| (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), | ||||
| (id)kCVPixelBufferWidthKey: @640, | ||||
| (id)kCVPixelBufferHeightKey: @480 | ||||
|         }]; | ||||
|     } else { | ||||
|         [_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}]; | ||||
|     } | ||||
|  | ||||
|     [_session addOutput: _linkerVideo]; | ||||
|     [_session startRunning]; | ||||
|  | ||||
|     pthread_mutex_unlock(&_frameLock); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| - (void)closeVideoDeviceIndex: | ||||
|     (uint32_t)device_idx | ||||
| { | ||||
|     NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; | ||||
|     AVCaptureDevice *device = [devices objectAtIndex: device_idx]; | ||||
|     NSError *error = NULL; | ||||
|     AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error]; | ||||
|     [_session stopRunning]; | ||||
|     [_session removeOutput: _linkerVideo]; | ||||
|     [_session removeInput: input]; | ||||
|     [_linkerVideo release]; | ||||
| } | ||||
|  | ||||
| - (void)captureOutput: | ||||
|     (AVCaptureOutput *)captureOutput didOutputSampleBuffer: | ||||
|     (CMSampleBufferRef)sampleBuffer fromConnection: | ||||
|     (AVCaptureConnection *)connection | ||||
| { | ||||
|     pthread_mutex_lock(&_frameLock); | ||||
|     CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer); | ||||
|  | ||||
|     if (!img) { | ||||
|         NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!"); | ||||
|     } else { | ||||
|         CVPixelBufferUnlockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly); | ||||
|         RELEASE_CHK(CFRelease, _currentFrame); | ||||
|  | ||||
|         _currentFrame = (CVImageBufferRef)CFRetain(img); | ||||
|         // we're not going to do anything to it, so it's safe to lock it always | ||||
|         CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly); | ||||
|     } | ||||
|  | ||||
|     pthread_mutex_unlock(&_frameLock); | ||||
| } | ||||
|  | ||||
| - (int)getVideoFrameY: | ||||
|     (uint8_t *)y U: | ||||
|     (uint8_t *)u V: | ||||
|     (uint8_t *)v Width: | ||||
|     (uint16_t *)width Height: | ||||
|     (uint16_t *)height | ||||
| { | ||||
|     if (!_currentFrame) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     pthread_mutex_lock(&_frameLock); | ||||
|     CFRetain(_currentFrame); | ||||
|  | ||||
|     CFTypeID imageType = CFGetTypeID(_currentFrame); | ||||
|  | ||||
|     if (imageType == CVPixelBufferGetTypeID()) { | ||||
|         // TODO maybe handle other formats | ||||
|         bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height); | ||||
|     } else if (imageType == CVOpenGLBufferGetTypeID()) { | ||||
|         // OpenGL pbuffer | ||||
|     } else if (imageType == CVOpenGLTextureGetTypeID()) { | ||||
|         // OpenGL Texture (Do we need to handle these?) | ||||
|     } | ||||
|  | ||||
|     CVPixelBufferRelease(_currentFrame); | ||||
|     pthread_mutex_unlock(&_frameLock); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @end | ||||
| /* | ||||
|  * End of implementation for OSXVideo | ||||
|  */ | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * C-interface for OSXVideo | ||||
|  */ | ||||
| static OSXVideo *_OSXVideo = nil; | ||||
|  | ||||
| int osx_video_init(char **device_names, int *size) | ||||
| { | ||||
|     _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size]; | ||||
|  | ||||
|     if ( _OSXVideo == nil ) | ||||
|         return -1; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void osx_video_release() | ||||
| { | ||||
|     [_OSXVideo release]; | ||||
|     _OSXVideo = nil; | ||||
| } | ||||
|  | ||||
| int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height) | ||||
| { | ||||
|     if ( _OSXVideo == nil ) | ||||
|         return -1; | ||||
|  | ||||
|     return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height]; | ||||
| } | ||||
|  | ||||
| void osx_video_close_device(uint32_t device_idx) | ||||
| { | ||||
|     [_OSXVideo closeVideoDeviceIndex: device_idx]; | ||||
| } | ||||
|  | ||||
| int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height) | ||||
| { | ||||
|     if ( _OSXVideo == nil ) | ||||
|         return -1; | ||||
|  | ||||
|     return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height]; | ||||
| } | ||||
| /* | ||||
|  * End of C-interface for OSXVideo | ||||
|  */ | ||||
|  | ||||
| #endif /* __OBJC__ */ | ||||
							
								
								
									
										218
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										218
									
								
								src/prompt.c
									
									
									
									
									
								
							| @@ -31,6 +31,7 @@ | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "prompt.h" | ||||
| #include "friendlist.h" | ||||
| #include "execute.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic_strings.h" | ||||
| @@ -46,24 +47,34 @@ extern ToxWindow *prompt; | ||||
| extern struct user_settings *user_settings; | ||||
| extern struct Winthread Winthread; | ||||
|  | ||||
| extern FriendsList Friends; | ||||
| FriendRequests FrndRequests; | ||||
| #ifdef VIDEO | ||||
| #define AC_NUM_GLOB_COMMANDS 23 | ||||
| #elif AUDIO | ||||
| #define AC_NUM_GLOB_COMMANDS 21 | ||||
| #else | ||||
| #define AC_NUM_GLOB_COMMANDS 19 | ||||
| #endif | ||||
|  | ||||
| /* Array of global command names used for tab completion. */ | ||||
| const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { | ||||
| static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { | ||||
|     { "/accept"     }, | ||||
|     { "/add"        }, | ||||
|     { "/avatar"     }, | ||||
|     { "/clear"      }, | ||||
|     { "/close"      },    /* rm /close when groupchats gets its own list */ | ||||
|     { "/connect"    }, | ||||
|     { "/decline"    }, | ||||
|     { "/exit"       }, | ||||
|     { "/groupchat"  }, | ||||
|     { "/group"      }, | ||||
|     { "/help"       }, | ||||
|     { "/join"       }, | ||||
|     { "/log"        }, | ||||
|     { "/myid"       }, | ||||
|     { "/myqr"       }, | ||||
|     { "/nick"       }, | ||||
|     { "/note"       }, | ||||
|     { "/nospam"     }, | ||||
|     { "/quit"       }, | ||||
|     { "/requests"   }, | ||||
|     { "/status"     }, | ||||
| @@ -74,9 +85,17 @@ const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { | ||||
|     { "/sdev"        }, | ||||
|  | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #ifdef VIDEO | ||||
|  | ||||
|     { "/lsvdev"      }, | ||||
|     { "/svdev"       }, | ||||
|  | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| }; | ||||
|  | ||||
| void kill_prompt_window(ToxWindow *self)  | ||||
| void kill_prompt_window(ToxWindow *self) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|     StatusBar *statusbar = self->stb; | ||||
| @@ -96,6 +115,13 @@ void kill_prompt_window(ToxWindow *self) | ||||
|     del_window(self); | ||||
| } | ||||
|  | ||||
| /* callback: Updates own connection status in prompt statusbar */ | ||||
| void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata) | ||||
| { | ||||
|     StatusBar *statusbar = prompt->stb; | ||||
|     statusbar->connection = connection_status; | ||||
| } | ||||
|  | ||||
| /* Updates own nick in prompt statusbar */ | ||||
| void prompt_update_nick(ToxWindow *prompt, const char *nick) | ||||
| { | ||||
| @@ -109,25 +135,23 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms | ||||
| { | ||||
|     StatusBar *statusbar = prompt->stb; | ||||
|     snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); | ||||
|     int len = strlen(statusbar->statusmsg); | ||||
|     size_t len = strlen(statusbar->statusmsg); | ||||
|     statusbar->statusmsg_len = len; | ||||
|     tox_set_status_message(m, (uint8_t *) statusmsg, (uint64_t) len);  | ||||
|  | ||||
|     TOX_ERR_SET_INFO err; | ||||
|     tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_SET_INFO_OK) | ||||
|         line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err); | ||||
| } | ||||
|  | ||||
| /* Updates own status in prompt statusbar */ | ||||
| void prompt_update_status(ToxWindow *prompt, uint8_t status) | ||||
| void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status) | ||||
| { | ||||
|     StatusBar *statusbar = prompt->stb; | ||||
|     statusbar->status = status; | ||||
| } | ||||
|  | ||||
| /* Updates own connection status in prompt statusbar */ | ||||
| void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected) | ||||
| { | ||||
|     StatusBar *statusbar = prompt->stb; | ||||
|     statusbar->is_online = is_connected; | ||||
| } | ||||
|  | ||||
| /* Adds friend request to pending friend requests. | ||||
|    Returns request number on success, -1 if queue is full. */ | ||||
| static int add_friend_request(const char *public_key, const char *data) | ||||
| @@ -140,7 +164,7 @@ static int add_friend_request(const char *public_key, const char *data) | ||||
|     for (i = 0; i <= FrndRequests.max_idx; ++i) { | ||||
|         if (!FrndRequests.request[i].active) { | ||||
|             FrndRequests.request[i].active = true; | ||||
|             memcpy(FrndRequests.request[i].key, public_key, TOX_CLIENT_ID_SIZE); | ||||
|             memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE); | ||||
|             snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data); | ||||
|  | ||||
|             if (i == FrndRequests.max_idx) | ||||
| @@ -163,16 +187,19 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | ||||
|     getyx(self->window, y, x); | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (x2 <= 0) | ||||
|     if (x2 <= 0 || y2 <= 0) | ||||
|         return; | ||||
|  | ||||
|     if (ctx->pastemode && key == '\r') | ||||
|         key = '\n'; | ||||
|  | ||||
|     /* ignore non-menu related input if active */ | ||||
|     if (self->help->active) { | ||||
|         help_onKey(self, key); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (ltr) {    /* char is printable */ | ||||
|     if (ltr || key == '\n') {    /* char is printable */ | ||||
|         input_new_char(self, key, x, y, x2, y2); | ||||
|         return; | ||||
|     } | ||||
| @@ -188,33 +215,42 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | ||||
|  | ||||
|             if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) | ||||
|                 diff = dir_match(self, m, ctx->line, L"/avatar"); | ||||
|             else | ||||
|             else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { | ||||
|                 const char status_cmd_list[3][8] = { | ||||
|                     {"online"}, | ||||
|                     {"away"}, | ||||
|                     {"busy"}, | ||||
|                 }; | ||||
|                 diff = complete_line(self, status_cmd_list, 3, 8); | ||||
|             } else | ||||
|                 diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); | ||||
|  | ||||
|             if (diff != -1) { | ||||
|                 if (x + diff > x2 - 1) { | ||||
|                     int wlen = wcswidth(ctx->line, sizeof(ctx->line)); | ||||
|                     int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); | ||||
|                     ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; | ||||
|                 } | ||||
|             } else { | ||||
|                 sound_notify(self, error, 0, NULL); | ||||
|                 sound_notify(self, notif_error, 0, NULL); | ||||
|             } | ||||
|         } else { | ||||
|             sound_notify(self, error, 0, NULL); | ||||
|             sound_notify(self, notif_error, 0, NULL); | ||||
|         } | ||||
|     } else if (key == '\n') { | ||||
|     } else if (key == '\r') { | ||||
|         rm_trailing_spaces_buf(ctx); | ||||
|  | ||||
|         char line[MAX_STR_SIZE] = {0}; | ||||
|  | ||||
|         if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) | ||||
|             memset(&line, 0, sizeof(line)); | ||||
|  | ||||
|         if (!string_is_empty(line)) | ||||
|         if (!wstring_is_empty(ctx->line)) { | ||||
|             add_line_to_hist(ctx); | ||||
|             wstrsubst(ctx->line, L'¶', L'\n'); | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); | ||||
|         execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); | ||||
|             char line[MAX_STR_SIZE] = {0}; | ||||
|  | ||||
|             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) | ||||
|                 memset(&line, 0, sizeof(line)); | ||||
|  | ||||
|             line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); | ||||
|             execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); | ||||
|         } | ||||
|  | ||||
|         wclear(ctx->linewin); | ||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||
| @@ -227,9 +263,15 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||
|     int x2, y2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) | ||||
|         return; | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     line_info_print(self); | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     wclear(ctx->linewin); | ||||
|  | ||||
|     curs_set(1); | ||||
| @@ -238,33 +280,37 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||
|         mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); | ||||
|  | ||||
|     StatusBar *statusbar = self->stb; | ||||
|  | ||||
|     mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2); | ||||
|     wmove(statusbar->topline, 0, 0); | ||||
|  | ||||
|     if (statusbar->is_online) { | ||||
|         int colour = WHITE; | ||||
|         const char *status_text = "Unknown"; | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     TOX_CONNECTION connection = statusbar->connection; | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|         switch (statusbar->status) { | ||||
|             case TOX_USERSTATUS_NONE: | ||||
|     if (connection != TOX_CONNECTION_NONE) { | ||||
|         int colour = MAGENTA; | ||||
|         const char *status_text = "ERROR"; | ||||
|  | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         TOX_USER_STATUS status = statusbar->status; | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|         switch (status) { | ||||
|             case TOX_USER_STATUS_NONE: | ||||
|                 status_text = "Online"; | ||||
|                 colour = GREEN; | ||||
|                 break; | ||||
|  | ||||
|             case TOX_USERSTATUS_AWAY: | ||||
|             case TOX_USER_STATUS_AWAY: | ||||
|                 status_text = "Away"; | ||||
|                 colour = YELLOW; | ||||
|                 break; | ||||
|  | ||||
|             case TOX_USERSTATUS_BUSY: | ||||
|             case TOX_USER_STATUS_BUSY: | ||||
|                 status_text = "Busy"; | ||||
|                 colour = RED; | ||||
|                 break; | ||||
|  | ||||
|             case TOX_USERSTATUS_INVALID: | ||||
|                 status_text = "ERROR"; | ||||
|                 colour = MAGENTA; | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); | ||||
| @@ -272,25 +318,30 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); | ||||
|  | ||||
|         wattron(statusbar->topline, A_BOLD); | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         wprintw(statusbar->topline, " %s", statusbar->nick); | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|         wattroff(statusbar->topline, A_BOLD); | ||||
|     } else { | ||||
|         wprintw(statusbar->topline, " [Offline]"); | ||||
|         wattron(statusbar->topline, A_BOLD); | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         wprintw(statusbar->topline, " %s", statusbar->nick); | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|         wattroff(statusbar->topline, A_BOLD); | ||||
|     } | ||||
|  | ||||
|     /* Reset statusbar->statusmsg on window resize */ | ||||
|     if (x2 != self->x) { | ||||
|         char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {0}; | ||||
|         char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; | ||||
|  | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         tox_get_self_status_message(m, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH); | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|         size_t slen = tox_self_get_status_message_size(m); | ||||
|         tox_self_get_status_message (m, (uint8_t *) statusmsg); | ||||
|         statusmsg[slen] = '\0'; | ||||
|         snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); | ||||
|         statusbar->statusmsg_len = strlen(statusbar->statusmsg); | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|     } | ||||
|  | ||||
|     self->x = x2; | ||||
| @@ -298,6 +349,8 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||
|     /* Truncate note if it doesn't fit in statusbar */ | ||||
|     uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3; | ||||
|  | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|  | ||||
|     if (statusbar->statusmsg_len > maxlen) { | ||||
|         statusbar->statusmsg[maxlen - 3] = '\0'; | ||||
|         strcat(statusbar->statusmsg, "..."); | ||||
| @@ -307,26 +360,25 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||
|     if (statusbar->statusmsg[0]) | ||||
|         wprintw(statusbar->topline, " : %s", statusbar->statusmsg); | ||||
|  | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); | ||||
|  | ||||
|     int y, x; | ||||
|     getyx(self->window, y, x); | ||||
|     (void) x; | ||||
|  | ||||
|     int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos); | ||||
|     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); | ||||
|     wmove(self->window, y + 1, new_x); | ||||
|  | ||||
|     wrefresh(self->window); | ||||
|     wnoutrefresh(self->window); | ||||
|  | ||||
|     if (self->help->active) | ||||
|         help_onDraw(self); | ||||
| } | ||||
|  | ||||
| static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status) | ||||
| static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status) | ||||
| { | ||||
|     if (friendnum < 0) | ||||
|         return; | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     char nick[TOX_MAX_NAME_LENGTH] = {0};    /* stop removing this initiation */ | ||||
| @@ -339,24 +391,28 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum | ||||
|     get_time_str(timefrmt, sizeof(timefrmt)); | ||||
|     const char *msg; | ||||
|  | ||||
|     if (status == 1) { | ||||
|     if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) { | ||||
|         msg = "has come online"; | ||||
|         line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); | ||||
|         write_to_log(msg, nick, ctx->log, true); | ||||
|  | ||||
|         if (self->active_box != -1) | ||||
|             box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,  | ||||
|             box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box, | ||||
|                         "%s has come online", nick ); | ||||
|         else | ||||
|             box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, | ||||
|                        "Toxic", "%s has come online", nick ); | ||||
|     } else { | ||||
|     } else if (connection_status == TOX_CONNECTION_NONE) { | ||||
|         msg = "has gone offline"; | ||||
|         line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, RED, msg); | ||||
|         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); | ||||
|         write_to_log(msg, nick, ctx->log, true); | ||||
|  | ||||
|         if (self->active_box != -1) | ||||
|             box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,  | ||||
|             box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box, | ||||
|                         "%s has gone offline", nick ); | ||||
|         else | ||||
|             box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, | ||||
| @@ -364,8 +420,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, | ||||
|                                    uint16_t length) | ||||
| static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
| @@ -391,30 +446,33 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m) | ||||
| { | ||||
|     int x2, y2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) | ||||
|         exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES); | ||||
|  | ||||
|     (void) y2; | ||||
|  | ||||
|     /* Init statusbar info */ | ||||
|     StatusBar *statusbar = self->stb; | ||||
|     statusbar->status = TOX_USERSTATUS_NONE; | ||||
|     statusbar->is_online = false; | ||||
|     statusbar->status = TOX_USER_STATUS_NONE; | ||||
|     statusbar->connection = TOX_CONNECTION_NONE; | ||||
|  | ||||
|     char nick[TOX_MAX_NAME_LENGTH]; | ||||
|     char statusmsg[MAX_STR_SIZE]; | ||||
|     char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; | ||||
|  | ||||
|     uint16_t n_len = tox_get_self_name(m, (uint8_t *) nick); | ||||
|     uint16_t s_len = tox_get_self_status_message(m, (uint8_t *) statusmsg, MAX_STR_SIZE); | ||||
|     uint8_t status = tox_get_self_user_status(m); | ||||
|     size_t n_len = tox_self_get_name_size(m); | ||||
|     tox_self_get_name(m, (uint8_t *) nick); | ||||
|  | ||||
|     size_t s_len = tox_self_get_status_message_size(m); | ||||
|     tox_self_get_status_message(m, (uint8_t *) statusmsg); | ||||
|  | ||||
|     TOX_USER_STATUS status = tox_self_get_status(m); | ||||
|  | ||||
|     nick[n_len] = '\0'; | ||||
|     statusmsg[s_len] = '\0'; | ||||
|  | ||||
|     /* load prev status message or show toxic version if it has never been set */ | ||||
|     char ver[strlen(TOXICVER) + 1]; | ||||
|     strcpy(ver, TOXICVER); | ||||
|     const char *toxic_ver = strtok(ver, "_"); | ||||
|  | ||||
|     if ( (s_len <= 0 || !strncmp("Toxing on Toxic", statusmsg, strlen("Toxing on Toxic"))) && toxic_ver != NULL) { | ||||
|         snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic v.%s", toxic_ver); | ||||
|     if (s_len == 0 || !strncmp(statusmsg, "Toxing on Toxic", strlen("Toxing on Toxic"))) { | ||||
|         snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic"); | ||||
|         s_len = strlen(statusmsg); | ||||
|         statusmsg[s_len] = '\0'; | ||||
|     } | ||||
| @@ -433,7 +491,7 @@ static void print_welcome_msg(ToxWindow *self) | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|"); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    "); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ "); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____|"); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____| v." TOXICVER); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|  | ||||
|     const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client."; | ||||
| @@ -449,6 +507,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m) | ||||
|     int y2, x2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) | ||||
|         exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES); | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); | ||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); | ||||
| @@ -462,9 +523,11 @@ static void prompt_onInit(ToxWindow *self, Tox *m) | ||||
|     line_info_init(ctx->hst); | ||||
|  | ||||
|     if (user_settings->autolog == AUTOLOG_ON) { | ||||
|         char myid[TOX_FRIEND_ADDRESS_SIZE]; | ||||
|         tox_get_address(m, (uint8_t *) myid); | ||||
|         log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT); | ||||
|         char myid[TOX_ADDRESS_SIZE]; | ||||
|         tox_self_get_address(m, (uint8_t *) myid); | ||||
|  | ||||
|         if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); | ||||
|     } | ||||
|  | ||||
|     scrollok(ctx->history, 0); | ||||
| @@ -479,6 +542,7 @@ ToxWindow new_prompt(void) | ||||
|     ToxWindow ret; | ||||
|     memset(&ret, 0, sizeof(ret)); | ||||
|  | ||||
|     ret.num = -1; | ||||
|     ret.active = true; | ||||
|     ret.is_prompt = true; | ||||
|  | ||||
| @@ -502,6 +566,6 @@ ToxWindow new_prompt(void) | ||||
|     ret.help = help; | ||||
|  | ||||
|     ret.active_box = -1; | ||||
|      | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/prompt.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/prompt.h
									
									
									
									
									
								
							| @@ -26,18 +26,12 @@ | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #ifdef AUDIO | ||||
| #define AC_NUM_GLOB_COMMANDS 19 | ||||
| #else | ||||
| #define AC_NUM_GLOB_COMMANDS 17 | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #define MAX_FRIEND_REQUESTS 32 | ||||
|  | ||||
| struct friend_request { | ||||
|     bool active; | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|     uint8_t key[TOX_CLIENT_ID_SIZE]; | ||||
|     uint8_t key[TOX_PUBLIC_KEY_SIZE]; | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
| @@ -47,12 +41,16 @@ typedef struct { | ||||
| } FriendRequests; | ||||
|  | ||||
| ToxWindow new_prompt(void); | ||||
|  | ||||
| void prep_prompt_win(void); | ||||
| void prompt_init_statusbar(ToxWindow *self, Tox *m); | ||||
| void prompt_update_nick(ToxWindow *prompt, const char *nick); | ||||
| void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg); | ||||
| void prompt_update_status(ToxWindow *prompt, uint8_t status); | ||||
| void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status); | ||||
| void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected); | ||||
| void kill_prompt_window(ToxWindow *self); | ||||
|  | ||||
| /* callback: Updates own connection status in prompt statusbar */ | ||||
| void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata); | ||||
|  | ||||
| #endif /* end of include guard: PROMPT_H */ | ||||
|   | ||||
							
								
								
									
										209
									
								
								src/qr_code.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								src/qr_code.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,209 @@ | ||||
| /*  qr_obj_code.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 <qrencode.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "qr_code.h" | ||||
|  | ||||
| #ifdef QRPNG | ||||
| #include <png.h> | ||||
| #define INCHES_PER_METER (100.0/2.54) | ||||
| #define DPI 72 | ||||
| #define SQUARE_SIZE 5 | ||||
| #endif /* QRPNG */ | ||||
|  | ||||
| #define BORDER_LEN 1 | ||||
| #define CHAR_1 "\342\226\210" | ||||
| #define CHAR_2 "\342\226\204" | ||||
| #define CHAR_3 "\342\226\200" | ||||
|  | ||||
| /* Converts a tox ID string into a QRcode and prints it into the given filename. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int ID_to_QRcode_txt(const char *tox_id, const char *outfile) | ||||
| { | ||||
|     FILE *fp = fopen(outfile, "wb"); | ||||
|  | ||||
|     if (fp == NULL) | ||||
|         return -1; | ||||
|  | ||||
|     QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0); | ||||
|  | ||||
|     if (qr_obj == NULL) { | ||||
|         fclose(fp); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     size_t width = qr_obj->width; | ||||
|     size_t i, j; | ||||
|  | ||||
|     for (i = 0; i < width + BORDER_LEN * 2; ++i) | ||||
|         fprintf(fp, "%s", CHAR_1); | ||||
|  | ||||
|     fprintf(fp, "\n"); | ||||
|  | ||||
|     for (i = 0; i < width; i += 2) { | ||||
|         for (j = 0; j < BORDER_LEN; ++j) | ||||
|             fprintf(fp, "%s", CHAR_1); | ||||
|  | ||||
|         const unsigned char *row_1 = qr_obj->data + width * i; | ||||
|         const unsigned char *row_2 = row_1 + width; | ||||
|  | ||||
|         for (j = 0; j < width; ++j) { | ||||
|             bool x = row_1[j] & 1; | ||||
|             bool y = (i + 1) < width ? (row_2[j] & 1) : false; | ||||
|  | ||||
|             if (x && y) | ||||
|                 fprintf(fp, " "); | ||||
|             else if (x) | ||||
|                 fprintf(fp, "%s", CHAR_2); | ||||
|             else if (y) | ||||
|                 fprintf(fp, "%s", CHAR_3); | ||||
|             else | ||||
|                 fprintf(fp, "%s", CHAR_1); | ||||
|         } | ||||
|  | ||||
|         for (j = 0; j < BORDER_LEN; ++j) | ||||
|             fprintf(fp, "%s", CHAR_1); | ||||
|  | ||||
|         fprintf(fp, "\n"); | ||||
|     } | ||||
|  | ||||
|     fclose(fp); | ||||
|     QRcode_free(qr_obj); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #ifdef QRPNG | ||||
| /* Converts a tox ID string into a QRcode and prints it into the given filename as png. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int ID_to_QRcode_png(const char *tox_id, const char *outfile) | ||||
| { | ||||
|     static FILE *fp; | ||||
|     unsigned char *p; | ||||
|     unsigned char black[4] = {0, 0, 0, 255}; | ||||
|     size_t x, y, xx, yy, real_width; | ||||
|     png_structp png_ptr; | ||||
|     png_infop info_ptr; | ||||
|  | ||||
|     fp = fopen(outfile, "wb"); | ||||
|  | ||||
|     if (fp == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0); | ||||
|  | ||||
|     if (qr_obj == NULL) { | ||||
|         fclose(fp); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     real_width = (qr_obj->width + BORDER_LEN * 2) * SQUARE_SIZE; | ||||
|     size_t row_size = real_width * 4; | ||||
|     unsigned char row[row_size]; | ||||
|  | ||||
|     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | ||||
|  | ||||
|     if (png_ptr == NULL) { | ||||
|         fclose(fp); | ||||
|         QRcode_free(qr_obj); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     info_ptr = png_create_info_struct(png_ptr); | ||||
|  | ||||
|     if (info_ptr == NULL) { | ||||
|         fclose(fp); | ||||
|         QRcode_free(qr_obj); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (setjmp(png_jmpbuf(png_ptr))) { | ||||
|         fclose(fp); | ||||
|         QRcode_free(qr_obj); | ||||
|         png_destroy_write_struct(&png_ptr, &info_ptr); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     png_init_io(png_ptr, fp); | ||||
|     png_set_IHDR(png_ptr, info_ptr, real_width, real_width, 8, | ||||
|                  PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, | ||||
|                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | ||||
|     png_set_pHYs(png_ptr, info_ptr, DPI * INCHES_PER_METER, | ||||
|                  DPI * INCHES_PER_METER, PNG_RESOLUTION_METER); | ||||
|     png_write_info(png_ptr, info_ptr); | ||||
|  | ||||
|     /* top margin */ | ||||
|     memset(row, 0xff, row_size); | ||||
|  | ||||
|     for (y = 0; y < BORDER_LEN * SQUARE_SIZE; y++) { | ||||
|         png_write_row(png_ptr, row); | ||||
|     } | ||||
|  | ||||
|     /* data */ | ||||
|     p = qr_obj->data; | ||||
|  | ||||
|     for (y = 0; y < qr_obj->width; y++) { | ||||
|         memset(row, 0xff, row_size); | ||||
|  | ||||
|         for (x = 0; x < qr_obj->width; x++) { | ||||
|             for (xx = 0; xx < SQUARE_SIZE; xx++) { | ||||
|                 if (*p & 1) { | ||||
|                     memcpy(&row[((BORDER_LEN + x) * SQUARE_SIZE + xx) * 4], black, 4); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             p++; | ||||
|         } | ||||
|  | ||||
|         for (yy = 0; yy < SQUARE_SIZE; yy++) { | ||||
|             png_write_row(png_ptr, row); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* bottom margin */ | ||||
|     memset(row, 0xff, row_size); | ||||
|  | ||||
|     for (y = 0; y < BORDER_LEN * SQUARE_SIZE; y++) { | ||||
|         png_write_row(png_ptr, row); | ||||
|     } | ||||
|  | ||||
|     png_write_end(png_ptr, info_ptr); | ||||
|     png_destroy_write_struct(&png_ptr, &info_ptr); | ||||
|  | ||||
|     fclose(fp); | ||||
|     QRcode_free(qr_obj); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| #endif /* QRPNG */ | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user