mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-10-31 18:56:51 +01:00 
			
		
		
		
	Compare commits
	
		
			277 Commits
		
	
	
		
			v0.6.1
			...
			new_groupc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9dfa455561 | ||
|  | 8805f694b9 | ||
|  | 71040355fd | ||
|  | 6bc5d8c543 | ||
|  | abb39ea6b5 | ||
|  | 15846d2b50 | ||
|  | 958df9f2e8 | ||
|  | 2fd43aebee | ||
|  | 34c29745cc | ||
|  | da6fe41d75 | ||
|  | 372fcb0a67 | ||
|  | e17fa89d8f | ||
|  | c760ffc563 | ||
|  | c6f2a9cb59 | ||
|  | f056f13329 | ||
|  | 3515623159 | ||
|  | 2194b9e259 | ||
|  | c24e1bd2b8 | ||
|  | 38ec96e96a | ||
|  | d2b572ede1 | ||
|  | 703d5419a3 | ||
|  | 221d761ff4 | ||
|  | 151f5f0c51 | ||
|  | 4f6c603543 | ||
|  | a009f11c0c | ||
|  | 1f8c11a33a | ||
|  | 5e20e6b279 | ||
|  | 1f02bb2be5 | ||
|  | 98154b3cba | ||
|  | 379ad9e116 | ||
|  | cb21672600 | ||
|  | 4019395f44 | ||
|  | ee084c572c | ||
|  | 41a8401ac5 | ||
|  | d8a3f7de4c | ||
|  | c425aa2f27 | ||
|  | 94e026d114 | ||
|  | f89638635a | ||
|  | 402b86687f | ||
|  | 5b1b420ac0 | ||
|  | 62ec514f17 | ||
|  | f893dd755f | ||
|  | 9aedcf7753 | ||
|  | d3effa26b5 | ||
|  | 2ec180789b | ||
|  | 76bfa34c45 | ||
|  | 9f74d3a3a8 | ||
|  | 43d9623877 | ||
|  | 9fcbc3bde0 | ||
|  | cf16849b37 | ||
|  | 32442b6286 | ||
|  | 50f227418b | ||
|  | fc06a625a6 | ||
|  | 70bd39eb74 | ||
|  | 4e0e322e32 | ||
|  | e73ac9b6a4 | ||
|  | bcda6e476e | ||
|  | 5b29ce7132 | ||
|  | f43f644451 | ||
|  | d6fdac9739 | ||
|  | c6a2bb8a90 | ||
|  | 04576fea7e | ||
|  | e6f839f9ac | ||
|  | eb02424f8a | ||
|  | 2e609c46f6 | ||
|  | a474e3bf39 | ||
|  | 93835f0455 | ||
|  | ac6d8ff89c | ||
|  | 88e74224ed | ||
|  | deccaec40e | ||
|  | 4419be36e8 | ||
|  | b34b51e8c1 | ||
|  | 74416b4b58 | ||
|  | 675712cea0 | ||
|  | 36feebfe8d | ||
|  | 3fe9abd84d | ||
|  | fd6432c727 | ||
|  | 60b431459c | ||
|  | 1feffcc2f0 | ||
|  | 6bba3cb9e2 | ||
|  | 6e7b0a5430 | ||
|  | 86b36d7a13 | ||
|  | 7e122ceec6 | ||
|  | 3cb6db3d60 | ||
|  | 77238eeadf | ||
|  | fe0eba9107 | ||
|  | 1d1c051a44 | ||
|  | 88270827a9 | ||
|  | aade65bfe1 | ||
|  | b24c5d8cf8 | ||
|  | 9f0feb7223 | ||
|  | 74c1eef1d1 | ||
|  | 65c07a57db | ||
|  | ab99c1ac73 | ||
|  | 05f5f16af3 | ||
|  | fbd22003a8 | ||
|  | d16be574f3 | ||
|  | 7e0b8b4870 | ||
|  | 39c4b7ecdd | ||
|  | c5d9aca3e1 | ||
|  | fa0e645a79 | ||
|  | 14a8bdb874 | ||
|  | 93a73cbef2 | ||
|  | 3cfda32b5e | ||
|  | 6aab9a79d8 | ||
|  | 9b1592b335 | ||
|  | dfff777283 | ||
|  | a95fc7824c | ||
|  | f707dce2da | ||
|  | 0d07d14b13 | ||
|  | 6cc1525daa | ||
|  | 49f5efaab0 | ||
|  | a5e5e98afc | ||
|  | 4ab99c73a0 | ||
|  | fbf50ecc8c | ||
|  | e02cf1bb7d | ||
|  | 9751cfc407 | ||
|  | 7025a33097 | ||
|  | 36963a5b38 | ||
|  | 7cf9c37aef | ||
|  | 2b4b8c0289 | ||
|  | 368a1465ec | ||
|  | fea317ee24 | ||
|  | 8584feab80 | ||
|  | f7e48d294e | ||
|  | 928f25bd89 | ||
|  | 941ac1d951 | ||
|  | 7af9327b37 | ||
|  | 6b97df2615 | ||
|  | cea5f1fe04 | ||
|  | abfdbfe468 | ||
|  | 462cfca175 | ||
|  | 171024428b | ||
|  | db410cb01e | ||
|  | 2e4c86be4b | ||
|  | 97d5fb84fc | ||
|  | a920f3edfe | ||
|  | 2c3921a9fb | ||
|  | f295352495 | ||
|  | b2c512687a | ||
|  | ffcc804efe | ||
|  | 8526d4d77e | ||
|  | ed1429afa1 | ||
|  | 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 | ||
|  | 6eef188d3c | ||
|  | 8e23ce1b83 | ||
|  | 15ef50e46c | ||
|  | 0597152352 | ||
|  | 23bb980173 | ||
|  | a846c38695 | ||
|  | 1f7f4490b2 | ||
|  | 40b220c821 | ||
|  | cb4a631df0 | ||
|  | 35718db46f | ||
|  | 1ba0891f71 | ||
|  | b36ada0f5b | ||
|  | 9cd2158c72 | ||
|  | 928134da7a | ||
|  | 52a3367b5e | ||
|  | 7e7087e94e | ||
|  | 9cd8afe90a | ||
|  | 526bd02189 | ||
|  | 3826fd793d | ||
|  | a6d7e9b839 | ||
|  | 29d0384a90 | ||
|  | 0481f43746 | ||
|  | f021908f8b | ||
|  | f82d58bbfc | ||
|  | 9385f1145f | ||
|  | 2acd99b04c | ||
|  | 6cd7fb9e5b | ||
|  | 540bf4a5c4 | ||
|  | a360afe08a | ||
|  | b66a1f6ba1 | ||
|  | b66932fcec | ||
|  | de38df32c2 | ||
|  | bdfbda7cda | ||
|  | 0c49ab392d | ||
|  | b2c0753fdf | ||
|  | c97095be68 | ||
|  | c2b9967691 | ||
|  | ee074f334e | ||
|  | 23429d8da4 | ||
|  | e34ac70a72 | ||
|  | 77d45334ac | ||
|  | 88719c4ccc | ||
|  | 386c5a8fa5 | ||
|  | 5caa9bed51 | ||
|  | 59b90b1328 | ||
|  | 05c05868c6 | ||
|  | 88d6d907d8 | ||
|  | 6cc8fdf215 | ||
|  | 02a0cac85a | ||
|  | 0976804fdf | ||
|  | f8ff5df2f3 | ||
|  | 22acba5aa8 | ||
|  | 2bcce234a8 | ||
|  | 5859763f04 | ||
|  | 4cc0805036 | ||
|  | 830ddb21b5 | ||
|  | b31bd93e7d | ||
|  | a89e6b2cdc | ||
|  | dd3ea3dab5 | ||
|  | c3179b3b22 | ||
|  | 5c98c1c51e | ||
|  | ff69cdd253 | ||
|  | 38e55d55b3 | ||
|  | 6f45085d86 | ||
|  | 0348f81ba8 | ||
|  | 7ee858110c | ||
|  | 89637e7d2f | ||
|  | ff3da5f657 | ||
|  | b3ab0bde05 | ||
|  | 26dda8dbf9 | 
							
								
								
									
										52
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,52 +0,0 @@ | |||||||
| language: c |  | ||||||
| compiler: |  | ||||||
|   - gcc |  | ||||||
|   - clang |  | ||||||
|  |  | ||||||
| before_script: |  | ||||||
|   # Installing yasm (needed for compiling vpx) and openal |  | ||||||
|   - sudo apt-get -yq install yasm libopenal-dev libconfig-dev libalut-dev libnotify-dev clang llvm-dev |  | ||||||
|   # Installing libsodium, needed for toxcore |  | ||||||
|   - git clone https://github.com/jedisct1/libsodium.git libsodium |  | ||||||
|   - cd libsodium |  | ||||||
|   - git checkout tags/1.0.3 > /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.1.tar.gz |  | ||||||
|   - tar xzf opus-1.1.tar.gz > /dev/null |  | ||||||
|   - cd opus-1.1 |  | ||||||
|   - ./configure > /dev/null |  | ||||||
|   - make -j2 || make || exit 1 > /dev/null |  | ||||||
|   - sudo make install > /dev/null |  | ||||||
|   - cd .. |  | ||||||
|   # Installing vpx |  | ||||||
|   - git clone https://chromium.googlesource.com/webm/libvpx 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: |  | ||||||
|   - 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)* | ||||||
							
								
								
									
										24
									
								
								INSTALL.md
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								INSTALL.md
									
									
									
									
									
								
							| @@ -9,17 +9,19 @@ | |||||||
|  |  | ||||||
| <a name="deps" /> | <a name="deps" /> | ||||||
| ## Dependencies | ## Dependencies | ||||||
| | Name                                                 | Needed by                  | Debian package   | | | Name                                                 | Needed by                  | Debian package      | | ||||||
| |------------------------------------------------------|----------------------------|------------------| | |------------------------------------------------------|----------------------------|---------------------| | ||||||
| | [Tox Core](https://github.com/irungentoo/toxcore)    | BASE                       | *None*           | | | [Tox Core](https://github.com/irungentoo/toxcore)    | BASE                       | *None*              | | ||||||
| | [NCurses](https://www.gnu.org/software/ncurses)      | BASE                       | libncursesw5-dev | | | [NCurses](https://www.gnu.org/software/ncurses)      | BASE                       | libncursesw5-dev    | | ||||||
| | [LibConfig](http://www.hyperrealm.com/libconfig)     | BASE                       | libconfig-dev    | | | [LibConfig](http://www.hyperrealm.com/libconfig)     | BASE                       | libconfig-dev       | | ||||||
| | [GNUmake](https://www.gnu.org/software/make)         | BASE                       | make             | | | [GNUmake](https://www.gnu.org/software/make)         | BASE                       | make                | | ||||||
| | [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO                      | *None*           | | | [libcurl](http://curl.haxx.se/)                      | BASE                       | libcurl4-openssl-dev| | ||||||
| | [OpenAL](http://openal.org)                          | AUDIO, SOUND NOTIFICATIONS | libopenal-dev    | | | [libqrencode](https://fukuchi.org/works/qrencode/)   | BASE                       | libqrencode-dev     | | ||||||
| | [OpenALUT](http://openal.org)                        | SOUND NOTIFICATIONS        | libalut-dev      | | | [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO                      | *None*              | | ||||||
| | [LibNotify](https://developer.gnome.org/libnotify)   | DESKTOP NOTIFICATIONS      | libnotify-dev    | | | [OpenAL](http://openal.org)                          | AUDIO, SOUND NOTIFICATIONS | libopenal-dev       | | ||||||
| | [AsciiDoc](http://asciidoc.org/index.html)           | DOCUMENTATION<sup>1</sup>  | asciidoc         | | | [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) | <sup>1</sup>: see [Documentation](#docs) | ||||||
|  |  | ||||||
| <a name="deps_osx" /> | <a name="deps_osx" /> | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								Makefile
									
									
									
									
									
								
							| @@ -3,18 +3,18 @@ CFG_DIR = $(BASE_DIR)/cfg | |||||||
|  |  | ||||||
| -include $(CFG_DIR)/global_vars.mk | -include $(CFG_DIR)/global_vars.mk | ||||||
|  |  | ||||||
| LIBS = libtoxcore ncursesw libconfig | LIBS = libtoxcore ncursesw libconfig libqrencode | ||||||
|  |  | ||||||
| CFLAGS = -std=gnu99 -pthread -Wall -g | CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all | ||||||
| CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | ||||||
| CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | ||||||
| CFLAGS += $(USER_CFLAGS) | CFLAGS += $(USER_CFLAGS) | ||||||
| LDFLAGS = $(USER_LDFLAGS) | LDFLAGS = $(USER_LDFLAGS) | ||||||
|  |  | ||||||
| OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_transfers.o notify.o | OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o | ||||||
| OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o | OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o | ||||||
| OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.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 += group_commands.o term_mplex.o avatars.o | OBJ += term_mplex.o toxic.o toxic_strings.o windows.o | ||||||
|  |  | ||||||
| # Check on wich system we are running | # Check on wich system we are running | ||||||
| UNAME_S = $(shell uname -s) | UNAME_S = $(shell uname -s) | ||||||
| @@ -24,6 +24,9 @@ endif | |||||||
| ifeq ($(UNAME_S), FreeBSD) | ifeq ($(UNAME_S), FreeBSD) | ||||||
|     -include $(CFG_DIR)/systems/FreeBSD.mk |     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||||
| endif | endif | ||||||
|  | ifeq ($(UNAME_S), DragonFly) | ||||||
|  |     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||||
|  | endif | ||||||
| ifeq ($(UNAME_S), OpenBSD) | ifeq ($(UNAME_S), OpenBSD) | ||||||
|     -include $(CFG_DIR)/systems/FreeBSD.mk |     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||||
| endif | endif | ||||||
| @@ -59,6 +62,10 @@ $(BUILD_DIR)/toxic: $(OBJ) | |||||||
| 	@echo "  LD    $(@:$(BUILD_DIR)/%=%)" | 	@echo "  LD    $(@:$(BUILD_DIR)/%=%)" | ||||||
| 	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS) | 	@$(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 | $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | ||||||
| 	@if [ ! -e $(BUILD_DIR) ]; then \ | 	@if [ ! -e $(BUILD_DIR) ]; then \ | ||||||
| 		mkdir -p $(BUILD_DIR) ;\ | 		mkdir -p $(BUILD_DIR) ;\ | ||||||
|   | |||||||
| @@ -1,5 +1,9 @@ | |||||||
| # Toxic [](https://travis-ci.org/Tox/toxic) | <a href="https://scan.coverity.com/projects/toxic-tox"> | ||||||
| Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application. |   <img alt="Coverity Scan Build Status" | ||||||
|  |        src="https://scan.coverity.com/projects/4975/badge.svg"/> | ||||||
|  | </a> | ||||||
|  |  | ||||||
|  | Toxic is a [Tox](https://tox.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) | [](https://i.imgur.com/san99Z2.png) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										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,10 +1,10 @@ | |||||||
| # Variables for audio call support
 | # Variables for audio call support
 | ||||||
| AUDIO_LIBS = libtoxav openal | AUDIO_LIBS = libtoxav openal | ||||||
| AUDIO_CFLAGS = -DAUDIO | AUDIO_CFLAGS = -DAUDIO | ||||||
| ifneq (, $(findstring device.o, $(OBJ))) | ifneq (, $(findstring audio_device.o, $(OBJ))) | ||||||
|     AUDIO_OBJ = audio_call.o |     AUDIO_OBJ = audio_call.o | ||||||
| else | else | ||||||
|     AUDIO_OBJ = audio_call.o device.o |     AUDIO_OBJ = audio_call.o audio_device.o | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| # Check if we can build audio support
 | # Check if we can build audio support
 | ||||||
| @@ -9,7 +9,17 @@ endif | |||||||
| # Check if we want build audio support | # Check if we want build audio support | ||||||
| AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||||
| ifneq ($(AUDIO), disabled) | 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 | endif | ||||||
|  |  | ||||||
| # Check if we want build sound notifications support | # Check if we want build sound notifications support | ||||||
| @@ -24,6 +34,12 @@ ifneq ($(DESK_NOTIFY), disabled) | |||||||
|     -include $(CHECKS_DIR)/desktop_notifications.mk |     -include $(CHECKS_DIR)/desktop_notifications.mk | ||||||
| endif | 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 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) | ifneq ($(CHECK_LIBS), error) | ||||||
|   | |||||||
							
								
								
									
										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,10 +1,10 @@ | |||||||
| # Variables for sound notifications support | # Variables for sound notifications support | ||||||
| SND_NOTIFY_LIBS = openal freealut | SND_NOTIFY_LIBS = openal freealut | ||||||
| SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY | SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY | ||||||
| ifneq (, $(findstring device.o, $(OBJ))) | ifneq (, $(findstring audio_device.o, $(OBJ))) | ||||||
|     SND_NOTIFY_OBJ = |     SND_NOTIFY_OBJ = | ||||||
| else | else | ||||||
|     SND_NOTIFY_OBJ = device.o |     SND_NOTIFY_OBJ = audio_device.o | ||||||
| endif | endif | ||||||
|  |  | ||||||
| # Check if we can build sound notifications support | # Check if we can build sound notifications support | ||||||
|   | |||||||
							
								
								
									
										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,5 +1,5 @@ | |||||||
| # Version | # Version | ||||||
| TOXIC_VERSION = 0.6.0 | TOXIC_VERSION = 0.7.1 | ||||||
| REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | ||||||
| ifneq (, $(findstring error, $(REV))) | ifneq (, $(findstring error, $(REV))) | ||||||
|     VERSION = $(TOXIC_VERSION) |     VERSION = $(TOXIC_VERSION) | ||||||
| @@ -16,7 +16,7 @@ MISC_DIR = $(BASE_DIR)/misc | |||||||
|  |  | ||||||
| # Project files | # Project files | ||||||
| MANFILES = toxic.1 toxic.conf.5 | MANFILES = toxic.1 toxic.conf.5 | ||||||
| DATAFILES = DHTnodes DNSservers toxic.conf.example | DATAFILES = nameservers toxic.conf.example | ||||||
| DESKFILE = toxic.desktop | DESKFILE = toxic.desktop | ||||||
| SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav | SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav | ||||||
| SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav | SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav | ||||||
| @@ -28,6 +28,5 @@ BINDIR = $(PREFIX)/bin | |||||||
| DATADIR = $(PREFIX)/share/toxic | DATADIR = $(PREFIX)/share/toxic | ||||||
| MANDIR = $(PREFIX)/share/man | MANDIR = $(PREFIX)/share/man | ||||||
| APPDIR = $(PREFIX)/share/applications | APPDIR = $(PREFIX)/share/applications | ||||||
|  |  | ||||||
| # Platform tools | # Platform tools | ||||||
| PKG_CONFIG = pkg-config | PKG_CONFIG = pkg-config | ||||||
|   | |||||||
| @@ -6,5 +6,13 @@ PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/o | |||||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | LIBS := $(filter-out ncursesw, $(LIBS)) | ||||||
|  |  | ||||||
| # OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw. | # OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw. | ||||||
| LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -ltoxdns -lresolv -lconfig -ltoxencryptsave -g | 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 | 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 | # Specials options for freebsd systems | ||||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | LIBS := $(filter-out ncursesw, $(LIBS)) | ||||||
| LDFLAGS += -lncursesw | LDFLAGS += -lncursesw -lcurl | ||||||
| MANDIR = $(PREFIX)/man | MANDIR = $(PREFIX)/man | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| # Specials options for linux systems | # Specials options for linux systems | ||||||
| CFLAGS += | CFLAGS += | ||||||
| LDFLAGS += -ldl -lresolv -lrt | LDFLAGS += -ldl -lrt -lcurl | ||||||
| MANDIR = $(PREFIX)/share/man | MANDIR = $(PREFIX)/share/man | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ help: | |||||||
| 	@echo "  DISABLE_AV:             Set to \"1\" to force building without audio call 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_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_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_CFLAGS:            Add custom flags to default CFLAGS" | ||||||
| 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | ||||||
| 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								doc/toxic.1
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								doc/toxic.1
									
									
									
									
									
								
							| @@ -2,12 +2,12 @@ | |||||||
| .\"     Title: toxic | .\"     Title: toxic | ||||||
| .\"    Author: [see the "AUTHORS" section] | .\"    Author: [see the "AUTHORS" section] | ||||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | ||||||
| .\"      Date: 2015-03-28 | .\"      Date: 2016-09-20 | ||||||
| .\"    Manual: Toxic Manual | .\"    Manual: Toxic Manual | ||||||
| .\"    Source: toxic __VERSION__ | .\"    Source: toxic __VERSION__ | ||||||
| .\"  Language: English | .\"  Language: English | ||||||
| .\" | .\" | ||||||
| .TH "TOXIC" "1" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual" | .TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual" | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| .\" * Define some portability stuff | .\" * 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 | Use specified | ||||||
| \fIdata\-file\fR | \fIdata\-file\fR | ||||||
| instead of | instead of | ||||||
| \fI~/\&.config/tox/data\fR | \fI~/\&.config/tox/toxic_profile\&.tox\fR | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \-h, \-\-help | \-h, \-\-help | ||||||
| @@ -82,8 +82,8 @@ Show help message | |||||||
| .RS 4 | .RS 4 | ||||||
| Use specified | Use specified | ||||||
| \fInodes\-file\fR | \fInodes\-file\fR | ||||||
| for DHT bootstrap nodes, instead of | for DHT bootstrap nodes instead of | ||||||
| \fI__DATADIR__/DHTnodes\fR | \fI~/\&.config/tox/DHTnodes\&.json\fR | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \-o, \-\-noconnect | \-o, \-\-noconnect | ||||||
| @@ -101,9 +101,9 @@ Use a SOCKS5 proxy: Requires [IP] [port] | |||||||
| Use a HTTP proxy: Requires [IP] [port] | Use a HTTP proxy: Requires [IP] [port] | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \-r, \-\-dnslist | \-r, \-\-namelist | ||||||
| .RS 4 | .RS 4 | ||||||
| Use specified DNSservers file | Use specified nameservers list | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \-t, \-\-force\-tcp | \-t, \-\-force\-tcp | ||||||
| @@ -122,12 +122,14 @@ Unencrypt a data file\&. A warning will appear if this option is used with a dat | |||||||
| .RE | .RE | ||||||
| .SH "FILES" | .SH "FILES" | ||||||
| .PP | .PP | ||||||
| __DATADIR__/DHTnodes | ~/\&.config/tox/DHTnodes\&.json | ||||||
| .RS 4 | .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 | .RE | ||||||
| .PP | .PP | ||||||
| ~/\&.config/tox/data | ~/\&.config/tox/toxic_profile\&.tox | ||||||
| .RS 4 | .RS 4 | ||||||
| Savestate which contains your personal info (nickname, Tox ID, contacts, etc) | Savestate which contains your personal info (nickname, Tox ID, contacts, etc) | ||||||
| .RE | .RE | ||||||
| @@ -144,7 +146,11 @@ Configuration example\&. | |||||||
| .RE | .RE | ||||||
| .SH "BUGS" | .SH "BUGS" | ||||||
| .sp | .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" | .SH "AUTHORS" | ||||||
| .sp | .sp | ||||||
| JFreegman <JFreegman@gmail\&.com> | JFreegman <JFreegman@gmail\&.com> | ||||||
| @@ -153,6 +159,6 @@ JFreegman <JFreegman@gmail\&.com> | |||||||
| \fBtoxic\&.conf\fR(5) | \fBtoxic\&.conf\fR(5) | ||||||
| .SH "LINKS" | .SH "LINKS" | ||||||
| .sp | .sp | ||||||
| Project page: https://github\&.com/Tox/toxic | Project page: https://github\&.com/JFreegman/toxic | ||||||
| .sp | .sp | ||||||
| IRC channel: chat\&.freenode\&.net#tox | IRC channel: chat\&.freenode\&.net#tox | ||||||
|   | |||||||
| @@ -35,14 +35,13 @@ OPTIONS | |||||||
|     is used with an encrypted data file. |     is used with an encrypted data file. | ||||||
|  |  | ||||||
| -f, --file 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:: | -h, --help:: | ||||||
|     Show help message |     Show help message | ||||||
|  |  | ||||||
| -n, --nodes nodes-file:: | -n, --nodes nodes-file:: | ||||||
|     Use specified 'nodes-file' for DHT bootstrap nodes, instead of |     Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json' | ||||||
|     '{datadir}/DHTnodes' |  | ||||||
|  |  | ||||||
| -o, --noconnect:: | -o, --noconnect:: | ||||||
|     Do not connect to the DHT network |     Do not connect to the DHT network | ||||||
| @@ -53,8 +52,8 @@ OPTIONS | |||||||
| -P, --HTTP-proxy:: | -P, --HTTP-proxy:: | ||||||
|     Use a HTTP proxy: Requires [IP] [port] |     Use a HTTP proxy: Requires [IP] [port] | ||||||
|  |  | ||||||
| -r, --dnslist:: | -r, --namelist:: | ||||||
|     Use specified DNSservers file |     Use specified nameservers list | ||||||
|  |  | ||||||
| -t, --force-tcp:: | -t, --force-tcp:: | ||||||
|     Force TCP connection (use this with proxies) |     Force TCP connection (use this with proxies) | ||||||
| @@ -68,10 +67,11 @@ OPTIONS | |||||||
|  |  | ||||||
| FILES | FILES | ||||||
| ----- | ----- | ||||||
| {datadir}/DHTnodes:: | ~/.config/tox/DHTnodes.json:: | ||||||
|     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 *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, |     Savestate which contains your personal info (nickname, Tox ID, contacts, | ||||||
|     etc) |     etc) | ||||||
|  |  | ||||||
| @@ -83,9 +83,12 @@ FILES | |||||||
|  |  | ||||||
| BUGS | BUGS | ||||||
| ---- | ---- | ||||||
| Unicode characters with a width larger than 1 column may cause strange | -Unicode characters with a width larger than 1 column may cause strange | ||||||
| behaviour. Expect more bugs and bad behaviour: this software is in a | behaviour. | ||||||
| pre-alpha stage. |  | ||||||
|  | -Text occasionally fails to auto-scroll to the bottom. | ||||||
|  |  | ||||||
|  | -Screen flickering sometimes occurs on certain terminals. | ||||||
|  |  | ||||||
| AUTHORS | AUTHORS | ||||||
| ------- | ------- | ||||||
| @@ -97,6 +100,6 @@ SEE ALSO | |||||||
|  |  | ||||||
| LINKS | LINKS | ||||||
| ----- | ----- | ||||||
| Project page: <https://github.com/Tox/toxic> | Project page: <https://github.com/JFreegman/toxic> | ||||||
|  |  | ||||||
| IRC channel: chat.freenode.net#tox | IRC channel: chat.freenode.net#tox | ||||||
|   | |||||||
| @@ -2,12 +2,12 @@ | |||||||
| .\"     Title: toxic.conf | .\"     Title: toxic.conf | ||||||
| .\"    Author: [see the "AUTHORS" section] | .\"    Author: [see the "AUTHORS" section] | ||||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | ||||||
| .\"      Date: 2015-03-28 | .\"      Date: 2016-07-21 | ||||||
| .\"    Manual: Toxic Manual | .\"    Manual: Toxic Manual | ||||||
| .\"    Source: toxic __VERSION__ | .\"    Source: toxic __VERSION__ | ||||||
| .\"  Language: English | .\"  Language: English | ||||||
| .\" | .\" | ||||||
| .TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual" | .TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual" | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| .\" * Define some portability stuff | .\" * Define some portability stuff | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| @@ -85,7 +85,7 @@ Time format string for logging enclosed by double quotes\&. See | |||||||
| .PP | .PP | ||||||
| \fBalerts\fR | \fBalerts\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Enable or disable terminal alerts on events\&. true or false | Enable or disable acoustic alerts on events\&. true or false | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \fBnative_colors\fR | \fBnative_colors\fR | ||||||
| @@ -113,6 +113,16 @@ Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false | |||||||
| Show welcome message on startup\&. true or false | Show welcome message on startup\&. true or false | ||||||
| .RE | .RE | ||||||
| .PP | .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 | \fBhistory_size\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Maximum lines for chat window history\&. Integer value\&. (for example: 700) | Maximum lines for chat window history\&. Integer value\&. (for example: 700) | ||||||
| @@ -138,6 +148,11 @@ Indicator for alert messages\&. | |||||||
| Indicator for normal messages\&. | Indicator for normal messages\&. | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
|  | \fBline_special\fR | ||||||
|  | .RS 4 | ||||||
|  | Indicator for special messages\&. | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
| \fBmplex_away\fR | \fBmplex_away\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Set user status when attaching and detaching from GNU screen or tmux\&. true or false | Set user status when attaching and detaching from GNU screen or tmux\&. true or false | ||||||
| @@ -146,6 +161,38 @@ Set user status when attaching and detaching from GNU screen or tmux\&. true or | |||||||
| \fBmplex_away_note\fR | \fBmplex_away_note\fR | ||||||
| .RS 4 | .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\&. | 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 | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| @@ -189,6 +236,11 @@ Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB) | |||||||
| .RS 4 | .RS 4 | ||||||
| Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. | Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. | ||||||
| .RE | .RE | ||||||
|  | .PP | ||||||
|  | \fBpassword_eval\fR | ||||||
|  | .RS 4 | ||||||
|  | Replace password prompt by running this command and using its output as the password\&. | ||||||
|  | .RE | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \fBsounds\fR | \fBsounds\fR | ||||||
| @@ -303,6 +355,11 @@ Key combination to scroll contacts list down\&. | |||||||
| .RS 4 | .RS 4 | ||||||
| Toggle the peer list on and off\&. | Toggle the peer list on and off\&. | ||||||
| .RE | .RE | ||||||
|  | .PP | ||||||
|  | \fBtoggle_paste_mode\fR | ||||||
|  | .RS 4 | ||||||
|  | Toggle treating linebreaks as enter key press\&. | ||||||
|  | .RE | ||||||
| .RE | .RE | ||||||
| .SH "FILES" | .SH "FILES" | ||||||
| .PP | .PP | ||||||
| @@ -320,7 +377,7 @@ Configuration example\&. | |||||||
| \fBtoxic\fR(1) | \fBtoxic\fR(1) | ||||||
| .SH "RESOURCES" | .SH "RESOURCES" | ||||||
| .sp | .sp | ||||||
| Project page: https://github\&.com/Tox/toxic | Project page: https://github\&.com/JFreegman/toxic | ||||||
| .sp | .sp | ||||||
| IRC channel: chat\&.freenode\&.net#tox | IRC channel: chat\&.freenode\&.net#tox | ||||||
| .SH "AUTHORS" | .SH "AUTHORS" | ||||||
|   | |||||||
| @@ -55,7 +55,7 @@ OPTIONS | |||||||
|         See *date*(1) |         See *date*(1) | ||||||
|  |  | ||||||
|     *alerts*;; |     *alerts*;; | ||||||
|         Enable or disable terminal alerts on events. true or false |         Enable or disable acoustic alerts on events. true or false | ||||||
|  |  | ||||||
|     *native_colors*;; |     *native_colors*;; | ||||||
|         Select between native terminal colors and toxic color theme. true or false |         Select between native terminal colors and toxic color theme. true or false | ||||||
| @@ -72,6 +72,12 @@ OPTIONS | |||||||
|     *show_welcome_msg*;; |     *show_welcome_msg*;; | ||||||
|         Show welcome message on startup. true or false |         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*;; |     *history_size*;; | ||||||
|         Maximum lines for chat window history. Integer value. (for example: 700) |         Maximum lines for chat window history. Integer value. (for example: 700) | ||||||
|  |  | ||||||
| @@ -88,6 +94,9 @@ OPTIONS | |||||||
|     *line_normal*;; |     *line_normal*;; | ||||||
|         Indicator for normal messages. |         Indicator for normal messages. | ||||||
|  |  | ||||||
|  |     *line_special*;; | ||||||
|  |         Indicator for special messages. | ||||||
|  |  | ||||||
|     *mplex_away*;; |     *mplex_away*;; | ||||||
|         Set user status when attaching and detaching from GNU screen or tmux. |         Set user status when attaching and detaching from GNU screen or tmux. | ||||||
|         true or false |         true or false | ||||||
| @@ -97,6 +106,23 @@ OPTIONS | |||||||
|         detach. When attaching, the status message is set back to the original |         detach. When attaching, the status message is set back to the original | ||||||
|         value. |         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*:: | *audio*:: | ||||||
|     Configuration related to audio devices. |     Configuration related to audio devices. | ||||||
|  |  | ||||||
| @@ -123,6 +149,10 @@ OPTIONS | |||||||
|     *chatlogs_path*;; |     *chatlogs_path*;; | ||||||
|         Default path for chatlogs. String value. Absolute path for chatlog files. |         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*:: | *sounds*:: | ||||||
|     Configuration related to notification sounds. |     Configuration related to notification sounds. | ||||||
|     Special value "silent" can be used to disable a specific notification. + |     Special value "silent" can be used to disable a specific notification. + | ||||||
| @@ -194,6 +224,9 @@ OPTIONS | |||||||
|     *toggle_peerlist*;; |     *toggle_peerlist*;; | ||||||
|         Toggle the peer list on and off. |         Toggle the peer list on and off. | ||||||
|  |  | ||||||
|  |     *toggle_paste_mode*;; | ||||||
|  |         Toggle treating linebreaks as enter key press. | ||||||
|  |  | ||||||
|  |  | ||||||
| FILES | FILES | ||||||
| ----- | ----- | ||||||
| @@ -211,7 +244,7 @@ SEE ALSO | |||||||
|  |  | ||||||
| RESOURCES | RESOURCES | ||||||
| --------- | --------- | ||||||
| Project page: <https://github.com/Tox/toxic> | Project page: <https://github.com/JFreegman/toxic> | ||||||
|  |  | ||||||
| IRC channel:  chat.freenode.net#tox | IRC channel:  chat.freenode.net#tox | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,25 +0,0 @@ | |||||||
| 192.254.75.102 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F |  | ||||||
| 144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F |  | ||||||
| 23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074 |  | ||||||
| 178.62.125.224 33445 10B20C49ACBD968D7C80F2E8438F92EA51F189F4E70CFBBB2C2C8C799E97F03E |  | ||||||
| 178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057 |  | ||||||
| 195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354 |  | ||||||
| 192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67 |  | ||||||
| 76.191.23.96 33445 93574A3FAB7D612FEA29FD8D67D3DD10DFD07A075A5D62E8AF3DD9F5D0932E11 |  | ||||||
| 46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A |  | ||||||
| 178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B |  | ||||||
| 78.225.128.39 33445 7A2306BFBA665E5480AE59B31E116BE9C04DCEFE04D9FE25082316FA34B4DA0C |  | ||||||
| 130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F |  | ||||||
| 104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57 |  | ||||||
| 195.154.109.148 33445 391C96CB67AE893D4782B8E4495EB9D89CF1031F48460C06075AA8CE76D50A21 |  | ||||||
| 192.3.173.88 33445 3E1FFDEB667BFF549F619EC6737834762124F50A89C8D0DBF1DDF64A2DD6CD1B |  | ||||||
| 205.185.116.116 33445 A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702 |  | ||||||
| 198.98.51.198 33445 1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F |  | ||||||
| 80.232.246.79 33445 A7A060D553B017D9D8F038E265C7AFB6C70BAAC55070197F9C007432D0038E0F |  | ||||||
| 108.61.165.198 33445 8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832 |  | ||||||
| 212.71.252.109 33445 C4CEB8C7AC607C6B374E2E782B3C00EA3A63B80D4910B8649CCACDD19F260819 |  | ||||||
| 194.249.212.109 33445 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B |  | ||||||
| 194.249.212.109 443 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B |  | ||||||
| 103.38.216.87 33445 601AEE6FC8C17E8CD8F8F1FFC4D4AD84E59A73BE451F037194E7A404E3795320 |  | ||||||
| 185.25.116.107 33445 DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43 |  | ||||||
| 192.99.168.140 33445 6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F |  | ||||||
| @@ -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 |   // true to enable timestamps, false to disable | ||||||
|   timestamps=true; |   timestamps=true; | ||||||
|  |  | ||||||
|   // true to enable terminal alerts on messages, false to disable |   // true to enable acoustic alerts on messages, false to disable | ||||||
|   alerts=true; |   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 |   // true to use native terminal colours, false to use toxic default colour theme | ||||||
|   native_colors=false; |   native_colors=false; | ||||||
|  |  | ||||||
| @@ -17,7 +29,7 @@ ui = { | |||||||
|   // 24 or 12 hour time |   // 24 or 12 hour time | ||||||
|   time_format=24; |   time_format=24; | ||||||
|  |  | ||||||
|   // timestamp format string according to date/strftime format. Overrides time_format setting |   // Timestamp format string according to date/strftime format. Overrides time_format setting | ||||||
|   timestamp_format="%H:%M:%S"; |   timestamp_format="%H:%M:%S"; | ||||||
|  |  | ||||||
|   // true to show you when others are typing a message in 1-on-1 chats |   // true to show you when others are typing a message in 1-on-1 chats | ||||||
| @@ -29,6 +41,12 @@ ui = { | |||||||
|   // true to show the welcome message on startup |   // true to show the welcome message on startup | ||||||
|   show_welcome_msg=true; |   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 |   // maximum lines for chat window history | ||||||
|   history_size=700; |   history_size=700; | ||||||
|  |  | ||||||
| @@ -44,6 +62,9 @@ ui = { | |||||||
|   // Indicator for normal messages. |   // Indicator for normal messages. | ||||||
|   line_normal="---"; |   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 |   // true to change status based on screen/tmux attach/detach, false to disable | ||||||
|   mplex_away=true; |   mplex_away=true; | ||||||
|  |  | ||||||
| @@ -66,7 +87,7 @@ tox = { | |||||||
|   // Path for downloaded files |   // Path for downloaded files | ||||||
|   // download_path="/home/USERNAME/Downloads/"; |   // 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"; |   // avatar_path="/home/USERNAME/Pictures/youravatar.png"; | ||||||
|  |  | ||||||
|   // Path for chatlogs |   // Path for chatlogs | ||||||
| @@ -86,7 +107,7 @@ sounds = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| // Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive) | // 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 = { | keys = { | ||||||
|   next_tab="Ctrl+P"; |   next_tab="Ctrl+P"; | ||||||
|   prev_tab="Ctrl+O"; |   prev_tab="Ctrl+O"; | ||||||
| @@ -98,5 +119,6 @@ keys = { | |||||||
|   peer_list_up="Ctrl+["; |   peer_list_up="Ctrl+["; | ||||||
|   peer_list_down="Ctrl+]"; |   peer_list_down="Ctrl+]"; | ||||||
|   toggle_peerlist="Ctrl+b"; |   toggle_peerlist="Ctrl+b"; | ||||||
|  |   toggle_paste_mode="Ctrl+T"; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										622
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										622
									
								
								src/audio_call.c
									
									
									
									
									
								
							| @@ -23,11 +23,17 @@ | |||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "audio_call.h" | #include "audio_call.h" | ||||||
| #include "device.h" | #include "audio_device.h" | ||||||
| #include "chat_commands.h" | #include "chat_commands.h" | ||||||
| #include "global_commands.h" | #include "global_commands.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
|  | #include "friendlist.h" | ||||||
|  | #include "chat.h" | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  | #include "video_call.h" | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <curses.h> | #include <curses.h> | ||||||
| @@ -49,174 +55,169 @@ | |||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | extern FriendsList Friends; | ||||||
|  |  | ||||||
| #define cbend pthread_exit(NULL) | #define cbend pthread_exit(NULL) | ||||||
|  |  | ||||||
| #define MAX_CALLS 10 | #define frame_size (CallControl.audio_sample_rate * CallControl.audio_frame_duration / 1000) | ||||||
|  |  | ||||||
| #define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000) | static int set_call(Call *call, bool start) | ||||||
|  |  | ||||||
| static int set_call(Call* call, bool start) |  | ||||||
| { | { | ||||||
|     call->in_idx = -1; |     call->in_idx = -1; | ||||||
|     call->out_idx = -1; |     call->out_idx = -1; | ||||||
|  | #ifdef VIDEO | ||||||
|  |     call->vin_idx = -1; | ||||||
|  |     call->vout_idx = -1; | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|     if ( start ) { |     if ( start ) { | ||||||
|         call->ttas = true; |         call->ttas = true; | ||||||
|  |  | ||||||
|         if (pthread_mutex_init(&call->mutex, NULL) != 0) |         if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) | ||||||
|             return -1; |             return -1; | ||||||
|     } |     } else { | ||||||
|     else { |  | ||||||
|         call->ttid = 0; |         call->ttid = 0; | ||||||
|         if (pthread_mutex_destroy(&call->mutex) != 0) |  | ||||||
|  |         if ( pthread_mutex_destroy(&call->mutex) != 0 ) | ||||||
|             return -1; |             return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| struct ASettings { | void call_cb                 ( ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, | ||||||
|     AudioError errors; |                                void *user_data ); | ||||||
|  | void callstate_cb            ( ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data ); | ||||||
|  | void receive_audio_frame_cb  ( ToxAV *av, uint32_t friend_number, int16_t const *pcm, size_t sample_count, | ||||||
|  |                                uint8_t channels, uint32_t sampling_rate, void *user_data ); | ||||||
|  | void receive_video_frame_cb  ( ToxAV *av, uint32_t friend_number, | ||||||
|  |                                uint16_t width, uint16_t height, | ||||||
|  |                                uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a, | ||||||
|  |                                int32_t ystride, int32_t ustride, int32_t vstride, int32_t astride, void *user_data ); | ||||||
|  |  | ||||||
|     ToxAv *av; | void callback_recv_invite   ( Tox *m, uint32_t friend_number ); | ||||||
|  | void callback_recv_ringing  ( uint32_t friend_number ); | ||||||
|  | void callback_recv_starting ( uint32_t friend_number ); | ||||||
|  | void callback_recv_ending   ( uint32_t friend_number ); | ||||||
|  | void callback_call_started  ( uint32_t friend_number ); | ||||||
|  | void callback_call_canceled ( uint32_t friend_number ); | ||||||
|  | void callback_call_rejected ( uint32_t friend_number ); | ||||||
|  | void callback_call_ended    ( uint32_t friend_number ); | ||||||
|  |  | ||||||
|     ToxAvCSettings cs; | void write_device_callback( uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels, | ||||||
|  |                             uint32_t sample_rate ); | ||||||
|     Call calls[MAX_CALLS]; |  | ||||||
| } ASettins; |  | ||||||
|  |  | ||||||
| void callback_recv_invite   ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_recv_ringing  ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_recv_starting ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_recv_ending   ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_call_started  ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_call_canceled ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_call_rejected ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_call_ended    ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_requ_timeout  ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_peer_timeout  ( void* av, int32_t call_index, void *arg ); |  | ||||||
| void callback_media_change  ( void* av, int32_t call_index, void *arg ); |  | ||||||
|  |  | ||||||
| void write_device_callback( void* agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg ); |  | ||||||
|  |  | ||||||
| static void print_err (ToxWindow *self, const char *error_str) | static void print_err (ToxWindow *self, const char *error_str) | ||||||
| { | { | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); | ||||||
| } | } | ||||||
|  |  | ||||||
| ToxAv *init_audio(ToxWindow *self, Tox *tox) | ToxAV *init_audio(ToxWindow *self, Tox *tox) | ||||||
| { | { | ||||||
|     ASettins.cs = av_DefaultSettings; |     TOXAV_ERR_NEW error; | ||||||
|     ASettins.cs.max_video_height = ASettins.cs.max_video_width = 0; |     CallControl.audio_errors = ae_None; | ||||||
|  |     CallControl.prompt = self; | ||||||
|  |     CallControl.pending_call = false; | ||||||
|  |  | ||||||
|     ASettins.errors = ae_None; |     CallControl.av = toxav_new(tox, &error); | ||||||
|  |  | ||||||
|     memset(ASettins.calls, 0, sizeof(ASettins.calls)); |     CallControl.audio_enabled = true; | ||||||
|  |     CallControl.audio_bit_rate = 64; | ||||||
|  |     CallControl.audio_sample_rate = 48000; | ||||||
|  |     CallControl.audio_frame_duration = 20; | ||||||
|  |     CallControl.audio_channels = 1; | ||||||
|  |  | ||||||
|  | #ifndef VIDEO | ||||||
|  |     CallControl.video_enabled = false; | ||||||
|  |     CallControl.video_bit_rate = 0; | ||||||
|  |     CallControl.video_frame_duration = 0; | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|     /* Streaming stuff from core */ |     memset(CallControl.calls, 0, sizeof(CallControl.calls)); | ||||||
|  |  | ||||||
|     ASettins.av = toxav_new(tox, MAX_CALLS); |     if ( !CallControl.av ) { | ||||||
|  |         CallControl.audio_errors |= ae_StartingCoreAudio; | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV"); | ||||||
|  |  | ||||||
|     if ( !ASettins.av ) { |  | ||||||
|         ASettins.errors |= ae_StartingCoreAudio; |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ( init_devices(ASettins.av) == de_InternalError ) { |     if ( init_devices(CallControl.av) == de_InternalError ) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices"); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices"); | ||||||
|         toxav_kill(ASettins.av); |         toxav_kill(CallControl.av); | ||||||
|         return ASettins.av = NULL; |  | ||||||
|  |         return CallControl.av = NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_call_started, av_OnStart, self); |     toxav_callback_call(CallControl.av, call_cb, tox); | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_call_canceled, av_OnCancel, self); |     toxav_callback_call_state(CallControl.av, callstate_cb, NULL); | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_call_rejected, av_OnReject, self); |     toxav_callback_audio_receive_frame(CallControl.av, receive_audio_frame_cb, NULL); | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_call_ended, av_OnEnd, self); |  | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_recv_invite, av_OnInvite, self); |  | ||||||
|  |  | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_recv_ringing, av_OnRinging, self); |     return CallControl.av; | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_recv_starting, av_OnStart, self); |  | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_recv_ending, av_OnEnd, self); |  | ||||||
|  |  | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_requ_timeout, av_OnRequestTimeout, self); |  | ||||||
|     toxav_register_callstate_callback(ASettins.av, callback_peer_timeout, av_OnPeerTimeout, self); |  | ||||||
|     //toxav_register_callstate_callback(ASettins.av, callback_media_change, av_OnMediaChange, self); |  | ||||||
|  |  | ||||||
|     toxav_register_audio_callback(ASettins.av, write_device_callback, NULL); |  | ||||||
|  |  | ||||||
|     return ASettins.av; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void terminate_audio() | void terminate_audio() | ||||||
| { | { | ||||||
|     int i; |     int i; | ||||||
|     for (i = 0; i < MAX_CALLS; ++i) |  | ||||||
|         stop_transmission(&ASettins.calls[i], i); |  | ||||||
|  |  | ||||||
|     if ( ASettins.av ) |     for (i = 0; i < MAX_CALLS; ++i) | ||||||
|         toxav_kill(ASettins.av); |         stop_transmission(&CallControl.calls[i], i); | ||||||
|  |  | ||||||
|  |     if ( CallControl.av ) | ||||||
|  |         toxav_kill(CallControl.av); | ||||||
|  |  | ||||||
|     terminate_devices(); |     terminate_devices(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void read_device_callback (const int16_t* captured, uint32_t size, void* data) | void read_device_callback(const int16_t *captured, uint32_t size, void *data) | ||||||
| { | { | ||||||
|     int32_t call_index = *((int32_t*)data); /* TODO: Or pass an array of call_idx's */ |     TOXAV_ERR_SEND_FRAME error; | ||||||
|  |     uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */ | ||||||
|  |     int64_t sample_count = ((int64_t) CallControl.audio_sample_rate) * \ | ||||||
|  |                            ((int64_t) CallControl.audio_frame_duration) / 1000; | ||||||
|  |  | ||||||
|     uint8_t encoded_payload[RTP_PAYLOAD_SIZE]; |     if ( sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number, | ||||||
|     int32_t payload_size = toxav_prepare_audio_frame(ASettins.av, call_index, encoded_payload, RTP_PAYLOAD_SIZE, captured, size); |             captured, sample_count, | ||||||
|     if ( payload_size <= 0 || toxav_send_audio(ASettins.av, call_index, encoded_payload, payload_size) < 0 ) { |             CallControl.audio_channels, | ||||||
|         /*fprintf(stderr, "Could not encode audio packet\n");*/ |             CallControl.audio_sample_rate, &error) == false ) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void write_device_callback(void *agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg) | void write_device_callback(uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels, | ||||||
|  |                            uint32_t sample_rate) | ||||||
| { | { | ||||||
|     (void)arg; |     if ( CallControl.calls[friend_number].ttas ) | ||||||
|     (void)agent; |         write_out(CallControl.calls[friend_number].out_idx, PCM, sample_count, channels, sample_rate); | ||||||
|  |  | ||||||
|     if (call_index >= 0 && ASettins.calls[call_index].ttas) { |  | ||||||
|         ToxAvCSettings csettings = ASettins.cs; |  | ||||||
|         toxav_get_peer_csettings(ASettins.av, call_index, 0, &csettings); |  | ||||||
|         write_out(ASettins.calls[call_index].out_idx, PCM, size, csettings.audio_channels); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int start_transmission(ToxWindow *self, Call *call) | int start_transmission(ToxWindow *self, Call *call) | ||||||
| { | { | ||||||
|     if ( !self || !ASettins.av || self->call_idx == -1 ) { |     if ( !self || !CallControl.av ) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission"); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Don't provide support for video */ |  | ||||||
|     if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_idx, 0) ) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission"); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ( !toxav_capability_supported(ASettins.av, self->call_idx, av_AudioDecoding) || |  | ||||||
|          !toxav_capability_supported(ASettins.av, self->call_idx, av_AudioEncoding) ) |  | ||||||
|         return -1; |  | ||||||
|  |  | ||||||
|     if (set_call(call, true) == -1) |     if (set_call(call, true) == -1) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     ToxAvCSettings csettings; |     DeviceError error = open_primary_device(input, &call->in_idx, | ||||||
|     toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings); |                                             CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels); | ||||||
|  |  | ||||||
|     if ( open_primary_device(input, &call->in_idx, |     if ( error != de_None ) { | ||||||
|             csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None ) |         if ( error == de_FailedStart) | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input device!"); |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to start input device"); | ||||||
|  |  | ||||||
|     if ( register_device_callback(self->call_idx, call->in_idx, |         if ( error == de_InternalError ) | ||||||
|          read_device_callback, &self->call_idx, true) != de_None) |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening input device"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( register_device_callback(self->num, call->in_idx, | ||||||
|  |                                   read_device_callback, &self->num, true) != de_None) | ||||||
|         /* Set VAD as true for all; TODO: Make it more dynamic */ |         /* Set VAD as true for all; TODO: Make it more dynamic */ | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!"); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!"); | ||||||
|  |  | ||||||
|     if ( open_primary_device(output, &call->out_idx, |     if ( open_primary_device(output, &call->out_idx, | ||||||
|             csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None ) { |                              CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None ) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!"); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!"); | ||||||
|         call->has_output = 0; |         call->has_output = 0; | ||||||
|     } |     } | ||||||
| @@ -224,22 +225,31 @@ int start_transmission(ToxWindow *self, Call *call) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int stop_transmission(Call *call, int32_t call_index) | int stop_transmission(Call *call, uint32_t friend_number) | ||||||
| { | { | ||||||
|     if ( call->ttas ) { |     if ( call->ttas ) { | ||||||
|         toxav_kill_transmission(ASettins.av, call_index); |         TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK; | ||||||
|         call->ttas = false; |  | ||||||
|  |  | ||||||
|         if ( call->in_idx != -1 ) |         if ( CallControl.call_state > TOXAV_FRIEND_CALL_STATE_FINISHED ) | ||||||
|             close_device(input, call->in_idx); |             toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_CANCEL, &error); | ||||||
|  |  | ||||||
|         if ( call->out_idx != -1 ) |         if ( error == TOXAV_ERR_CALL_CONTROL_OK ) { | ||||||
|             close_device(output, call->out_idx); |             call->ttas = false; | ||||||
|  |  | ||||||
|  |             if ( call->in_idx != -1 ) | ||||||
|  |                 close_device(input, call->in_idx); | ||||||
|  |  | ||||||
|  |             if ( call->out_idx != -1 ) | ||||||
|  |                 close_device(output, call->out_idx); | ||||||
|  |  | ||||||
|  |             if ( set_call(call, false) == -1 ) | ||||||
|  |                 return -1; | ||||||
|  |  | ||||||
|  |             return 0; | ||||||
|  |         } else { | ||||||
|  |  | ||||||
|         if (set_call(call, false) == -1) |  | ||||||
|             return -1; |             return -1; | ||||||
|  |         } | ||||||
|         return 0; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return -1; |     return -1; | ||||||
| @@ -255,85 +265,194 @@ int stop_transmission(Call *call, int32_t call_index) | |||||||
| /* | /* | ||||||
|  * Callbacks |  * Callbacks | ||||||
|  */ |  */ | ||||||
|  | void call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) | ||||||
| #define CB_BODY(call_idx, Arg, onFunc) do { ToxWindow* windows = (Arg); int i;\ |  | ||||||
| for (i = 0; i < MAX_WINDOWS_NUM; ++i) if (windows[i].onFunc != NULL) windows[i].onFunc(&windows[i], ASettins.av, call_idx); } while (0) |  | ||||||
|  |  | ||||||
| void callback_recv_invite ( void* av, int32_t call_index, void* arg ) |  | ||||||
| { | { | ||||||
|     CB_BODY(call_index, arg, onInvite); |     Tox *m = (Tox *) user_data; | ||||||
|  |     CallControl.pending_call = true; | ||||||
|  |     callback_recv_invite(m, friend_number); | ||||||
| } | } | ||||||
| void callback_recv_ringing ( void* av, int32_t call_index, void* arg ) |  | ||||||
|  | void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) | ||||||
| { | { | ||||||
|     CB_BODY(call_index, arg, onRinging); |     CallControl.call_state = state; | ||||||
| } |  | ||||||
| void callback_recv_starting ( void* av, int32_t call_index, void* arg ) |     switch ( state ) { | ||||||
| { |         case ( TOXAV_FRIEND_CALL_STATE_ERROR ): | ||||||
|     ToxWindow* windows = arg; |             line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!"); | ||||||
|     int i; |  | ||||||
|     for (i = 0; i < MAX_WINDOWS_NUM; ++i) | #ifdef VIDEO | ||||||
|         if (windows[i].onStarting != NULL && windows[i].call_idx == call_index) { |             callback_video_end(friend_number); | ||||||
|             windows[i].onStarting(&windows[i], ASettins.av, call_index); | #endif /* VIDEO */ | ||||||
|             if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index])) {/* YEAH! */ |  | ||||||
|                 line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!"); |             stop_transmission(&CallControl.calls[friend_number], friend_number); | ||||||
|  |             callback_call_ended(friend_number); | ||||||
|  |             CallControl.pending_call = false; | ||||||
|  |  | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case ( TOXAV_FRIEND_CALL_STATE_FINISHED ): | ||||||
|  |             if ( CallControl.pending_call ) | ||||||
|  |                 callback_call_rejected(friend_number); | ||||||
|  |             else | ||||||
|  |                 callback_call_ended(friend_number); | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |             callback_recv_video_end(friend_number); | ||||||
|  |             callback_video_end(friend_number); | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  |             stop_transmission(&CallControl.calls[friend_number], friend_number); | ||||||
|  |  | ||||||
|  |             /* Reset stored call state after finishing */ | ||||||
|  |             CallControl.call_state = 0; | ||||||
|  |             CallControl.pending_call = false; | ||||||
|  |  | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         default: | ||||||
|  |             if ( CallControl.pending_call ) { | ||||||
|  |                 /* Start answered call */ | ||||||
|  |                 callback_call_started(friend_number); | ||||||
|  |                 CallControl.pending_call = false; | ||||||
|  |  | ||||||
|  |             } else { | ||||||
|  | #ifdef VIDEO | ||||||
|  |  | ||||||
|  |                 /* Handle receiving client video call states */ | ||||||
|  |                 if ( state & TOXAV_FRIEND_CALL_STATE_SENDING_V ) | ||||||
|  |                     callback_recv_video_starting(friend_number); | ||||||
|  |                 else if ( state & ~TOXAV_FRIEND_CALL_STATE_SENDING_V ) | ||||||
|  |                     callback_recv_video_end(friend_number); | ||||||
|  |  | ||||||
|  | #endif /* VIDEO */ | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, | ||||||
|  |                             int16_t const *pcm, size_t sample_count, | ||||||
|  |                             uint8_t channels, uint32_t sampling_rate, void *user_data) | ||||||
|  | { | ||||||
|  |     write_device_callback(friend_number, pcm, sample_count, channels, sampling_rate); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, | ||||||
|  |                               uint32_t video_bit_rate, void *user_data) | ||||||
|  | { | ||||||
|  |     CallControl.audio_bit_rate = audio_bit_rate; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void callback_recv_invite(Tox *m, uint32_t friend_number) | ||||||
|  | { | ||||||
|  |     if (friend_number >= Friends.max_idx) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     if (Friends.list[friend_number].chatwin == -1) { | ||||||
|  |         if (get_num_active_windows() >= MAX_WINDOWS_NUM) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ToxWindow *windows = CallControl.prompt; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onInvite != NULL && windows[i].num == friend_number) { | ||||||
|  |             windows[i].onInvite(&windows[i], CallControl.av, friend_number, CallControl.call_state); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | void callback_recv_ringing(uint32_t friend_number) | ||||||
|  | { | ||||||
|  |     ToxWindow *windows = CallControl.prompt; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onRinging != NULL && windows[i].num == friend_number) { | ||||||
|  |             windows[i].onRinging(&windows[i], CallControl.av, friend_number, CallControl.call_state); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | void callback_recv_starting(uint32_t friend_number) | ||||||
|  | { | ||||||
|  |     ToxWindow *windows = CallControl.prompt; | ||||||
|  |  | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if ( windows[i].onStarting != NULL && windows[i].num == friend_number ) { | ||||||
|  |             windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state); | ||||||
|  |  | ||||||
|  |             if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */ | ||||||
|  |                 line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!"); | ||||||
|  |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| void callback_recv_ending ( void* av, int32_t call_index, void* arg ) | void callback_recv_ending(uint32_t friend_number) | ||||||
| { | { | ||||||
|     CB_BODY(call_index, arg, onEnding); |     ToxWindow *windows = CallControl.prompt; | ||||||
|     stop_transmission(&ASettins.calls[call_index], call_index); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void callback_call_started ( void* av, int32_t call_index, void* arg ) |  | ||||||
| { |  | ||||||
|     ToxWindow* windows = arg; |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onEnding != NULL && windows[i].num == friend_number) { | ||||||
|  |             windows[i].onEnding(&windows[i], CallControl.av, friend_number, CallControl.call_state); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | void callback_call_started(uint32_t friend_number) | ||||||
|  | { | ||||||
|  |     ToxWindow *windows = CallControl.prompt; | ||||||
|  |  | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_WINDOWS_NUM; ++i) |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) | ||||||
|         if (windows[i].onStart != NULL && windows[i].call_idx == call_index) { |         if ( windows[i].onStart != NULL && windows[i].num == friend_number ) { | ||||||
|             windows[i].onStart(&windows[i], ASettins.av, call_index); |             windows[i].onStart(&windows[i], CallControl.av, friend_number, CallControl.call_state); | ||||||
|             if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index]) ) {/* YEAH! */ |  | ||||||
|  |             if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) {/* YEAH! */ | ||||||
|                 line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!"); |                 line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!"); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| } | } | ||||||
| void callback_call_canceled ( void* av, int32_t call_index, void* arg ) | void callback_call_canceled(uint32_t friend_number) | ||||||
| { | { | ||||||
|     CB_BODY(call_index, arg, onCancel); |     ToxWindow *windows = CallControl.prompt; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|     /* In case call is active */ |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|     stop_transmission(&ASettins.calls[call_index], call_index); |         if (windows[i].onCancel != NULL && windows[i].num == friend_number) { | ||||||
|  |             windows[i].onCancel(&windows[i], CallControl.av, friend_number, CallControl.call_state); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| void callback_call_rejected ( void* av, int32_t call_index, void* arg ) | void callback_call_rejected(uint32_t friend_number) | ||||||
| { | { | ||||||
|     CB_BODY(call_index, arg, onReject); |     ToxWindow *windows = CallControl.prompt; | ||||||
| } |     int i; | ||||||
| void callback_call_ended ( void* av, int32_t call_index, void* arg ) |  | ||||||
| { |  | ||||||
|     CB_BODY(call_index, arg, onEnd); |  | ||||||
|     stop_transmission(&ASettins.calls[call_index], call_index); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void callback_requ_timeout ( void* av, int32_t call_index, void* arg ) |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
| { |         if (windows[i].onReject != NULL && windows[i].num == friend_number) { | ||||||
|     CB_BODY(call_index, arg, onRequestTimeout); |             windows[i].onReject(&windows[i], CallControl.av, friend_number, CallControl.call_state); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| void callback_peer_timeout ( void* av, int32_t call_index, void* arg ) | void callback_call_ended(uint32_t friend_number) | ||||||
| { | { | ||||||
|     CB_BODY(call_index, arg, onPeerTimeout); |     ToxWindow *windows = CallControl.prompt; | ||||||
|     stop_transmission(&ASettins.calls[call_index], call_index); |     int i; | ||||||
|     /* Call is stopped manually since there might be some other |  | ||||||
|      * actions that one can possibly take on timeout |  | ||||||
|      */ |  | ||||||
|     toxav_stop_call(ASettins.av, call_index); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // void callback_media_change(void* av, int32_t call_index, void* arg) |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
| // { |         if (windows[i].onEnd != NULL && windows[i].num == friend_number) { | ||||||
|   /*... TODO cancel all media change requests */ |             windows[i].onEnd(&windows[i], CallControl.av, friend_number, CallControl.call_state); | ||||||
| // } |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * End of Callbacks |  * End of Callbacks | ||||||
| @@ -345,33 +464,43 @@ void callback_peer_timeout ( void* av, int32_t call_index, void* arg ) | |||||||
|  */ |  */ | ||||||
| void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     TOXAV_ERR_CALL error; | ||||||
|     const char *error_str; |     const char *error_str; | ||||||
|  |  | ||||||
|     if (argc != 0) { |     if ( argc != 0 ) { | ||||||
|         error_str = "Unknown arguments."; |         error_str = "Unknown arguments."; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ( !ASettins.av ) { |     if ( !CallControl.av ) { | ||||||
|         error_str = "Audio not supported!"; |         error_str = "ToxAV not supported!"; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!self->stb->connection) { |     if ( !self->stb->connection ) { | ||||||
|         error_str = "Friend is offline."; |         error_str = "Friend is offline."; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, &ASettins.cs, 30); |     if ( CallControl.pending_call ) { | ||||||
|  |         error_str = "Already a pending call!"; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ( error != av_ErrorNone ) { |     toxav_call(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error); | ||||||
|         if ( error == av_ErrorAlreadyInCallWithPeer ) error_str = "Already in a call!"; |  | ||||||
|  |     if ( error != TOXAV_ERR_CALL_OK ) { | ||||||
|  |         if ( error == TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL ) error_str = "Already in a call!"; | ||||||
|  |         else if ( error == TOXAV_ERR_CALL_MALLOC ) error_str = "Memory allocation issue"; | ||||||
|  |         else if ( error == TOXAV_ERR_CALL_FRIEND_NOT_FOUND ) error_str = "Friend number invalid"; | ||||||
|  |         else if ( error == TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED ) error_str = "Friend is valid but not currently connected"; | ||||||
|         else error_str = "Internal error!"; |         else error_str = "Internal error!"; | ||||||
|  |  | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Calling... idx: %d", self->call_idx); |     CallControl.pending_call = true; | ||||||
|  |     callback_recv_ringing(self->num); | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
| on_error: | on_error: | ||||||
| @@ -380,29 +509,39 @@ on_error: | |||||||
|  |  | ||||||
| void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     TOXAV_ERR_ANSWER error; | ||||||
|     const char *error_str; |     const char *error_str; | ||||||
|  |  | ||||||
|     if (argc != 0) { |     if ( argc != 0 ) { | ||||||
|         error_str = "Unknown arguments."; |         error_str = "Unknown arguments."; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ( !ASettins.av ) { |     if ( !CallControl.av ) { | ||||||
|         error_str = "Audio not supported!"; |         error_str = "Audio not supported!"; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ToxAvError error = toxav_answer(ASettins.av, self->call_idx, &ASettins.cs); |     if ( !CallControl.pending_call ) { | ||||||
|  |         error_str = "No incoming call!"; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ( error != av_ErrorNone ) { |     toxav_answer(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error); | ||||||
|         if ( error == av_ErrorInvalidState ) error_str = "Cannot answer in invalid state!"; |  | ||||||
|         else if ( error == av_ErrorNoCall ) error_str = "No incoming call!"; |     if ( error != TOXAV_ERR_ANSWER_OK ) { | ||||||
|  |         if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING ) error_str = "No incoming call!"; | ||||||
|  |         else if ( error == TOXAV_ERR_ANSWER_CODEC_INITIALIZATION ) error_str = "Failed to initialize codecs!"; | ||||||
|  |         else if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND ) error_str = "Friend not found!"; | ||||||
|  |         else if ( error == TOXAV_ERR_ANSWER_INVALID_BIT_RATE ) error_str = "Invalid bit rate!"; | ||||||
|         else error_str = "Internal error!"; |         else error_str = "Internal error!"; | ||||||
|  |  | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Callback will print status... */ |     /* Callback will print status... */ | ||||||
|  |     callback_recv_starting(self->num); | ||||||
|  |     CallControl.pending_call = false; | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
| on_error: | on_error: | ||||||
| @@ -413,27 +552,27 @@ void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
| { | { | ||||||
|     const char *error_str; |     const char *error_str; | ||||||
|  |  | ||||||
|     if (argc != 0) { |     if ( argc != 0 ) { | ||||||
|         error_str = "Unknown arguments."; |         error_str = "Unknown arguments."; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ( !ASettins.av ) { |     if ( !CallControl.av ) { | ||||||
|         error_str = "Audio not supported!"; |         error_str = "Audio not supported!"; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ToxAvError error = toxav_reject(ASettins.av, self->call_idx, "Why not?"); |     if ( !CallControl.pending_call ) { | ||||||
|  |         error_str = "No incoming call!"; | ||||||
|     if ( error != av_ErrorNone ) { |  | ||||||
|         if ( error == av_ErrorInvalidState ) error_str = "Cannot reject in invalid state!"; |  | ||||||
|         else if ( error == av_ErrorNoCall ) error_str = "No incoming call!"; |  | ||||||
|         else error_str = "Internal error!"; |  | ||||||
|  |  | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /* Manually send a cancel call control because call hasn't started */ | ||||||
|  |     toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL); | ||||||
|  |     CallControl.pending_call = false; | ||||||
|  |  | ||||||
|     /* Callback will print status... */ |     /* Callback will print status... */ | ||||||
|  |     callback_call_rejected(self->num); | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
| on_error: | on_error: | ||||||
| @@ -442,39 +581,28 @@ on_error: | |||||||
|  |  | ||||||
| void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     const char *error_str; |     const char *error_str = NULL; | ||||||
|  |  | ||||||
|     if (argc != 0) { |     if ( !CallControl.av ) { | ||||||
|         error_str = "Unknown arguments."; |  | ||||||
|         goto on_error; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ( !ASettins.av ) { |  | ||||||
|         error_str = "Audio not supported!"; |         error_str = "Audio not supported!"; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ToxAvError error; |     if ( argc != 0 ) { | ||||||
|  |         error_str = "Unknown arguments."; | ||||||
|     if (toxav_get_call_state(ASettins.av, self->call_idx) == av_CallInviting) { |  | ||||||
|         error = toxav_cancel(ASettins.av, self->call_idx, self->num, |  | ||||||
|                                         "Only those who appreciate small things know the beauty that is life"); |  | ||||||
| #ifdef SOUND_NOTIFY |  | ||||||
|         stop_sound(self->ringing_sound); |  | ||||||
| #endif |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!"); |  | ||||||
|     } else { |  | ||||||
|         error = toxav_hangup(ASettins.av, self->call_idx); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ( error != av_ErrorNone ) { |  | ||||||
|         if ( error == av_ErrorInvalidState ) error_str = "Cannot hangup in invalid state!"; |  | ||||||
|         else if ( error == av_ErrorNoCall ) error_str = "No call!"; |  | ||||||
|         else error_str = "Internal error!"; |  | ||||||
|  |  | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ( !self->is_call && !CallControl.pending_call ) { | ||||||
|  |         error_str = "Not in a call."; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |     callback_video_end(self->num); | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  |     stop_current_call(self); | ||||||
|     return; |     return; | ||||||
| on_error: | on_error: | ||||||
|     print_err (self, error_str); |     print_err (self, error_str); | ||||||
| @@ -547,7 +675,7 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char ( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ( set_primary_device(type, selection) == de_InvalidSelection ) { |     if ( set_primary_device(type, selection) == de_InvalidSelection ) { | ||||||
|         error_str="Invalid selection!"; |         error_str = "Invalid selection!"; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -591,33 +719,31 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ( selection_valid(type, selection) == de_InvalidSelection ) { |     if ( selection_valid(type, selection) == de_InvalidSelection ) { | ||||||
|         error_str="Invalid selection!"; |         error_str = "Invalid selection!"; | ||||||
|         goto on_error; |         goto on_error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If call is active, change device */ |     /* If call is active, change device */ | ||||||
|     if ( self->call_idx > -1) { |     if ( self->is_call ) { | ||||||
|         Call* this_call = &ASettins.calls[self->call_idx]; |         Call *this_call = &CallControl.calls[self->num]; | ||||||
|         if (this_call->ttas) { |  | ||||||
|  |  | ||||||
|             ToxAvCSettings csettings; |         if ( this_call->ttas ) { | ||||||
|             toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings); |  | ||||||
|  |  | ||||||
|             if (type == output) { |  | ||||||
|  |             if ( type == output ) { | ||||||
|                 pthread_mutex_lock(&this_call->mutex); |                 pthread_mutex_lock(&this_call->mutex); | ||||||
|                 close_device(output, this_call->out_idx); |                 close_device(output, this_call->out_idx); | ||||||
|                 this_call->has_output = open_device(output, selection, &this_call->out_idx, |                 this_call->has_output = open_device(output, selection, &this_call->out_idx, | ||||||
|                     csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) |                                                     CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) | ||||||
|                     == de_None ? 1 : 0; |                                         == de_None ? 1 : 0; | ||||||
|                 pthread_mutex_unlock(&this_call->mutex); |                 pthread_mutex_unlock(&this_call->mutex); | ||||||
|             } |             } else { | ||||||
|             else { |  | ||||||
|                 /* TODO: check for failure */ |                 /* TODO: check for failure */ | ||||||
|                 close_device(input, this_call->in_idx); |                 close_device(input, this_call->in_idx); | ||||||
|                 open_device(input, selection, &this_call->in_idx, csettings.audio_sample_rate, |                 open_device(input, selection, &this_call->in_idx, CallControl.audio_sample_rate, | ||||||
|                     csettings.audio_frame_duration, csettings.audio_channels); |                             CallControl.audio_frame_duration, CallControl.audio_channels); | ||||||
|                 /* Set VAD as true for all; TODO: Make it more dynamic */ |                 /* Set VAD as true for all; TODO: Make it more dynamic */ | ||||||
|                 register_device_callback(self->call_idx, this_call->in_idx, read_device_callback, &self->call_idx, true); |                 register_device_callback(self->num, this_call->in_idx, read_device_callback, &self->num, true); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -625,7 +751,7 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a | |||||||
|     self->device_selection[type] = selection; |     self->device_selection[type] = selection; | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
|     on_error: | on_error: | ||||||
|     print_err (self, error_str); |     print_err (self, error_str); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -655,23 +781,25 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|  |  | ||||||
|  |  | ||||||
|     /* If call is active, use this_call values */ |     /* If call is active, use this_call values */ | ||||||
|     if ( self->call_idx > -1) { |     if ( self->is_call ) { | ||||||
|         Call* this_call = &ASettins.calls[self->call_idx]; |         Call *this_call = &CallControl.calls[self->num]; | ||||||
|  |  | ||||||
|         pthread_mutex_lock(&this_call->mutex); |         pthread_mutex_lock(&this_call->mutex); | ||||||
|         if (type == input) { |  | ||||||
|  |         if ( type == input ) { | ||||||
|             device_mute(type, this_call->in_idx); |             device_mute(type, this_call->in_idx); | ||||||
|             self->chatwin->infobox.in_is_muted ^= 1; |             self->chatwin->infobox.in_is_muted ^= 1; | ||||||
|         } else { |         } else { | ||||||
|             device_mute(type, this_call->out_idx); |             device_mute(type, this_call->out_idx); | ||||||
|             self->chatwin->infobox.out_is_muted ^= 1; |             self->chatwin->infobox.out_is_muted ^= 1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         pthread_mutex_unlock(&this_call->mutex); |         pthread_mutex_unlock(&this_call->mutex); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
|     on_error: | on_error: | ||||||
|     print_err (self, error_str); |     print_err (self, error_str); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -695,8 +823,8 @@ void cmd_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[M | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Call must be active */ |     /* Call must be active */ | ||||||
|     if ( self->call_idx > -1) { |     if ( self->is_call ) { | ||||||
|         device_set_VAD_treshold(ASettins.calls[self->call_idx].in_idx, value); |         device_set_VAD_treshold(CallControl.calls[self->num].in_idx, value); | ||||||
|         self->chatwin->infobox.vad_lvl = value; |         self->chatwin->infobox.vad_lvl = value; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -707,25 +835,15 @@ on_error: | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void stop_current_call(ToxWindow* self) | void stop_current_call(ToxWindow *self) | ||||||
| { | { | ||||||
|     ToxAvCallState callstate; |     if ( CallControl.pending_call ) { | ||||||
|     if ( ASettins.av != NULL && self->call_idx != -1 && |         toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL); | ||||||
|        ( callstate = toxav_get_call_state(ASettins.av, self->call_idx) ) != av_CallNonExistent) { |         callback_call_canceled(self->num); | ||||||
|         switch (callstate) |     } else { | ||||||
|         { |         stop_transmission(&CallControl.calls[self->num], self->num); | ||||||
|         case av_CallActive: |         callback_call_ended(self->num); | ||||||
|         case av_CallHold: |  | ||||||
|             toxav_hangup(ASettins.av, self->call_idx); |  | ||||||
|             break; |  | ||||||
|         case av_CallInviting: |  | ||||||
|             toxav_cancel(ASettins.av, self->call_idx, 0, "Not interested anymore"); |  | ||||||
|             break; |  | ||||||
|         case av_CallStarting: |  | ||||||
|             toxav_reject(ASettins.av, self->call_idx, "Not interested"); |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     CallControl.pending_call = false; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,12 +20,14 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef AUDIO_H | #ifndef AUDIO_CALL_H | ||||||
| #define AUDIO_H | #define AUDIO_CALL_H | ||||||
|  |  | ||||||
| #include <tox/toxav.h> | #include <tox/toxav.h> | ||||||
|  |  | ||||||
| #include "device.h" | #include "audio_device.h" | ||||||
|  |  | ||||||
|  | #define MAX_CALLS 10 | ||||||
|  |  | ||||||
| typedef enum _AudioError { | typedef enum _AudioError { | ||||||
|     ae_None = 0, |     ae_None = 0, | ||||||
| @@ -34,18 +36,58 @@ typedef enum _AudioError { | |||||||
|     ae_StartingCoreAudio = 1 << 2 |     ae_StartingCoreAudio = 1 << 2 | ||||||
| } AudioError; | } AudioError; | ||||||
|  |  | ||||||
|  | #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 { | typedef struct Call { | ||||||
|     pthread_t ttid; /* Transmission thread id */ |     pthread_t ttid; /* Transmission thread id */ | ||||||
|     bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */ |     bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */ | ||||||
|     uint32_t in_idx, out_idx; |     uint32_t in_idx, out_idx; /* Audio Index */ | ||||||
|  | #ifdef VIDEO | ||||||
|  |     uint32_t vin_idx, vout_idx; /* Video Index */ | ||||||
|  | #endif /* VIDEO */ | ||||||
|     pthread_mutex_t mutex; |     pthread_mutex_t mutex; | ||||||
| } Call; | } 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 */ | /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | ||||||
| ToxAv *init_audio(ToxWindow *self, Tox *tox); | ToxAV *init_audio(ToxWindow *self, Tox *tox); | ||||||
| void terminate_audio(); | void terminate_audio(); | ||||||
| int start_transmission(ToxWindow *self, Call *call); | int start_transmission(ToxWindow *self, Call *call); | ||||||
| int stop_transmission(Call *call, int call_index); | int stop_transmission(Call *call, uint32_t friend_number); | ||||||
| void stop_current_call(ToxWindow *self); | void stop_current_call(ToxWindow *self); | ||||||
|  |  | ||||||
| #endif /* AUDIO_H */ | #endif /* AUDIO_CALL_H */ | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| /*  device.c
 | /*  audio_device.c
 | ||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
| @@ -20,7 +20,7 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include "device.h" | #include "audio_device.h" | ||||||
| 
 | 
 | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| #include "audio_call.h" | #include "audio_call.h" | ||||||
| @@ -56,8 +56,8 @@ typedef struct Device { | |||||||
|     ALCdevice  *dhndl;                     /* Handle of device selected/opened */ |     ALCdevice  *dhndl;                     /* Handle of device selected/opened */ | ||||||
|     ALCcontext *ctx;                       /* Device context */ |     ALCcontext *ctx;                       /* Device context */ | ||||||
|     DataHandleCallback cb;                 /* Use this to handle data from input device usually */ |     DataHandleCallback cb;                 /* Use this to handle data from input device usually */ | ||||||
|     void* cb_data;                         /* Data to be passed to callback */ |     void *cb_data;                         /* Data to be passed to callback */ | ||||||
|     int32_t call_idx;                      /* ToxAv call index */ |     int32_t friend_number;                      /* ToxAV friend number */ | ||||||
| 
 | 
 | ||||||
|     uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */ |     uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */ | ||||||
|     uint32_t ref_count; |     uint32_t ref_count; | ||||||
| @@ -80,7 +80,7 @@ Device *running[2][MAX_DEVICES] = {{NULL}};     /* Running devices */ | |||||||
| uint32_t primary_device[2];          /* Primary device */ | uint32_t primary_device[2];          /* Primary device */ | ||||||
| 
 | 
 | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| static ToxAv* av = NULL; | static ToxAV *av = NULL; | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| 
 | 
 | ||||||
| /* q_mutex */ | /* q_mutex */ | ||||||
| @@ -90,12 +90,12 @@ pthread_mutex_t mutex; | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| bool thread_running = true, | bool thread_running = true, | ||||||
|       thread_paused = true;               /* Thread control */ |      thread_paused = true;               /* Thread control */ | ||||||
| 
 | 
 | ||||||
| void* thread_poll(void*); | void *thread_poll(void *); | ||||||
| /* Meet devices */ | /* Meet devices */ | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| DeviceError init_devices(ToxAv* av_) | DeviceError init_devices(ToxAV *av_) | ||||||
| #else | #else | ||||||
| DeviceError init_devices() | DeviceError init_devices() | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| @@ -103,6 +103,7 @@ DeviceError init_devices() | |||||||
|     const char *stringed_device_list; |     const char *stringed_device_list; | ||||||
| 
 | 
 | ||||||
|     size[input] = 0; |     size[input] = 0; | ||||||
|  | 
 | ||||||
|     if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) { |     if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) { | ||||||
|         ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); |         ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); | ||||||
| 
 | 
 | ||||||
| @@ -113,7 +114,13 @@ DeviceError init_devices() | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     size[output] = 0; |     size[output] = 0; | ||||||
|     if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) { | 
 | ||||||
|  |     if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) | ||||||
|  |         stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); | ||||||
|  |     else | ||||||
|  |         stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER); | ||||||
|  | 
 | ||||||
|  |     if (stringed_device_list) { | ||||||
|         ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); |         ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); | ||||||
| 
 | 
 | ||||||
|         for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) { |         for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) { | ||||||
| @@ -127,6 +134,7 @@ DeviceError init_devices() | |||||||
|         return de_InternalError; |         return de_InternalError; | ||||||
| 
 | 
 | ||||||
|     pthread_t thread_id; |     pthread_t thread_id; | ||||||
|  | 
 | ||||||
|     if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) |     if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) | ||||||
|         return de_InternalError; |         return de_InternalError; | ||||||
| 
 | 
 | ||||||
| @@ -155,9 +163,10 @@ DeviceError terminate_devices() | |||||||
| DeviceError device_mute(DeviceType type, uint32_t device_idx) | DeviceError device_mute(DeviceType type, uint32_t device_idx) | ||||||
| { | { | ||||||
|     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; |     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||||
|  | 
 | ||||||
|     lock; |     lock; | ||||||
| 
 | 
 | ||||||
|     Device* device = running[type][device_idx]; |     Device *device = running[type][device_idx]; | ||||||
| 
 | 
 | ||||||
|     if (!device) { |     if (!device) { | ||||||
|         unlock; |         unlock; | ||||||
| @@ -174,9 +183,10 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx) | |||||||
| DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) | DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) | ||||||
| { | { | ||||||
|     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; |     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||||
|  | 
 | ||||||
|     lock; |     lock; | ||||||
| 
 | 
 | ||||||
|     Device* device = running[input][device_idx]; |     Device *device = running[input][device_idx]; | ||||||
| 
 | 
 | ||||||
|     if (!device) { |     if (!device) { | ||||||
|         unlock; |         unlock; | ||||||
| @@ -194,12 +204,14 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) | |||||||
| DeviceError set_primary_device(DeviceType type, int32_t selection) | DeviceError set_primary_device(DeviceType type, int32_t selection) | ||||||
| { | { | ||||||
|     if (size[type] <= selection || selection < 0) return de_InvalidSelection; |     if (size[type] <= selection || selection < 0) return de_InvalidSelection; | ||||||
|  | 
 | ||||||
|     primary_device[type] = selection; |     primary_device[type] = selection; | ||||||
| 
 | 
 | ||||||
|     return de_None; |     return de_None; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, 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); |     return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels); | ||||||
| } | } | ||||||
| @@ -210,7 +222,8 @@ void get_primary_device_name(DeviceType type, char *buf, int size) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: generate buffers separately
 | // 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 (size[type] <= selection || selection < 0) return de_InvalidSelection; | ||||||
| 
 | 
 | ||||||
| @@ -221,10 +234,13 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx | |||||||
|     const uint32_t frame_size = (sample_rate * frame_duration / 1000); |     const uint32_t frame_size = (sample_rate * frame_duration / 1000); | ||||||
| 
 | 
 | ||||||
|     uint32_t i; |     uint32_t i; | ||||||
|  | 
 | ||||||
|     for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i); |     for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i); | ||||||
| 
 | 
 | ||||||
|     if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; } |     if (i == MAX_DEVICES) { | ||||||
|     else *device_idx = i; |         unlock; | ||||||
|  |         return de_AllDevicesBusy; | ||||||
|  |     } else *device_idx = i; | ||||||
| 
 | 
 | ||||||
|     for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */ |     for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */ | ||||||
|         if ( running[type][i] && running[type][i]->selection == selection ) { |         if ( running[type][i] && running[type][i]->selection == selection ) { | ||||||
| @@ -238,7 +254,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Device* device = running[type][*device_idx] = calloc(1, sizeof(Device)); |     Device *device = running[type][*device_idx] = calloc(1, sizeof(Device)); | ||||||
|     device->selection = selection; |     device->selection = selection; | ||||||
| 
 | 
 | ||||||
|     device->sample_rate = sample_rate; |     device->sample_rate = sample_rate; | ||||||
| @@ -254,12 +270,12 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx | |||||||
|     if (type == input) { |     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); |                                              sample_rate, device->sound_mode, frame_size * 2); | ||||||
|     #ifdef AUDIO | #ifdef AUDIO | ||||||
|         device->VAD_treshold = user_settings->VAD_treshold; |         device->VAD_treshold = user_settings->VAD_treshold; | ||||||
|     #endif | #endif | ||||||
|     } |     } else { | ||||||
|     else { |  | ||||||
|         device->dhndl = alcOpenDevice(devices_names[type][selection]); |         device->dhndl = alcOpenDevice(devices_names[type][selection]); | ||||||
|  | 
 | ||||||
|         if ( !device->dhndl ) { |         if ( !device->dhndl ) { | ||||||
|             free(device); |             free(device); | ||||||
|             running[type][*device_idx] = NULL; |             running[type][*device_idx] = NULL; | ||||||
| @@ -275,10 +291,10 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx | |||||||
|         alSourcei(device->source, AL_LOOPING, AL_FALSE); |         alSourcei(device->source, AL_LOOPING, AL_FALSE); | ||||||
| 
 | 
 | ||||||
|         uint16_t zeros[frame_size]; |         uint16_t zeros[frame_size]; | ||||||
|         memset(zeros, 0, frame_size*2); |         memset(zeros, 0, frame_size * 2); | ||||||
| 
 | 
 | ||||||
|         for ( i = 0; i < OPENAL_BUFS; ++i ) { |         for ( i = 0; i < OPENAL_BUFS; ++i ) { | ||||||
|             alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate); |             alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers); |         alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers); | ||||||
| @@ -306,7 +322,7 @@ DeviceError close_device(DeviceType type, uint32_t device_idx) | |||||||
|     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; |     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||||
| 
 | 
 | ||||||
|     lock; |     lock; | ||||||
|     Device* device = running[type][device_idx]; |     Device *device = running[type][device_idx]; | ||||||
|     DeviceError rc = de_None; |     DeviceError rc = de_None; | ||||||
| 
 | 
 | ||||||
|     if (!device) { |     if (!device) { | ||||||
| @@ -317,32 +333,30 @@ DeviceError close_device(DeviceType type, uint32_t device_idx) | |||||||
|     running[type][device_idx] = NULL; |     running[type][device_idx] = NULL; | ||||||
| 
 | 
 | ||||||
|     if ( !device->ref_count ) { |     if ( !device->ref_count ) { | ||||||
| 
 |  | ||||||
| //         printf("Closed device ");
 |  | ||||||
| 
 |  | ||||||
|         if (type == input) { |         if (type == input) { | ||||||
|             if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError; |             if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError; | ||||||
|         } |         } else { | ||||||
|         else { |  | ||||||
|             if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx); |             if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx); | ||||||
| 
 | 
 | ||||||
|             alDeleteSources(1, &device->source); |             alDeleteSources(1, &device->source); | ||||||
|             alDeleteBuffers(OPENAL_BUFS, device->buffers); |             alDeleteBuffers(OPENAL_BUFS, device->buffers); | ||||||
| 
 | 
 | ||||||
|             alcMakeContextCurrent(NULL); |             alcMakeContextCurrent(NULL); | ||||||
|  | 
 | ||||||
|             if ( device->ctx ) alcDestroyContext(device->ctx); |             if ( device->ctx ) alcDestroyContext(device->ctx); | ||||||
|  | 
 | ||||||
|             if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError; |             if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         free(device); |         free(device); | ||||||
|     } |     } else device->ref_count--; | ||||||
|     else device->ref_count--; |  | ||||||
| 
 | 
 | ||||||
|     unlock; |     unlock; | ||||||
|     return rc; |     return rc; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD) | DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, | ||||||
|  |                                       void *data, bool enable_VAD) | ||||||
| { | { | ||||||
|     if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) |     if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) | ||||||
|         return de_InvalidSelection; |         return de_InvalidSelection; | ||||||
| @@ -351,17 +365,18 @@ DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, Dat | |||||||
|     running[input][device_idx]->cb = callback; |     running[input][device_idx]->cb = callback; | ||||||
|     running[input][device_idx]->cb_data = data; |     running[input][device_idx]->cb_data = data; | ||||||
|     running[input][device_idx]->enable_VAD = enable_VAD; |     running[input][device_idx]->enable_VAD = enable_VAD; | ||||||
|     running[input][device_idx]->call_idx = call_idx; |     running[input][device_idx]->friend_number = friend_number; | ||||||
|     unlock; |     unlock; | ||||||
| 
 | 
 | ||||||
|     return de_None; |     return de_None; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels) | inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels, | ||||||
|  |                                uint32_t sample_rate) | ||||||
| { | { | ||||||
|     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; |     if (device_idx >= MAX_DEVICES) return de_InvalidSelection; | ||||||
| 
 | 
 | ||||||
|     Device* device = running[output][device_idx]; |     Device *device = running[output][device_idx]; | ||||||
| 
 | 
 | ||||||
|     if (!device || device->muted) return de_DeviceNotActive; |     if (!device || device->muted) return de_DeviceNotActive; | ||||||
| 
 | 
 | ||||||
| @@ -373,33 +388,33 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_ | |||||||
|     alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed); |     alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed); | ||||||
|     alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued); |     alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued); | ||||||
| 
 | 
 | ||||||
|     if(processed) { |     if (processed) { | ||||||
|         ALuint bufids[processed]; |         ALuint bufids[processed]; | ||||||
|         alSourceUnqueueBuffers(device->source, processed, bufids); |         alSourceUnqueueBuffers(device->source, processed, bufids); | ||||||
|         alDeleteBuffers(processed - 1, bufids + 1); |         alDeleteBuffers(processed - 1, bufids + 1); | ||||||
|         bufid = bufids[0]; |         bufid = bufids[0]; | ||||||
|     } |     } else if (queued < 16) alGenBuffers(1, &bufid); | ||||||
|     else if(queued < 16) alGenBuffers(1, &bufid); |  | ||||||
|     else { |     else { | ||||||
|         pthread_mutex_unlock(device->mutex); |         pthread_mutex_unlock(device->mutex); | ||||||
|         return de_Busy; |         return de_Busy; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     alBufferData(bufid, device->sound_mode, data, length * 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); |     alSourceQueueBuffers(device->source, 1, &bufid); | ||||||
| 
 | 
 | ||||||
|     ALint state; |     ALint state; | ||||||
|     alGetSourcei(device->source, AL_SOURCE_STATE, &state); |     alGetSourcei(device->source, AL_SOURCE_STATE, &state); | ||||||
| 
 | 
 | ||||||
|     if(state != AL_PLAYING) alSourcePlay(device->source); |     if (state != AL_PLAYING) alSourcePlay(device->source); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     pthread_mutex_unlock(device->mutex); |     pthread_mutex_unlock(device->mutex); | ||||||
|     return de_None; |     return de_None; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* thread_poll (void* arg) // TODO: maybe use thread for every input source
 | void *thread_poll (void *arg) // TODO: maybe use thread for every input source
 | ||||||
| { | { | ||||||
|     /*
 |     /*
 | ||||||
|      * NOTE: We only need to poll input devices for data. |      * NOTE: We only need to poll input devices for data. | ||||||
| @@ -409,23 +424,27 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source | |||||||
|     int32_t sample = 0; |     int32_t sample = 0; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     while (true) |     while (1) { | ||||||
|     { |  | ||||||
|         lock; |         lock; | ||||||
|  | 
 | ||||||
|         if (!thread_running) { |         if (!thread_running) { | ||||||
|             unlock; |             unlock; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         bool paused = thread_paused; | ||||||
|         unlock; |         unlock; | ||||||
| 
 | 
 | ||||||
|         if (thread_paused) usleep(10000); /* Wait for unpause. */ |         /* Wait for unpause. */ | ||||||
|         else |         if (paused) { | ||||||
|         { |             usleep(10000); | ||||||
|             for (i = 0; i < size[input]; ++i) |         } | ||||||
|             { | 
 | ||||||
|  |         else { | ||||||
|  |             for (i = 0; i < size[input]; ++i) { | ||||||
|                 lock; |                 lock; | ||||||
|                 if (running[input][i] != NULL) | 
 | ||||||
|                 { |                 if (running[input][i] != NULL) { | ||||||
|                     alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample); |                     alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample); | ||||||
| 
 | 
 | ||||||
|                     int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000); |                     int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000); | ||||||
| @@ -434,7 +453,8 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source | |||||||
|                         unlock; |                         unlock; | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|                     Device* device = running[input][i]; | 
 | ||||||
|  |                     Device *device = running[input][i]; | ||||||
| 
 | 
 | ||||||
|                     int16_t frame[16000]; |                     int16_t frame[16000]; | ||||||
|                     alcCaptureSamples(device->dhndl, frame, f_size); |                     alcCaptureSamples(device->dhndl, frame, f_size); | ||||||
| @@ -446,8 +466,10 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source | |||||||
| 
 | 
 | ||||||
|                     if ( device->cb ) device->cb(frame, f_size, device->cb_data); |                     if ( device->cb ) device->cb(frame, f_size, device->cb_data); | ||||||
|                 } |                 } | ||||||
|  | 
 | ||||||
|                 unlock; |                 unlock; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|             usleep(5000); |             usleep(5000); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -455,7 +477,7 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source | |||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void print_devices(ToxWindow* self, DeviceType type) | void print_devices(ToxWindow *self, DeviceType type) | ||||||
| { | { | ||||||
|     int i; |     int i; | ||||||
| 
 | 
 | ||||||
| @@ -470,7 +492,7 @@ DeviceError selection_valid(DeviceType type, int32_t selection) | |||||||
|     return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None; |     return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* get_device_callback_data(uint32_t device_idx) | void *get_device_callback_data(uint32_t device_idx) | ||||||
| { | { | ||||||
|     if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) |     if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) | ||||||
|         return NULL; |         return NULL; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| /*  device.h
 | /*  audio_device.h
 | ||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
| @@ -26,8 +26,8 @@ | |||||||
|  * Read from running input device(s) via select()/callback combo. |  * Read from running input device(s) via select()/callback combo. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #ifndef DEVICE_H | #ifndef AUDIO_DEVICE_H | ||||||
| #define DEVICE_H | #define AUDIO_DEVICE_H | ||||||
| 
 | 
 | ||||||
| #define OPENAL_BUFS 5 | #define OPENAL_BUFS 5 | ||||||
| #define MAX_DEVICES 32 | #define MAX_DEVICES 32 | ||||||
| @@ -52,11 +52,11 @@ typedef enum DeviceError { | |||||||
|     de_AlError = -9, |     de_AlError = -9, | ||||||
| } DeviceError; | } DeviceError; | ||||||
| 
 | 
 | ||||||
| typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data); | typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| DeviceError init_devices(ToxAv* av); | DeviceError init_devices(ToxAV *av); | ||||||
| #else | #else | ||||||
| DeviceError init_devices(); | DeviceError init_devices(); | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| @@ -64,8 +64,9 @@ DeviceError init_devices(); | |||||||
| DeviceError terminate_devices(); | DeviceError terminate_devices(); | ||||||
| 
 | 
 | ||||||
| /* Callback handles ready data from INPUT device */ | /* Callback handles ready data from INPUT device */ | ||||||
| DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD); | DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, | ||||||
| void* get_device_callback_data(uint32_t device_idx); |                                      void *data, bool enable_VAD); | ||||||
|  | void *get_device_callback_data(uint32_t device_idx); | ||||||
| 
 | 
 | ||||||
| /* toggle device mute */ | /* toggle device mute */ | ||||||
| DeviceError device_mute(DeviceType type, uint32_t device_idx); | DeviceError device_mute(DeviceType type, uint32_t device_idx); | ||||||
| @@ -75,17 +76,20 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value); | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| DeviceError set_primary_device(DeviceType type, int32_t selection); | DeviceError set_primary_device(DeviceType type, int32_t selection); | ||||||
| DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, 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 */ | /* 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 */ | /* Stop device */ | ||||||
| DeviceError close_device(DeviceType type, uint32_t device_idx); | DeviceError close_device(DeviceType type, uint32_t device_idx); | ||||||
| 
 | 
 | ||||||
| /* Write data to device */ | /* Write data to device */ | ||||||
| DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, 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); | void get_primary_device_name(DeviceType type, char *buf, int size); | ||||||
| 
 | 
 | ||||||
| DeviceError selection_valid(DeviceType type, int32_t selection); | DeviceError selection_valid(DeviceType type, int32_t selection); | ||||||
| #endif /* DEVICE_H */ | #endif /* AUDIO_DEVICE_H */ | ||||||
| @@ -25,10 +25,10 @@ | |||||||
| #include <limits.h> | #include <limits.h> | ||||||
|  |  | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|     #include <sys/types.h> | #include <sys/types.h> | ||||||
|     #include <sys/dir.h> | #include <sys/dir.h> | ||||||
| #else | #else | ||||||
|     #include <dirent.h> | #include <dirent.h> | ||||||
| #endif /* ifdef __APPLE__ */ | #endif /* ifdef __APPLE__ */ | ||||||
|  |  | ||||||
| #include "windows.h" | #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. | /* 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. */ |  * e.g. if matches contains: [foo, foobar, foe] we put fo in match. | ||||||
| static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n) |  * | ||||||
|  |  * 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) { |     if (n == 1) { | ||||||
|         strcpy(match, matches[0]); |         return snprintf(match, match_sz, "%s", matches[0]); | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
| @@ -71,14 +73,14 @@ static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_ | |||||||
|             char ch2 = matches[j][i]; |             char ch2 = matches[j][i]; | ||||||
|  |  | ||||||
|             if (ch1 != ch2 || !ch1) { |             if (ch1 != ch2 || !ch1) { | ||||||
|                 strcpy(match, matches[0]); |                 snprintf(match, match_sz, "%s", matches[0]); | ||||||
|                 match[i] = '\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, | /* looks for all instances in list that begin with the last entered word in line according to pos, | ||||||
| @@ -106,7 +108,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | |||||||
|  |  | ||||||
|     /* TODO: generalize this */ |     /* TODO: generalize this */ | ||||||
|     bool dir_search =    !strncmp(ubuf, "/sendfile", strlen("/sendfile")) |     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 */ |     /* isolate substring from space behind pos to pos */ | ||||||
|     char tmp[MAX_STR_SIZE]; |     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'; |     tmp[ctx->pos] = '\0'; | ||||||
|  |  | ||||||
|     const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' '); |     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) |     if (sub == NULL) | ||||||
|         exit_toxic_err("failed in complete_line", FATALERR_MEMORY); |         exit_toxic_err("failed in complete_line", FATALERR_MEMORY); | ||||||
| @@ -142,14 +144,14 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     int s_len = strlen(sub); |     int s_len = strlen(sub); | ||||||
|     const char *str; |  | ||||||
|     int n_matches = 0; |     int n_matches = 0; | ||||||
|     char matches[n_items][MAX_STR_SIZE]; |     char matches[n_items][MAX_STR_SIZE]; | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |  | ||||||
|     /* put all list matches in matches array */ |     /* put all list matches in matches array */ | ||||||
|     for (i = 0; i < n_items; ++i) { |     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) |         if (strncasecmp(str, sub, s_len) == 0) | ||||||
|             strcpy(matches[n_matches++], str); |             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); |         print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); | ||||||
|  |  | ||||||
|     char match[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 (dir_search) { | ||||||
|         if (n_matches == 1) |         if (n_matches == 1) | ||||||
|             endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/"; |             endchrs = char_rfind(match, '.', match_len) ? "\"" : "/"; | ||||||
|         else |         else | ||||||
|             endchrs = ""; |             endchrs = ""; | ||||||
|     } else if (n_matches > 1) { |     } 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 */ |     /* put match in correct spot in buf and append endchars */ | ||||||
|     int n_endchrs = strlen(endchrs); |     int n_endchrs = strlen(endchrs); | ||||||
|     int m_len = strlen(match); |  | ||||||
|     int strt = ctx->pos - s_len; |     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) |     if (ctx->len + diff >= MAX_STR_SIZE) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     char tmpend[MAX_STR_SIZE]; |     char tmpend[MAX_STR_SIZE]; | ||||||
|     snprintf(tmpend, sizeof(tmpend), "%s", &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], match); | ||||||
|     strcpy(&ubuf[strt + m_len], endchrs); |     strcpy(&ubuf[strt + match_len], endchrs); | ||||||
|     strcpy(&ubuf[strt + m_len + n_endchrs], tmpend); |     strcpy(&ubuf[strt + match_len + n_endchrs], tmpend); | ||||||
|  |  | ||||||
|     /* convert to widechar and copy back to original buf */ |     /* convert to widechar and copy back to original buf */ | ||||||
|     wchar_t newbuf[MAX_STR_SIZE]; |     wchar_t newbuf[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|     if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) |     if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     wcscpy(ctx->line, newbuf); |     wcscpy(ctx->line, newbuf); | ||||||
|  |  | ||||||
| @@ -278,7 +289,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | |||||||
|  |  | ||||||
|     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { |     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { | ||||||
|         if (strncmp(entry->d_name, b_name, b_name_len) == 0 |         if (strncmp(entry->d_name, b_name, b_name_len) == 0 | ||||||
|                                 && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { |                 && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { | ||||||
|             snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name); |             snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name); | ||||||
|             ++dircount; |             ++dircount; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -55,6 +55,7 @@ int avatar_send(Tox *m, uint32_t friendnum) | |||||||
|     TOX_ERR_FILE_SEND err; |     TOX_ERR_FILE_SEND err; | ||||||
|     uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, |     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); |                                      NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); | ||||||
|  |  | ||||||
|     if (Avatar.size == 0) |     if (Avatar.size == 0) | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
| @@ -150,6 +151,7 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co | |||||||
|             } else if (ft->state == FILE_TRANSFER_PAUSED) { |             } else if (ft->state == FILE_TRANSFER_PAUSED) { | ||||||
|                 ft->state = FILE_TRANSFER_STARTED; |                 ft->state = FILE_TRANSFER_STARTED; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_FILE_CONTROL_PAUSE: |         case TOX_FILE_CONTROL_PAUSE: | ||||||
|   | |||||||
							
								
								
									
										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 */ | ||||||
							
								
								
									
										278
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								src/chat.c
									
									
									
									
									
								
							| @@ -48,8 +48,11 @@ | |||||||
| #include "message_queue.h" | #include "message_queue.h" | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     #include "audio_call.h" | #include "audio_call.h" | ||||||
| #endif /* AUDIO */ | #ifdef VIDEO | ||||||
|  | #include "video_call.h" | ||||||
|  | #endif  /* VIDEO */ | ||||||
|  | #endif  /* AUDIO */ | ||||||
|  |  | ||||||
| extern char *DATA_FILE; | extern char *DATA_FILE; | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
| @@ -63,9 +66,9 @@ static void kill_infobox(ToxWindow *self); | |||||||
| #endif  /* AUDIO */ | #endif  /* AUDIO */ | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| #define AC_NUM_CHAT_COMMANDS 27 | #define AC_NUM_CHAT_COMMANDS 31 | ||||||
| #else | #else | ||||||
| #define AC_NUM_CHAT_COMMANDS 20 | #define AC_NUM_CHAT_COMMANDS 23 | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| /* Array of chat command names used for tab completion. */ | /* Array of chat command names used for tab completion. */ | ||||||
| @@ -78,14 +81,17 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { | |||||||
|     { "/close"      }, |     { "/close"      }, | ||||||
|     { "/connect"    }, |     { "/connect"    }, | ||||||
|     { "/exit"       }, |     { "/exit"       }, | ||||||
|  |     { "/gaccept"    }, | ||||||
|     { "/group"      }, |     { "/group"      }, | ||||||
|     { "/help"       }, |     { "/help"       }, | ||||||
|     { "/invite"     }, |     { "/invite"     }, | ||||||
|     { "/join"       }, |     { "/join"       }, | ||||||
|     { "/log"        }, |     { "/log"        }, | ||||||
|     { "/myid"       }, |     { "/myid"       }, | ||||||
|  |     { "/myqr"       }, | ||||||
|     { "/nick"       }, |     { "/nick"       }, | ||||||
|     { "/note"       }, |     { "/note"       }, | ||||||
|  |     { "/nospam"     }, | ||||||
|     { "/quit"       }, |     { "/quit"       }, | ||||||
|     { "/savefile"   }, |     { "/savefile"   }, | ||||||
|     { "/sendfile"   }, |     { "/sendfile"   }, | ||||||
| @@ -100,6 +106,7 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { | |||||||
|     { "/sdev"       }, |     { "/sdev"       }, | ||||||
|     { "/mute"       }, |     { "/mute"       }, | ||||||
|     { "/sense"      }, |     { "/sense"      }, | ||||||
|  |     { "/video"      }, | ||||||
|  |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| }; | }; | ||||||
| @@ -120,15 +127,18 @@ void kill_chat_window(ToxWindow *self, Tox *m) | |||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|     StatusBar *statusbar = self->stb; |     StatusBar *statusbar = self->stb; | ||||||
|  |  | ||||||
|  | #ifdef AUDIO | ||||||
|  | #ifdef VIDEO | ||||||
|  |     stop_video_stream(self); | ||||||
|  | #endif  /* VIDEO */ | ||||||
|  |     stop_current_call(self); | ||||||
|  | #endif  /* AUDIO */ | ||||||
|  |  | ||||||
|     kill_all_file_transfers_friend(m, self->num); |     kill_all_file_transfers_friend(m, self->num); | ||||||
|     log_disable(ctx->log); |     log_disable(ctx->log); | ||||||
|     line_info_cleanup(ctx->hst); |     line_info_cleanup(ctx->hst); | ||||||
|     cqueue_cleanup(ctx->cqueue); |     cqueue_cleanup(ctx->cqueue); | ||||||
|  |  | ||||||
| #ifdef AUDIO |  | ||||||
|     stop_current_call(self); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     delwin(ctx->linewin); |     delwin(ctx->linewin); | ||||||
|     delwin(ctx->history); |     delwin(ctx->history); | ||||||
|     delwin(statusbar->topline); |     delwin(statusbar->topline); | ||||||
| @@ -143,7 +153,7 @@ void kill_chat_window(ToxWindow *self, Tox *m) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const char *msg, size_t len, | static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const char *msg, size_t len, | ||||||
|                                const char *nick, const char *timefrmt) |                                 const char *nick, const char *timefrmt) | ||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
| @@ -151,9 +161,11 @@ static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const cha | |||||||
|     write_to_log(msg, nick, ctx->log, false); |     write_to_log(msg, nick, ctx->log, false); | ||||||
|  |  | ||||||
|     if (self->active_box != -1) |     if (self->active_box != -1) | ||||||
|         box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, self->active_box, "%s", msg); |         box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, | ||||||
|  |                     self->active_box, "%s", msg); | ||||||
|     else |     else | ||||||
|         box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, &self->active_box, nick, "%s", msg); |         box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, | ||||||
|  |                    &self->active_box, nick, "%s", msg); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char *action, size_t len, | static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char *action, size_t len, | ||||||
| @@ -165,9 +177,11 @@ static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char | |||||||
|     write_to_log(action, nick, ctx->log, true); |     write_to_log(action, nick, ctx->log, true); | ||||||
|  |  | ||||||
|     if (self->active_box != -1) |     if (self->active_box != -1) | ||||||
|         box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, self->active_box, "* %s %s", nick, action ); |         box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, | ||||||
|  |                     self->active_box, "* %s %s", nick, action ); | ||||||
|     else |     else | ||||||
|         box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action ); |         box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, | ||||||
|  |                    &self->active_box, self->name, "* %s %s", nick, action ); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TYPE type, const char *msg, size_t len) | static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TYPE type, const char *msg, size_t len) | ||||||
| @@ -206,7 +220,14 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C | |||||||
|     char nick[TOX_MAX_NAME_LENGTH]; |     char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_nick_truncate(m, nick, num); |     get_nick_truncate(m, nick, num); | ||||||
|  |  | ||||||
|     if (connection_status != TOX_CONNECTION_NONE && statusbar->connection == TOX_CONNECTION_NONE) { |     TOX_CONNECTION prev_status = statusbar->connection; | ||||||
|  |     statusbar->connection = connection_status; | ||||||
|  |  | ||||||
|  |     if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (prev_status == TOX_CONNECTION_NONE) { | ||||||
|         Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON |         Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON | ||||||
|                                       ? tox_friend_get_typing(m, num, NULL) : false; |                                       ? tox_friend_get_typing(m, num, NULL) : false; | ||||||
|         chat_resume_file_senders(self, m, num); |         chat_resume_file_senders(self, m, num); | ||||||
| @@ -226,8 +247,6 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C | |||||||
|         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); |         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); | ||||||
|         write_to_log(msg, nick, ctx->log, true); |         write_to_log(msg, nick, ctx->log, true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     statusbar->connection = connection_status; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void chat_onTypingChange(ToxWindow *self, Tox *m, uint32_t num, bool is_typing) | static void chat_onTypingChange(ToxWindow *self, Tox *m, uint32_t num, bool is_typing) | ||||||
| @@ -436,11 +455,11 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint | |||||||
|             if (ft->state == FILE_TRANSFER_PENDING) { |             if (ft->state == FILE_TRANSFER_PENDING) { | ||||||
|                 ft->state = FILE_TRANSFER_STARTED; |                 ft->state = FILE_TRANSFER_STARTED; | ||||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.", |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.", | ||||||
|                                                                       ft->index, ft->file_name); |                               ft->index, ft->file_name); | ||||||
|                 char progline[MAX_STR_SIZE]; |                 char progline[MAX_STR_SIZE]; | ||||||
|                 init_progress_bar(progline); |                 init_progress_bar(progline); | ||||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); |                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||||
|                 sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL); |                 sound_notify(self, silent, NT_NOFOCUS | user_settings->bell_on_filetrans_accept | NT_WNDALERT_2, NULL); | ||||||
|                 ft->line_id = self->chatwin->hst->line_end->id + 2; |                 ft->line_id = self->chatwin->hst->line_end->id + 2; | ||||||
|             } else if (ft->state == FILE_TRANSFER_PAUSED) {    /* transfer is resumed */ |             } else if (ft->state == FILE_TRANSFER_PAUSED) {    /* transfer is resumed */ | ||||||
|                 ft->state = FILE_TRANSFER_STARTED; |                 ft->state = FILE_TRANSFER_STARTED; | ||||||
| @@ -448,10 +467,12 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint | |||||||
|  |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         case TOX_FILE_CONTROL_PAUSE: { |         case TOX_FILE_CONTROL_PAUSE: { | ||||||
|             ft->state = FILE_TRANSFER_PAUSED; |             ft->state = FILE_TRANSFER_PAUSED; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         case TOX_FILE_CONTROL_CANCEL: { |         case TOX_FILE_CONTROL_CANCEL: { | ||||||
|             snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name); |             snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name); | ||||||
|             close_file_transfer(self, m, ft, -1, msg, notif_error); |             close_file_transfer(self, m, ft, -1, msg, notif_error); | ||||||
| @@ -582,34 +603,27 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_ | |||||||
|     tox_file_get_file_id(m, friendnum, filenum, ft->file_id, NULL); |     tox_file_get_file_id(m, friendnum, filenum, ft->file_id, NULL); | ||||||
|  |  | ||||||
|     if (self->active_box != -1) |     if (self->active_box != -1) | ||||||
|         box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box, |         box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans, | ||||||
|                     "Incoming file: %s", filename ); |                     self->active_box, "Incoming file: %s", filename ); | ||||||
|     else |     else | ||||||
|         box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name, |         box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans, | ||||||
|                     "Incoming file: %s", filename ); |                    &self->active_box, self->name, "Incoming file: %s", filename ); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, uint8_t type, const char *group_pub_key, | static void chat_onGroupInvite(ToxWindow *self, Tox *m, uint32_t friendnumber, const char *invite_data, | ||||||
|                                uint16_t length) |                                size_t length) | ||||||
| { | { | ||||||
|     if (self->num != friendnumber) |     if (self->num != friendnumber) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     if (Friends.list[friendnumber].group_invite.key != NULL) |     if (Friends.list[friendnumber].group_invite.data) | ||||||
|         free(Friends.list[friendnumber].group_invite.key); |         free(Friends.list[friendnumber].group_invite.data); | ||||||
|  |  | ||||||
|     char *k = malloc(length); |     Friends.list[friendnumber].group_invite.data = malloc(length * sizeof(uint8_t)); | ||||||
|  |     memcpy(Friends.list[friendnumber].group_invite.data, invite_data, length); | ||||||
|     if (k == NULL) |  | ||||||
|         exit_toxic_err("Failed in chat_onGroupInvite", FATALERR_MEMORY); |  | ||||||
|  |  | ||||||
|     memcpy(k, group_pub_key, length); |  | ||||||
|     Friends.list[friendnumber].group_invite.key = k; |  | ||||||
|     Friends.list[friendnumber].group_invite.pending = true; |  | ||||||
|     Friends.list[friendnumber].group_invite.length = length; |     Friends.list[friendnumber].group_invite.length = length; | ||||||
|     Friends.list[friendnumber].group_invite.type = type; |  | ||||||
|  |  | ||||||
|     sound_notify(self, generic_message, NT_WNDALERT_2, NULL); |     sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL); | ||||||
|  |  | ||||||
|     char name[TOX_MAX_NAME_LENGTH]; |     char name[TOX_MAX_NAME_LENGTH]; | ||||||
|     get_nick_truncate(m, name, friendnumber); |     get_nick_truncate(m, name, friendnumber); | ||||||
| @@ -620,25 +634,24 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, ui | |||||||
|         box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join group chat"); |         box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join group chat"); | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a group chat.", name); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a group chat.", name); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat."); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/gaccept\" to join the chat."); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Av Stuff */ | /* AV Stuff */ | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
| void chat_onInvite (ToxWindow *self, ToxAv *av, int call_index) | void chat_onInvite (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if (!self || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     /* call_index is set here and reset on call end */ |     /* call is flagged active here */ | ||||||
|  |     self->is_call = true; | ||||||
|  |  | ||||||
|     self->call_idx = call_index; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\""); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\""); | ||||||
|  |  | ||||||
|     if (self->ringing_sound == -1) |     if (self->ringing_sound == -1) | ||||||
|         sound_notify(self, call_incoming, NT_LOOP, &self->ringing_sound); |         sound_notify(self, call_incoming, NT_LOOP | user_settings->bell_on_invite, &self->ringing_sound); | ||||||
|  |  | ||||||
|  |  | ||||||
|     if (self->active_box != -1) |     if (self->active_box != -1) | ||||||
|         box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!"); |         box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!"); | ||||||
| @@ -646,53 +659,59 @@ void chat_onInvite (ToxWindow *self, ToxAv *av, int call_index) | |||||||
|         box_silent_notify(self, NT_NOFOCUS | NT_WNDALERT_0, &self->active_box, self->name, "Incoming audio call!"); |         box_silent_notify(self, NT_NOFOCUS | NT_WNDALERT_0, &self->active_box, self->name, "Incoming audio call!"); | ||||||
| } | } | ||||||
|  |  | ||||||
| void chat_onRinging (ToxWindow *self, ToxAv *av, int call_index) | void chat_onRinging (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it."); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it."); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|  |  | ||||||
|     if (self->ringing_sound == -1) |     if (self->ringing_sound == -1) | ||||||
|         sound_notify(self, call_outgoing, NT_LOOP, &self->ringing_sound); |         sound_notify(self, call_outgoing, NT_LOOP, &self->ringing_sound); | ||||||
|  |  | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| void chat_onStarting (ToxWindow *self, ToxAv *av, int call_index) | void chat_onStarting (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     init_infobox(self); |     init_infobox(self); | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); | ||||||
|  |  | ||||||
|  |     /* call is flagged active here */ | ||||||
|  |     self->is_call = true; | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     stop_sound(self->ringing_sound); |     stop_sound(self->ringing_sound); | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| void chat_onEnding (ToxWindow *self, ToxAv *av, int call_index) | void chat_onEnding (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     kill_infobox(self); |     kill_infobox(self); | ||||||
|     self->call_idx = -1; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); | ||||||
|  |  | ||||||
|  |     self->is_call = false; | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     stop_sound(self->ringing_sound); |     stop_sound(self->ringing_sound); | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| void chat_onError (ToxWindow *self, ToxAv *av, int call_index) | void chat_onError (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     self->call_idx = -1; |     self->is_call = false; | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!"); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!"); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
| @@ -700,11 +719,14 @@ void chat_onError (ToxWindow *self, ToxAv *av, int call_index) | |||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| void chat_onStart (ToxWindow *self, ToxAv *av, int call_index) | void chat_onStart (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|  |     /* call is flagged active here */ | ||||||
|  |     self->is_call = true; | ||||||
|  |  | ||||||
|     init_infobox(self); |     init_infobox(self); | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); | ||||||
| @@ -714,13 +736,13 @@ void chat_onStart (ToxWindow *self, ToxAv *av, int call_index) | |||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index) | void chat_onCancel (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|  |     self->is_call = false; | ||||||
|     kill_infobox(self); |     kill_infobox(self); | ||||||
|     self->call_idx = -1; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!"); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!"); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
| @@ -728,54 +750,27 @@ void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index) | |||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| void chat_onReject (ToxWindow *self, ToxAv *av, int call_index) | void chat_onReject (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self  || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     self->call_idx = -1; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!"); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!"); | ||||||
|  |     self->is_call = false; | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     stop_sound(self->ringing_sound); |     stop_sound(self->ringing_sound); | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| void chat_onEnd (ToxWindow *self, ToxAv *av, int call_index) | void chat_onEnd (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |     if (!self || self->num != friend_number) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     kill_infobox(self); |     kill_infobox(self); | ||||||
|     self->call_idx = -1; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); | ||||||
|  |     self->is_call = false; | ||||||
| #ifdef SOUND_NOTIFY |  | ||||||
|     stop_sound(self->ringing_sound); |  | ||||||
| #endif /* SOUND_NOTIFY */ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void chat_onRequestTimeout (ToxWindow *self, ToxAv *av, int call_index) |  | ||||||
| { |  | ||||||
|     if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     self->call_idx = -1; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No answer!"); |  | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY |  | ||||||
|     stop_sound(self->ringing_sound); |  | ||||||
| #endif /* SOUND_NOTIFY */ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void chat_onPeerTimeout (ToxWindow *self, ToxAv *av, int call_index) |  | ||||||
| { |  | ||||||
|     if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0)) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     kill_infobox(self); |  | ||||||
|     self->call_idx = -1; |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer disconnected; call ended!"); |  | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     stop_sound(self->ringing_sound); |     stop_sound(self->ringing_sound); | ||||||
| @@ -788,6 +783,10 @@ static void init_infobox(ToxWindow *self) | |||||||
|  |  | ||||||
|     int x2, y2; |     int x2, y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     (void) y2; |     (void) y2; | ||||||
|  |  | ||||||
|     memset(&ctx->infobox, 0, sizeof(struct infobox)); |     memset(&ctx->infobox, 0, sizeof(struct infobox)); | ||||||
| @@ -824,7 +823,7 @@ static void draw_infobox(ToxWindow *self) | |||||||
|     if (x2 < INFOBOX_WIDTH || y2 < INFOBOX_HEIGHT) |     if (x2 < INFOBOX_WIDTH || y2 < INFOBOX_HEIGHT) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     uint64_t curtime = get_unix_time(); |     time_t curtime = get_unix_time(); | ||||||
|  |  | ||||||
|     /* update elapsed time string once per second */ |     /* update elapsed time string once per second */ | ||||||
|     if (curtime > infobox->lastupdate) |     if (curtime > infobox->lastupdate) | ||||||
| @@ -861,7 +860,7 @@ static void draw_infobox(ToxWindow *self) | |||||||
|     wprintw(infobox->win, "%.2f\n", infobox->vad_lvl); |     wprintw(infobox->win, "%.2f\n", infobox->vad_lvl); | ||||||
|  |  | ||||||
|     wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' '); |     wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' '); | ||||||
|     wrefresh(infobox->win); |     wnoutrefresh(infobox->win); | ||||||
| } | } | ||||||
|  |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| @@ -886,7 +885,6 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action) | |||||||
|  |  | ||||||
| static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | ||||||
| { | { | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|     StatusBar *statusbar = self->stb; |     StatusBar *statusbar = self->stb; | ||||||
|  |  | ||||||
| @@ -894,15 +892,18 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|     getyx(self->window, y, x); |     getyx(self->window, y, x); | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (x2 <= 0) |     if (y2 <= 0 || x2 <= 0) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|  |     if (ctx->pastemode && key == '\r') | ||||||
|  |         key = '\n'; | ||||||
|  |  | ||||||
|     if (self->help->active) { |     if (self->help->active) { | ||||||
|         help_onKey(self, key); |         help_onKey(self, key); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ltr) {    /* char is printable */ |     if (ltr || key == '\n') {    /* char is printable */ | ||||||
|         input_new_char(self, key, x, y, x2, y2); |         input_new_char(self, key, x, y, x2, y2); | ||||||
|  |  | ||||||
|         if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE) |         if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE) | ||||||
| @@ -924,11 +925,11 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|             diff = dir_match(self, m, ctx->line, L"/sendfile"); |             diff = dir_match(self, m, ctx->line, L"/sendfile"); | ||||||
|         } else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) { |         } else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) { | ||||||
|             diff = dir_match(self, m, ctx->line, L"/avatar"); |             diff = dir_match(self, m, ctx->line, L"/avatar"); | ||||||
|         } else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0){ |         } else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { | ||||||
|             const char status_cmd_list[3][8] = { |             const char status_cmd_list[3][8] = { | ||||||
|               {"online"}, |                 {"online"}, | ||||||
|               {"away"}, |                 {"away"}, | ||||||
|               {"busy"}, |                 {"busy"}, | ||||||
|             }; |             }; | ||||||
|             diff = complete_line(self, status_cmd_list, 3, 8); |             diff = complete_line(self, status_cmd_list, 3, 8); | ||||||
|         } else { |         } else { | ||||||
| @@ -944,38 +945,41 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|             sound_notify(self, notif_error, 0, NULL); |             sound_notify(self, notif_error, 0, NULL); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     } else if (key == '\n') { |     } else if (key == '\r') { | ||||||
|         rm_trailing_spaces_buf(ctx); |         rm_trailing_spaces_buf(ctx); | ||||||
|  |  | ||||||
|         char line[MAX_STR_SIZE]; |         if (!wstring_is_empty(ctx->line)) { | ||||||
|  |  | ||||||
|         if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) |  | ||||||
|             memset(&line, 0, sizeof(line)); |  | ||||||
|  |  | ||||||
|         if (!string_is_empty(line)) |  | ||||||
|             add_line_to_hist(ctx); |             add_line_to_hist(ctx); | ||||||
|  |  | ||||||
|         if (line[0] == '/') { |             wstrsubst(ctx->line, L'¶', L'\n'); | ||||||
|             if (strcmp(line, "/close") == 0) { |  | ||||||
|                 kill_chat_window(self, m); |             char line[MAX_STR_SIZE] = {0}; | ||||||
|                 return; |  | ||||||
|             } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { |             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) | ||||||
|                 send_action(self, ctx, m, line + strlen("/me ")); |                 memset(&line, 0, sizeof(line)); | ||||||
|  |  | ||||||
|  |             if (line[0] == '/') { | ||||||
|  |                 if (strcmp(line, "/close") == 0) { | ||||||
|  |                     kill_chat_window(self, m); | ||||||
|  |                     return; | ||||||
|  |                 } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { | ||||||
|  |                     send_action(self, ctx, m, line + strlen("/me ")); | ||||||
|  |                 } else { | ||||||
|  |                     execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); | ||||||
|  |                 } | ||||||
|             } else { |             } else { | ||||||
|                 execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); |                 char selfname[TOX_MAX_NAME_LENGTH]; | ||||||
|  |                 tox_self_get_name(m, (uint8_t *) selfname); | ||||||
|  |  | ||||||
|  |                 size_t len = tox_self_get_name_size(m); | ||||||
|  |                 selfname[len] = '\0'; | ||||||
|  |  | ||||||
|  |                 char timefrmt[TIME_STR_SIZE]; | ||||||
|  |                 get_time_str(timefrmt, sizeof(timefrmt)); | ||||||
|  |  | ||||||
|  |                 line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line); | ||||||
|  |                 cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1); | ||||||
|             } |             } | ||||||
|         } else if (!string_is_empty(line)) { |  | ||||||
|             char selfname[TOX_MAX_NAME_LENGTH]; |  | ||||||
|             tox_self_get_name(m, (uint8_t *) selfname); |  | ||||||
|  |  | ||||||
|             size_t len = tox_self_get_name_size(m); |  | ||||||
|             selfname[len] = '\0'; |  | ||||||
|  |  | ||||||
|             char timefrmt[TIME_STR_SIZE]; |  | ||||||
|             get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|             line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line); |  | ||||||
|             cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         wclear(ctx->linewin); |         wclear(ctx->linewin); | ||||||
| @@ -992,6 +996,9 @@ static void chat_onDraw(ToxWindow *self, Tox *m) | |||||||
|     int x2, y2; |     int x2, y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
| @@ -1019,9 +1026,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m) | |||||||
|             case TOX_USER_STATUS_NONE: |             case TOX_USER_STATUS_NONE: | ||||||
|                 colour = GREEN; |                 colour = GREEN; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_AWAY: |             case TOX_USER_STATUS_AWAY: | ||||||
|                 colour = YELLOW; |                 colour = YELLOW; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_BUSY: |             case TOX_USER_STATUS_BUSY: | ||||||
|                 colour = RED; |                 colour = RED; | ||||||
|                 break; |                 break; | ||||||
| @@ -1094,13 +1103,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m) | |||||||
|     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); |     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); | ||||||
|     wmove(self->window, y + 1, new_x); |     wmove(self->window, y + 1, new_x); | ||||||
|  |  | ||||||
|     wrefresh(self->window); |     wnoutrefresh(self->window); | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|     if (ctx->infobox.active) { |     if (ctx->infobox.active) { | ||||||
|         draw_infobox(self); |         draw_infobox(self); | ||||||
|         wrefresh(self->window); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     if (self->help->active) |     if (self->help->active) | ||||||
| @@ -1116,6 +1126,10 @@ static void chat_onInit(ToxWindow *self, Tox *m) | |||||||
|     curs_set(1); |     curs_set(1); | ||||||
|     int x2, y2; |     int x2, y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) | ||||||
|  |         exit_toxic_err("failed in chat_onInit", FATALERR_CURSES); | ||||||
|  |  | ||||||
|     self->x = x2; |     self->x = x2; | ||||||
|  |  | ||||||
|     /* Init statusbar info */ |     /* Init statusbar info */ | ||||||
| @@ -1186,7 +1200,6 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum) | |||||||
|     ret.onMessage = &chat_onMessage; |     ret.onMessage = &chat_onMessage; | ||||||
|     ret.onConnectionChange = &chat_onConnectionChange; |     ret.onConnectionChange = &chat_onConnectionChange; | ||||||
|     ret.onTypingChange = & chat_onTypingChange; |     ret.onTypingChange = & chat_onTypingChange; | ||||||
|     ret.onGroupInvite = &chat_onGroupInvite; |  | ||||||
|     ret.onNickChange = &chat_onNickChange; |     ret.onNickChange = &chat_onNickChange; | ||||||
|     ret.onStatusChange = &chat_onStatusChange; |     ret.onStatusChange = &chat_onStatusChange; | ||||||
|     ret.onStatusMessageChange = &chat_onStatusMessageChange; |     ret.onStatusMessageChange = &chat_onStatusMessageChange; | ||||||
| @@ -1195,6 +1208,7 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum) | |||||||
|     ret.onFileControl = &chat_onFileControl; |     ret.onFileControl = &chat_onFileControl; | ||||||
|     ret.onFileRecv = &chat_onFileRecv; |     ret.onFileRecv = &chat_onFileRecv; | ||||||
|     ret.onReadReceipt = &chat_onReadReceipt; |     ret.onReadReceipt = &chat_onReadReceipt; | ||||||
|  |     ret.onGroupInvite = &chat_onGroupInvite; | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     ret.onInvite = &chat_onInvite; |     ret.onInvite = &chat_onInvite; | ||||||
| @@ -1206,10 +1220,8 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum) | |||||||
|     ret.onCancel = &chat_onCancel; |     ret.onCancel = &chat_onCancel; | ||||||
|     ret.onReject = &chat_onReject; |     ret.onReject = &chat_onReject; | ||||||
|     ret.onEnd = &chat_onEnd; |     ret.onEnd = &chat_onEnd; | ||||||
|     ret.onRequestTimeout = &chat_onRequestTimeout; |  | ||||||
|     ret.onPeerTimeout = &chat_onPeerTimeout; |  | ||||||
|  |  | ||||||
|     ret.call_idx = -1; |     ret.is_call = false; | ||||||
|     ret.device_selection[0] = ret.device_selection[1] = -1; |     ret.device_selection[0] = ret.device_selection[1] = -1; | ||||||
|     ret.ringing_sound = -1; |     ret.ringing_sound = -1; | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|   | |||||||
| @@ -45,9 +45,9 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|  |  | ||||||
|     char msg[MAX_STR_SIZE]; |     char msg[MAX_STR_SIZE]; | ||||||
|     const char *inoutstr = argv[1]; |     const char *inoutstr = argv[1]; | ||||||
|     int idx = atoi(argv[2]); |     long int idx = strtol(argv[2], NULL, 10); | ||||||
|  |  | ||||||
|     if (idx >= MAX_FILES || idx < 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."); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -78,6 +78,47 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|     close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent); |     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]) | void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
| @@ -87,58 +128,22 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a | |||||||
|  |  | ||||||
|     int groupnum = atoi(argv[1]); |     int groupnum = atoi(argv[1]); | ||||||
|  |  | ||||||
|  |  | ||||||
|     if (groupnum == 0 && strcmp(argv[1], "0")) {    /* atoi returns 0 value on invalid input */ |     if (groupnum == 0 && strcmp(argv[1], "0")) {    /* atoi returns 0 value on invalid input */ | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number."); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (tox_invite_friend(m, self->num, groupnum) == -1) { |     TOX_ERR_GROUP_INVITE_FRIEND err; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group."); |  | ||||||
|  |     if (!tox_group_invite_friend(m, groupnum, self->num, &err)) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum); |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) |  | ||||||
| { |  | ||||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { |  | ||||||
|         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; |  | ||||||
|     uint8_t type = Friends.list[self->num].group_invite.type; |  | ||||||
|  |  | ||||||
|     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 = -1; |  | ||||||
|  |  | ||||||
|     if (type == TOX_GROUPCHAT_TYPE_TEXT) |  | ||||||
|         groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length); |  | ||||||
| #ifdef AUDIO |  | ||||||
|     else |  | ||||||
|         groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length, |  | ||||||
|                                            NULL, NULL); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     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, type) == -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]) | void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
| @@ -146,9 +151,9 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int idx = atoi(argv[1]); |     long int idx = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((idx == 0 && strcmp(argv[1], "0")) || idx >= 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."); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -190,6 +195,7 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     return; |     return; | ||||||
|  |  | ||||||
| on_recv_error: | on_recv_error: | ||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: |         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."); |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); | ||||||
| @@ -258,7 +264,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|  |  | ||||||
|     TOX_ERR_FILE_SEND err; |     TOX_ERR_FILE_SEND err; | ||||||
|     uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL, |     uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL, | ||||||
|                                     (uint8_t *) file_name, namelen, &err); |                                      (uint8_t *) file_name, namelen, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FILE_SEND_OK) |     if (err != TOX_ERR_FILE_SEND_OK) | ||||||
|         goto on_send_error; |         goto on_send_error; | ||||||
| @@ -282,6 +288,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     return; |     return; | ||||||
|  |  | ||||||
| on_send_error: | on_send_error: | ||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: |         case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: | ||||||
|             errmsg = "File transfer failed: Invalid friend."; |             errmsg = "File transfer failed: Invalid friend."; | ||||||
|   | |||||||
| @@ -26,9 +26,9 @@ | |||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "toxic.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_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_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  |  | ||||||
| @@ -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]); | void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| #endif /* AUDIO */ | #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 */ | #endif /* #define CHAT_COMMANDS_H */ | ||||||
|   | |||||||
| @@ -31,8 +31,9 @@ | |||||||
|  |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "configdir.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) | void get_home_dir(char *home, int size) | ||||||
| { | { | ||||||
|     struct passwd pwd; |     struct passwd pwd; | ||||||
| @@ -69,8 +70,8 @@ char *get_user_config_dir(void) | |||||||
|     char home[NSS_BUFLEN_PASSWD] = {0}; |     char home[NSS_BUFLEN_PASSWD] = {0}; | ||||||
|     get_home_dir(home, sizeof(home)); |     get_home_dir(home, sizeof(home)); | ||||||
|  |  | ||||||
|     char *user_config_dir; |     char *user_config_dir = NULL; | ||||||
|     size_t len; |     size_t len = 0; | ||||||
|  |  | ||||||
| # if defined(__APPLE__) | # if defined(__APPLE__) | ||||||
|     len = strlen(home) + strlen("/Library/Application Support") + 1; |     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); |     snprintf(user_config_dir, len, "%s/Library/Application Support", home); | ||||||
| # else /* __APPLE__ */ | # else /* __APPLE__ */ | ||||||
|  |  | ||||||
|     const char *tmp; |     const char *tmp = getenv("XDG_CONFIG_HOME"); | ||||||
|  |  | ||||||
|     if (!(tmp = getenv("XDG_CONFIG_HOME"))) { |     if (tmp == NULL) { | ||||||
|         len = strlen(home) + strlen("/.config") + 1; |         len = strlen(home) + strlen("/.config") + 1; | ||||||
|         user_config_dir = malloc(len); |         user_config_dir = malloc(len); | ||||||
|  |  | ||||||
| @@ -101,8 +102,10 @@ char *get_user_config_dir(void) | |||||||
|     return user_config_dir; |     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) | int create_user_config_dirs(char *path) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -34,8 +34,23 @@ | |||||||
| #define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR) | #define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Get the user's config directory. | ||||||
|  |  * | ||||||
|  |  * This is without a trailing slash. Resulting string must be freed. | ||||||
|  |  * | ||||||
|  |  * @return The users config dir or NULL on error. | ||||||
|  |  */ | ||||||
| char *get_user_config_dir(void); | char *get_user_config_dir(void); | ||||||
|  |  | ||||||
|  | /* get the user's home directory. */ | ||||||
| void get_home_dir(char *home, int size); | 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); | int create_user_config_dirs(char *path); | ||||||
|  |  | ||||||
| #endif /* #define CONFIGDIR_H */ | #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 */ | ||||||
							
								
								
									
										432
									
								
								src/dns.c
									
									
									
									
									
								
							
							
						
						
									
										432
									
								
								src/dns.c
									
									
									
									
									
								
							| @@ -1,432 +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_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 killdns_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, size_t namebuf_sz, char *dombuf, size_t dombuf_sz) |  | ||||||
| { |  | ||||||
|     if (strlen(addr) >= MAX_STR_SIZE) |  | ||||||
|         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 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, sizeof(name), inputdomain, sizeof(inputdomain)); |  | ||||||
|  |  | ||||||
|     if (namelen == -1) { |  | ||||||
|         dns_error(self, "Must be a Tox ID or an address in the form username@domain"); |  | ||||||
|         killdns_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."); |  | ||||||
|         killdns_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."); |  | ||||||
|         killdns_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."); |  | ||||||
|         killdns_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."); |  | ||||||
|         killdns_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) |  | ||||||
|         killdns_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."); |  | ||||||
|         killdns_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."); |  | ||||||
|         killdns_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); |  | ||||||
|  |  | ||||||
|     killdns_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.proxy_type != TOX_PROXY_TYPE_NONE && arg_opts.force_tcp) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "DNS lookups are disabled."); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (t_data.busy) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous user lookup to finish."); |  | ||||||
|         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) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread attr failed to init"); |  | ||||||
|         memset(&t_data, 0, sizeof(struct thread_data)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread attr failed to set"); |  | ||||||
|         pthread_attr_destroy(&dns_thread.attr); |  | ||||||
|         memset(&t_data, 0, sizeof(struct thread_data)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread failed to init"); |  | ||||||
|         pthread_attr_destroy(&dns_thread.attr); |  | ||||||
|         memset(&t_data, 0, sizeof(struct thread_data)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										121
									
								
								src/execute.c
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								src/execute.c
									
									
									
									
									
								
							| @@ -34,6 +34,8 @@ | |||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
|  |  | ||||||
|  | #define MAX_NUM_ARGS 10     /* Includes command */ | ||||||
|  |  | ||||||
| struct cmd_func { | struct cmd_func { | ||||||
|     const char *name; |     const char *name; | ||||||
|     void (*func)(WINDOW *w, ToxWindow *, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); |     void (*func)(WINDOW *w, ToxWindow *, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| @@ -49,10 +51,13 @@ static struct cmd_func global_commands[] = { | |||||||
|     { "/exit",      cmd_quit          }, |     { "/exit",      cmd_quit          }, | ||||||
|     { "/group",     cmd_groupchat     }, |     { "/group",     cmd_groupchat     }, | ||||||
|     { "/help",      cmd_prompt_help   }, |     { "/help",      cmd_prompt_help   }, | ||||||
|  |     { "/join",      cmd_join          }, | ||||||
|     { "/log",       cmd_log           }, |     { "/log",       cmd_log           }, | ||||||
|     { "/myid",      cmd_myid          }, |     { "/myid",      cmd_myid          }, | ||||||
|  |     { "/myqr",      cmd_myqr          }, | ||||||
|     { "/nick",      cmd_nick          }, |     { "/nick",      cmd_nick          }, | ||||||
|     { "/note",      cmd_note          }, |     { "/note",      cmd_note          }, | ||||||
|  |     { "/nospam",    cmd_nospam        }, | ||||||
|     { "/q",         cmd_quit          }, |     { "/q",         cmd_quit          }, | ||||||
|     { "/quit",      cmd_quit          }, |     { "/quit",      cmd_quit          }, | ||||||
|     { "/requests",  cmd_requests      }, |     { "/requests",  cmd_requests      }, | ||||||
| @@ -61,13 +66,17 @@ static struct cmd_func global_commands[] = { | |||||||
|     { "/lsdev",     cmd_list_devices  }, |     { "/lsdev",     cmd_list_devices  }, | ||||||
|     { "/sdev",      cmd_change_device }, |     { "/sdev",      cmd_change_device }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  | #ifdef VIDEO | ||||||
|  |     { "/lsvdev",    cmd_list_video_devices }, | ||||||
|  |     { "/svdev" ,    cmd_change_video_device }, | ||||||
|  | #endif /* VIDEO */ | ||||||
|     { NULL,         NULL              }, |     { NULL,         NULL              }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct cmd_func chat_commands[] = { | static struct cmd_func chat_commands[] = { | ||||||
|     { "/cancel",    cmd_cancelfile  }, |     { "/cancel",    cmd_cancelfile  }, | ||||||
|  |     { "/gaccept",   cmd_groupaccept }, | ||||||
|     { "/invite",    cmd_groupinvite }, |     { "/invite",    cmd_groupinvite }, | ||||||
|     { "/join",      cmd_join_group  }, |  | ||||||
|     { "/savefile",  cmd_savefile    }, |     { "/savefile",  cmd_savefile    }, | ||||||
|     { "/sendfile",  cmd_sendfile    }, |     { "/sendfile",  cmd_sendfile    }, | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| @@ -78,23 +87,106 @@ static struct cmd_func chat_commands[] = { | |||||||
|     { "/mute",      cmd_mute        }, |     { "/mute",      cmd_mute        }, | ||||||
|     { "/sense",     cmd_sense       }, |     { "/sense",     cmd_sense       }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  | #ifdef VIDEO | ||||||
|  |     { "/video",     cmd_video       }, | ||||||
|  | #endif /* VIDEO */ | ||||||
|     { NULL,         NULL            }, |     { NULL,         NULL            }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct cmd_func group_commands[] = { | static struct cmd_func group_commands[] = { | ||||||
|     { "/title",     cmd_set_title   }, |     { "/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 | #ifdef AUDIO | ||||||
|     { "/mute",      cmd_mute        }, |     { "/mute",      cmd_mute           }, | ||||||
|     { "/sense",     cmd_sense       }, |     { "/sense",     cmd_sense          }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|     { NULL,         NULL            }, |     { NULL,         NULL               }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Parses input command and puts args into arg array. | #define NUM_SPECIAL_COMMANDS 15 | ||||||
|    Returns number of arguments on success, -1 on failure. */ | 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]) | 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); |     char *cmd = strdup(input); | ||||||
|  |  | ||||||
|     if (cmd == NULL) |     if (cmd == NULL) | ||||||
| @@ -132,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 */ |         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); |     free(cmd); | ||||||
|     return num_args; |     return num_args; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Matches command to respective function. Returns 0 on match, 1 on no match */ | /* Matches command to respective function. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on match, | ||||||
|  |  * Returns -1 on no match | ||||||
|  |  */ | ||||||
| static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, | static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, | ||||||
|                       char (*args)[MAX_STR_SIZE]) |                       char (*args)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
| @@ -149,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) | void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | ||||||
| @@ -177,6 +277,7 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | |||||||
|         case GROUPCHAT_COMMAND_MODE: |         case GROUPCHAT_COMMAND_MODE: | ||||||
|             if (do_command(w, self, m, num_args, group_commands, args) == 0) |             if (do_command(w, self, m, num_args, group_commands, args) == 0) | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,13 +26,11 @@ | |||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| #define MAX_NUM_ARGS 4     /* Includes command */ |  | ||||||
|  |  | ||||||
| enum { | enum { | ||||||
|     GLOBAL_COMMAND_MODE, |     GLOBAL_COMMAND_MODE, | ||||||
|     CHAT_COMMAND_MODE, |     CHAT_COMMAND_MODE, | ||||||
|     GROUPCHAT_COMMAND_MODE, |     GROUPCHAT_COMMAND_MODE, | ||||||
| }; | } COMMAND_MODE; | ||||||
|  |  | ||||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -38,87 +38,48 @@ extern FriendsList Friends; | |||||||
| /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | ||||||
| #define NUM_PROG_MARKS 50 | #define NUM_PROG_MARKS 50 | ||||||
|  |  | ||||||
| /* Checks for timed out file transfers and closes them. */ |  | ||||||
| #define CHECK_FILE_TIMEOUT_INTERAVAL 5 |  | ||||||
| void check_file_transfer_timeouts(Tox *m) |  | ||||||
| { |  | ||||||
|     char msg[MAX_STR_SIZE]; |  | ||||||
|     static uint64_t last_check = 0; |  | ||||||
|  |  | ||||||
|     if (!timed_out(last_check, CHECK_FILE_TIMEOUT_INTERAVAL)) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     last_check = get_unix_time(); |  | ||||||
|  |  | ||||||
|     size_t i, j; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < Friends.max_idx; ++i) { |  | ||||||
|         if (!Friends.list[i].active) |  | ||||||
|             continue; |  | ||||||
|  |  | ||||||
|         for (j = 0; j < MAX_FILES; ++j) { |  | ||||||
|             struct FileTransfer *ft_send = &Friends.list[i].file_sender[j]; |  | ||||||
|  |  | ||||||
|             if (ft_send->state > FILE_TRANSFER_PAUSED) { |  | ||||||
|                 if (timed_out(ft_send->last_keep_alive, TIMEOUT_FILESENDER)) { |  | ||||||
|                     snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_send->file_name); |  | ||||||
|                     close_file_transfer(ft_send->window, m, ft_send, TOX_FILE_CONTROL_CANCEL, msg, notif_error); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             struct FileTransfer *ft_recv = &Friends.list[i].file_receiver[j]; |  | ||||||
|  |  | ||||||
|             if (ft_recv->state > FILE_TRANSFER_PAUSED) { |  | ||||||
|                 if (timed_out(ft_recv->last_keep_alive, TIMEOUT_FILESENDER)) { |  | ||||||
|                     snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_recv->file_name); |  | ||||||
|                     close_file_transfer(ft_recv->window, m, ft_recv, TOX_FILE_CONTROL_CANCEL, msg, notif_error); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* creates initial progress line that will be updated during file transfer. | /* creates initial progress line that will be updated during file transfer. | ||||||
|    Assumes progline is of size MAX_STR_SIZE */ |    Assumes progline has room for at least MAX_STR_SIZE bytes */ | ||||||
| void init_progress_bar(char *progline) | void init_progress_bar(char *progline) | ||||||
| { | { | ||||||
|     strcpy(progline, "0.0 B/s ["); |     strcpy(progline, "0% ["); | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; i < NUM_PROG_MARKS; ++i) |     for (i = 0; i < NUM_PROG_MARKS; ++i) | ||||||
|         strcat(progline, "-"); |         strcat(progline, "-"); | ||||||
|  |  | ||||||
|     strcat(progline, "] 0%"); |     strcat(progline, "] 0.0 B/s"); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* prints a progress bar for file transfers. | /* 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, double bps, double pct_done, uint32_t line_id) | void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id) | ||||||
| { | { | ||||||
|     if (bps < 0 || pct_done < 0 || pct_done > 100) |     if (bps < 0 || pct_done < 0 || pct_done > 100) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     char msg[MAX_STR_SIZE]; |     char pct_str[24]; | ||||||
|     bytes_convert_str(msg, sizeof(msg), bps); |     snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done); | ||||||
|     strcat(msg, "/s ["); |  | ||||||
|  |  | ||||||
|  |     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 n = pct_done / (100 / NUM_PROG_MARKS); | ||||||
|     int i, j; |     int i, j; | ||||||
|  |  | ||||||
|     for (i = 0; i < n; ++i) |     for (i = 0; i < n; ++i) | ||||||
|         strcat(msg, "#"); |         strcat(prog_line, "="); | ||||||
|  |  | ||||||
|     for (j = i; j < NUM_PROG_MARKS; ++j) |     if (pct_done < 100) | ||||||
|         strcat(msg, "-"); |         strcpy(prog_line + n, ">"); | ||||||
|  |  | ||||||
|     strcat(msg, "] "); |     for (j = i; j < NUM_PROG_MARKS - 1; ++j) | ||||||
|  |         strcat(prog_line, "-"); | ||||||
|  |  | ||||||
|     char pctstr[16]; |     char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7]; | ||||||
|     const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%"; |     snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str); | ||||||
|     snprintf(pctstr, sizeof(pctstr), frmt, pct_done); |  | ||||||
|     strcat(msg, pctstr); |  | ||||||
|  |  | ||||||
|     line_info_set(self, line_id, msg); |     line_info_set(self, line_id, full_line); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft) | static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft) | ||||||
| @@ -175,7 +136,7 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen | |||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | ||||||
|                                                     FILE_TRANSFER_DIRECTION direction) |         FILE_TRANSFER_DIRECTION direction) | ||||||
| { | { | ||||||
|     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) |     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) | ||||||
|         return NULL; |         return NULL; | ||||||
| @@ -184,8 +145,8 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t | |||||||
|  |  | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |     for (i = 0; i < MAX_FILES; ++i) { | ||||||
|         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? |         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? | ||||||
|                                               &Friends.list[friendnum].file_sender[i] : |                                       &Friends.list[friendnum].file_sender[i] : | ||||||
|                                               &Friends.list[friendnum].file_receiver[i]; |                                       &Friends.list[friendnum].file_receiver[i]; | ||||||
|  |  | ||||||
|         if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) |         if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) | ||||||
|             return ft; |             return ft; | ||||||
|   | |||||||
| @@ -34,7 +34,6 @@ | |||||||
| #define GiB 1073741824    /* 1024^3 */ | #define GiB 1073741824    /* 1024^3 */ | ||||||
|  |  | ||||||
| #define MAX_FILES 32 | #define MAX_FILES 32 | ||||||
| #define TIMEOUT_FILESENDER 120 |  | ||||||
|  |  | ||||||
| typedef enum FILE_TRANSFER_STATE { | typedef enum FILE_TRANSFER_STATE { | ||||||
|     FILE_TRANSFER_INACTIVE, |     FILE_TRANSFER_INACTIVE, | ||||||
| @@ -62,15 +61,12 @@ struct FileTransfer { | |||||||
|     size_t   index; |     size_t   index; | ||||||
|     uint64_t file_size; |     uint64_t file_size; | ||||||
|     uint64_t position; |     uint64_t position; | ||||||
|     uint64_t last_line_progress;   /* The last time we updated the progress bar */ |     time_t   last_line_progress;   /* The last time we updated the progress bar */ | ||||||
|     uint64_t last_keep_alive;  /* The last time we sent or received data */ |     time_t   last_keep_alive;  /* The last time we sent or received data */ | ||||||
|     uint32_t line_id; |     uint32_t line_id; | ||||||
|     uint8_t  file_id[TOX_FILE_ID_LENGTH]; |     uint8_t  file_id[TOX_FILE_ID_LENGTH]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Checks for timed out file transfers and closes them. */ |  | ||||||
| void check_file_transfer_timeouts(Tox *m); |  | ||||||
|  |  | ||||||
| /* creates initial progress line that will be updated during file transfer. | /* creates initial progress line that will be updated during file transfer. | ||||||
|    progline must be at lesat MAX_STR_SIZE bytes */ |    progline must be at lesat MAX_STR_SIZE bytes */ | ||||||
| void init_progress_bar(char *progline); | void init_progress_bar(char *progline); | ||||||
| @@ -91,7 +87,7 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen | |||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | ||||||
|                                                     FILE_TRANSFER_DIRECTION direction); |         FILE_TRANSFER_DIRECTION direction); | ||||||
|  |  | ||||||
| /* Initializes an unused file transfer and returns its pointer. | /* Initializes an unused file transfer and returns its pointer. | ||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|   | |||||||
							
								
								
									
										136
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								src/friendlist.c
									
									
									
									
									
								
							| @@ -25,6 +25,7 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||||
|  | #include <assert.h> | ||||||
|  |  | ||||||
| #include <tox/tox.h> | #include <tox/tox.h> | ||||||
|  |  | ||||||
| @@ -112,31 +113,38 @@ static void realloc_blocklist(int n) | |||||||
|  |  | ||||||
| void kill_friendlist(void) | void kill_friendlist(void) | ||||||
| { | { | ||||||
|     int i; |     size_t i; | ||||||
|  |  | ||||||
|     for (i = 0; i < Friends.max_idx; ++i) { |     for (i = 0; i < Friends.max_idx; ++i) { | ||||||
|         if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL) |         if (Friends.list[i].group_invite.data != NULL) | ||||||
|             free(Friends.list[i].group_invite.key); |             free(Friends.list[i].group_invite.data); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     realloc_blocklist(0); |     realloc_blocklist(0); | ||||||
|     realloc_friends(0); |     realloc_friends(0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Saves the blocklist to path. If there are no items in the blocklist the | ||||||
|  |  * empty file will be removed. | ||||||
|  |  * | ||||||
|  |  * Returns 0 if stored successfully. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | #define TEMP_BLOCKLIST_EXT ".tmp" | ||||||
| static int save_blocklist(char *path) | static int save_blocklist(char *path) | ||||||
| { | { | ||||||
|     if (path == NULL) |     if (path == NULL) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int len = sizeof(BlockedFriend) * Blocked.num_blocked; |     int len = sizeof(BlockedFriend) * Blocked.num_blocked; | ||||||
|     char *data = malloc(len); |     char *data = malloc(len * sizeof(char)); | ||||||
|  |  | ||||||
|     if (data == NULL) |     if (data == NULL) { | ||||||
|         exit_toxic_err("Failed in save_blocklist", FATALERR_MEMORY); |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int i; |     int i, count = 0; | ||||||
|  |  | ||||||
|     int count = 0; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < Blocked.max_idx; ++i) { |     for (i = 0; i < Blocked.max_idx; ++i) { | ||||||
|         if (count > Blocked.num_blocked) { |         if (count > Blocked.num_blocked) { | ||||||
| @@ -161,7 +169,21 @@ static int save_blocklist(char *path) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     FILE *fp = fopen(path, "wb"); |     /* Blocklist is empty, we can remove the empty file */ | ||||||
|  |     if (count == 0) { | ||||||
|  |         free(data); | ||||||
|  |  | ||||||
|  |         if (remove(path) != 0) { | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     char temp_path[strlen(path) + strlen(TEMP_BLOCKLIST_EXT) + 1]; | ||||||
|  |     snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_BLOCKLIST_EXT); | ||||||
|  |  | ||||||
|  |     FILE *fp = fopen(temp_path, "wb"); | ||||||
|  |  | ||||||
|     if (fp == NULL) { |     if (fp == NULL) { | ||||||
|         free(data); |         free(data); | ||||||
| @@ -169,6 +191,7 @@ static int save_blocklist(char *path) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (fwrite(data, len, 1, fp) != 1) { |     if (fwrite(data, len, 1, fp) != 1) { | ||||||
|  |         fprintf(stderr, "Failed to write blocklist data.\n"); | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|         free(data); |         free(data); | ||||||
|         return -1; |         return -1; | ||||||
| @@ -176,6 +199,11 @@ static int save_blocklist(char *path) | |||||||
|  |  | ||||||
|     fclose(fp); |     fclose(fp); | ||||||
|     free(data); |     free(data); | ||||||
|  |  | ||||||
|  |     if (rename(temp_path, path) != 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -198,22 +226,15 @@ int load_blocklist(char *path) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char *data = malloc(len); |     char data[len]; | ||||||
|  |  | ||||||
|     if (data == NULL) { |  | ||||||
|         fclose(fp); |  | ||||||
|         exit_toxic_err("Failed in load_blocklist", FATALERR_MEMORY); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (fread(data, len, 1, fp) != 1) { |     if (fread(data, len, 1, fp) != 1) { | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|         free(data); |  | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (len % sizeof(BlockedFriend) != 0) { |     if (len % sizeof(BlockedFriend) != 0) { | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
|         free(data); |  | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -231,7 +252,7 @@ int load_blocklist(char *path) | |||||||
|         memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend)); |         memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend)); | ||||||
|         Blocked.list[i].active = true; |         Blocked.list[i].active = true; | ||||||
|         Blocked.list[i].num = i; |         Blocked.list[i].num = i; | ||||||
|         Blocked.list[i].namelength = ntohs(tmp.namelength); |         Blocked.list[i].namelength = MIN(TOXIC_MAX_NAME_LENGTH, ntohs(tmp.namelength)); | ||||||
|         memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1); |         memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1); | ||||||
|         memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE); |         memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE); | ||||||
|  |  | ||||||
| @@ -243,7 +264,6 @@ int load_blocklist(char *path) | |||||||
|         ++Blocked.num_blocked; |         ++Blocked.num_blocked; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     free(data); |  | ||||||
|     fclose(fp); |     fclose(fp); | ||||||
|     sort_blocklist_index(); |     sort_blocklist_index(); | ||||||
|  |  | ||||||
| @@ -294,10 +314,10 @@ static void sort_blocklist_index(void) | |||||||
|     qsort(Blocked.index, Blocked.num_blocked, sizeof(uint32_t), index_name_cmp_block); |     qsort(Blocked.index, Blocked.num_blocked, sizeof(uint32_t), index_name_cmp_block); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void update_friend_last_online(uint32_t num, uint64_t timestamp) | static void update_friend_last_online(uint32_t num, time_t timestamp) | ||||||
| { | { | ||||||
|     Friends.list[num].last_online.last_on = timestamp; |     Friends.list[num].last_online.last_on = timestamp; | ||||||
|     Friends.list[num].last_online.tm = *localtime((const time_t*)×tamp); |     Friends.list[num].last_online.tm = *localtime((const time_t *)×tamp); | ||||||
|  |  | ||||||
|     /* if the format changes make sure TIME_STR_SIZE is the correct size */ |     /* if the format changes make sure TIME_STR_SIZE is the correct size */ | ||||||
|     const char *t = user_settings->timestamp_format; |     const char *t = user_settings->timestamp_format; | ||||||
| @@ -419,7 +439,7 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort) | |||||||
|             fprintf(stderr, "tox_friend_get_public_key failed (error %d)\n", pkerr); |             fprintf(stderr, "tox_friend_get_public_key failed (error %d)\n", pkerr); | ||||||
|  |  | ||||||
|         TOX_ERR_FRIEND_GET_LAST_ONLINE loerr; |         TOX_ERR_FRIEND_GET_LAST_ONLINE loerr; | ||||||
|         uint64_t t = tox_friend_get_last_online(m, num, &loerr); |         time_t t = tox_friend_get_last_online(m, num, &loerr); | ||||||
|  |  | ||||||
|         if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK) |         if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK) | ||||||
|             t = 0; |             t = 0; | ||||||
| @@ -500,8 +520,7 @@ static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_ | |||||||
|     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); |     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, const char *group_pub_key, | static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, uint32_t num, const char *data, size_t length) | ||||||
|                                      uint16_t length) |  | ||||||
| { | { | ||||||
|     if (num >= Friends.max_idx) |     if (num >= Friends.max_idx) | ||||||
|         return; |         return; | ||||||
| @@ -518,7 +537,7 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8 | |||||||
|     get_nick_truncate(m, nick, num); |     get_nick_truncate(m, nick, num); | ||||||
|  |  | ||||||
|     line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, |     line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, | ||||||
|                  "* Group chat invite from %s failed: too many windows are open.", nick); |                   "* Group chat invite from %s failed: too many windows are open.", nick); | ||||||
|  |  | ||||||
|     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); |     sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); | ||||||
| } | } | ||||||
| @@ -540,6 +559,7 @@ static void select_friend(ToxWindow *self, wint_t key, int *selected, int num) | |||||||
| static void delete_friend(Tox *m, uint32_t f_num) | static void delete_friend(Tox *m, uint32_t f_num) | ||||||
| { | { | ||||||
|     TOX_ERR_FRIEND_DELETE err; |     TOX_ERR_FRIEND_DELETE err; | ||||||
|  |  | ||||||
|     if (tox_friend_delete(m, f_num, &err) != true) { |     if (tox_friend_delete(m, f_num, &err) != true) { | ||||||
|         fprintf(stderr, "tox_friend_delete failed with error %d\n", err); |         fprintf(stderr, "tox_friend_delete failed with error %d\n", err); | ||||||
|         return; |         return; | ||||||
| @@ -560,9 +580,6 @@ static void delete_friend(Tox *m, uint32_t f_num) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (Friends.list[f_num].group_invite.key != NULL) |  | ||||||
|         free(Friends.list[f_num].group_invite.key); |  | ||||||
|  |  | ||||||
|     memset(&Friends.list[f_num], 0, sizeof(ToxicFriend)); |     memset(&Friends.list[f_num], 0, sizeof(ToxicFriend)); | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
| @@ -625,10 +642,12 @@ static void draw_del_popup(void) | |||||||
|     wattron(PendingDelete.popup, A_BOLD); |     wattron(PendingDelete.popup, A_BOLD); | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |  | ||||||
|     if (blocklist_view == 0) |     if (blocklist_view == 0) | ||||||
|         wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name); |         wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name); | ||||||
|     else |     else | ||||||
|         wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name); |         wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name); | ||||||
|  |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     wattroff(PendingDelete.popup, A_BOLD); |     wattroff(PendingDelete.popup, A_BOLD); | ||||||
| @@ -704,7 +723,7 @@ static void unblock_friend(Tox *m, uint32_t bnum) | |||||||
|     uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err); |     uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FRIEND_ADD_OK) { |     if (err != TOX_ERR_FRIEND_ADD_OK) { | ||||||
|         line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)\n", err); |         line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)", err); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -752,7 +771,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     switch (key) { |     switch (key) { | ||||||
|         case '\n': |         case '\r': | ||||||
|             if (blocklist_view) |             if (blocklist_view) | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
| @@ -779,6 +798,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|                 block_friend(m, f); |                 block_friend(m, f); | ||||||
|             else |             else | ||||||
|                 unblock_friend(m, f); |                 unblock_friend(m, f); | ||||||
|  |  | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case KEY_RIGHT: |         case KEY_RIGHT: | ||||||
| @@ -791,6 +811,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|                 select_friend(self, key, &Friends.num_selected, Friends.num_friends); |                 select_friend(self, key, &Friends.num_selected, Friends.num_friends); | ||||||
|             else |             else | ||||||
|                 select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked); |                 select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked); | ||||||
|  |  | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -890,7 +911,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint64_t cur_time = time(NULL); |     time_t cur_time = get_unix_time(); | ||||||
|     struct tm cur_loc_tm = *localtime((const time_t *) &cur_time); |     struct tm cur_loc_tm = *localtime((const time_t *) &cur_time); | ||||||
|  |  | ||||||
|     wattron(self->window, A_BOLD); |     wattron(self->window, A_BOLD); | ||||||
| @@ -950,9 +971,11 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) | |||||||
|                     case TOX_USER_STATUS_NONE: |                     case TOX_USER_STATUS_NONE: | ||||||
|                         colour = GREEN; |                         colour = GREEN; | ||||||
|                         break; |                         break; | ||||||
|  |  | ||||||
|                     case TOX_USER_STATUS_AWAY: |                     case TOX_USER_STATUS_AWAY: | ||||||
|                         colour = YELLOW; |                         colour = YELLOW; | ||||||
|                         break; |                         break; | ||||||
|  |  | ||||||
|                     case TOX_USER_STATUS_BUSY: |                     case TOX_USER_STATUS_BUSY: | ||||||
|                         colour = RED; |                         colour = RED; | ||||||
|                         break; |                         break; | ||||||
| @@ -1027,16 +1050,16 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) | |||||||
|                     wattroff(self->window, COLOR_PAIR(BLUE)); |                     wattroff(self->window, COLOR_PAIR(BLUE)); | ||||||
|  |  | ||||||
|                 pthread_mutex_lock(&Winthread.lock); |                 pthread_mutex_lock(&Winthread.lock); | ||||||
|                 uint64_t last_seen = Friends.list[f].last_online.last_on; |                 time_t last_seen = Friends.list[f].last_online.last_on; | ||||||
|                 pthread_mutex_unlock(&Winthread.lock); |                 pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|                 if (last_seen != 0) { |                 if (last_seen != 0) { | ||||||
|                     pthread_mutex_lock(&Winthread.lock); |                     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |  | ||||||
|                     int day_dist = ( |                     int day_dist = ( | ||||||
|                             cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday |                                        cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday | ||||||
|                         + ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365) |                                        + ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365) | ||||||
|                     ); |                                    ); | ||||||
|                     const char *hourmin = Friends.list[f].last_online.hour_min_str; |                     const char *hourmin = Friends.list[f].last_online.hour_min_str; | ||||||
|  |  | ||||||
|                     pthread_mutex_unlock(&Winthread.lock); |                     pthread_mutex_unlock(&Winthread.lock); | ||||||
| @@ -1089,23 +1112,25 @@ void disable_chatwin(uint32_t f_num) | |||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index) | static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) | ||||||
| { | { | ||||||
|     int id = toxav_get_peer_id(av, call_index, 0); |     assert(0); | ||||||
|  |  | ||||||
|     if ( id != av_ErrorUnknown && id >= Friends.max_idx) |     if ( friend_number >= Friends.max_idx) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|  |     assert(0); | ||||||
|     Tox *m = toxav_get_tox(av); |     Tox *m = toxav_get_tox(av); | ||||||
|  |  | ||||||
|     if (Friends.list[id].chatwin == -1) { |     if (Friends.list[friend_number].chatwin == -1) { | ||||||
|         if (get_num_active_windows() < MAX_WINDOWS_NUM) { |         if (get_num_active_windows() < MAX_WINDOWS_NUM) { | ||||||
|             if (toxav_get_call_state(av, call_index) == av_CallStarting) { /* Only open windows when call is incoming */ |             if (state != TOXAV_FRIEND_CALL_STATE_FINISHED) { | ||||||
|                 Friends.list[id].chatwin = add_window(m, new_chat(m, Friends.list[id].num)); |                 Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num)); | ||||||
|  |                 set_active_window(Friends.list[friend_number].chatwin); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             char nick[TOX_MAX_NAME_LENGTH]; |             char nick[TOX_MAX_NAME_LENGTH]; | ||||||
|             get_nick_truncate(m, nick, Friends.list[id].num); |             get_nick_truncate(m, nick, Friends.list[friend_number].num); | ||||||
|             line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick); |             line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick); | ||||||
|  |  | ||||||
|             const char *errmsg = "* Warning: Too many windows are open."; |             const char *errmsg = "* Warning: Too many windows are open."; | ||||||
| @@ -1137,22 +1162,21 @@ ToxWindow new_friendlist(void) | |||||||
|     ret.onGroupInvite = &friendlist_onGroupInvite; |     ret.onGroupInvite = &friendlist_onGroupInvite; | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     ret.onInvite = &friendlist_onAv; |     ret.onInvite = &friendlist_onAV; | ||||||
|     ret.onRinging = &friendlist_onAv; |     ret.onRinging = &friendlist_onAV; | ||||||
|     ret.onStarting = &friendlist_onAv; |     ret.onStarting = &friendlist_onAV; | ||||||
|     ret.onEnding = &friendlist_onAv; |     ret.onEnding = &friendlist_onAV; | ||||||
|     ret.onError = &friendlist_onAv; |     ret.onError = &friendlist_onAV; | ||||||
|     ret.onStart = &friendlist_onAv; |     ret.onStart = &friendlist_onAV; | ||||||
|     ret.onCancel = &friendlist_onAv; |     ret.onCancel = &friendlist_onAV; | ||||||
|     ret.onReject = &friendlist_onAv; |     ret.onReject = &friendlist_onAV; | ||||||
|     ret.onEnd = &friendlist_onAv; |     ret.onEnd = &friendlist_onAV; | ||||||
|     ret.onRequestTimeout = &friendlist_onAv; |  | ||||||
|     ret.onPeerTimeout = &friendlist_onAv; |  | ||||||
|  |  | ||||||
|     ret.call_idx = -1; |     ret.is_call = false; | ||||||
|     ret.device_selection[0] = ret.device_selection[1] = -1; |     ret.device_selection[0] = ret.device_selection[1] = -1; | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  |     ret.num = -1; | ||||||
|     ret.active_box = -1; |     ret.active_box = -1; | ||||||
|  |  | ||||||
|     Help *help = calloc(1, sizeof(Help)); |     Help *help = calloc(1, sizeof(Help)); | ||||||
|   | |||||||
| @@ -35,11 +35,9 @@ struct LastOnline { | |||||||
|     char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */ |     char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct GroupChatInvite { | struct GroupInvite { | ||||||
|     char *key; |     uint8_t *data; | ||||||
|     uint16_t length; |     uint16_t length; | ||||||
|     uint8_t type; |  | ||||||
|     bool pending; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
| @@ -57,7 +55,7 @@ typedef struct { | |||||||
|     uint8_t status; |     uint8_t status; | ||||||
|  |  | ||||||
|     struct LastOnline last_online; |     struct LastOnline last_online; | ||||||
|     struct GroupChatInvite group_invite; |     struct GroupInvite group_invite; | ||||||
|  |  | ||||||
|     struct FileTransfer file_receiver[MAX_FILES]; |     struct FileTransfer file_receiver[MAX_FILES]; | ||||||
|     struct FileTransfer file_sender[MAX_FILES]; |     struct FileTransfer file_sender[MAX_FILES]; | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ | |||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <arpa/inet.h> |  | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| @@ -30,12 +29,14 @@ | |||||||
| #include "friendlist.h" | #include "friendlist.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "dns.h" |  | ||||||
| #include "groupchat.h" | #include "groupchat.h" | ||||||
| #include "prompt.h" | #include "prompt.h" | ||||||
| #include "help.h" | #include "help.h" | ||||||
| #include "term_mplex.h" | #include "term_mplex.h" | ||||||
| #include "avatars.h" | #include "avatars.h" | ||||||
|  | #include "name_lookup.h" | ||||||
|  | #include "qr_code.h" | ||||||
|  | #include "toxic_strings.h" | ||||||
|  |  | ||||||
| extern char *DATA_FILE; | extern char *DATA_FILE; | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| @@ -50,9 +51,9 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|         return; |         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."); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -129,6 +130,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg | |||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_ERR_FRIEND_ADD_NULL: |         case TOX_ERR_FRIEND_ADD_NULL: | ||||||
|  |  | ||||||
|         /* fallthrough */ |         /* fallthrough */ | ||||||
|         default: |         default: | ||||||
|             errmsg = "Faile to add friend: Unknown error."; |             errmsg = "Faile to add friend: Unknown error."; | ||||||
| @@ -192,8 +194,8 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         cmd_add_helper(self, m, id_bin, msg); |         cmd_add_helper(self, m, id_bin, msg); | ||||||
|     } else {    /* assume id is a username@domain address and do DNS lookup */ |     } else {    /* assume id is a username@domain address and do http name server lookup */ | ||||||
|         dns3_lookup(self, m, id_bin, id, msg); |         name_lookup(self, m, id_bin, id, msg); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -248,23 +250,26 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *ip = argv[1]; |     const char *ip = argv[1]; | ||||||
|     const char *port = argv[2]; |     const char *port_str = argv[2]; | ||||||
|     const char *ascii_key = argv[3]; |     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."); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; |     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) { |     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."); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     TOX_ERR_BOOTSTRAP err; |     TOX_ERR_BOOTSTRAP err; | ||||||
|     tox_bootstrap(m, ip, atoi(port), (uint8_t *) key_binary, &err); |     tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err); | ||||||
|     tox_add_tcp_relay(m, ip, atoi(port), (uint8_t *) key_binary, &err); |     tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err); | ||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_BOOTSTRAP_BAD_HOST: |         case TOX_ERR_BOOTSTRAP_BAD_HOST: | ||||||
| @@ -278,6 +283,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|         case TOX_ERR_BOOTSTRAP_NULL: |         case TOX_ERR_BOOTSTRAP_NULL: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
| @@ -290,9 +296,9 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | |||||||
|         return; |         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."); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -323,42 +329,133 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio"); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name required"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t type; |     const char *tmp_name = argv[1]; | ||||||
|  |     int len = strlen(tmp_name); | ||||||
|  |  | ||||||
|     if (!strcasecmp(argv[1], "audio")) |     if (len == 0 || len > TOX_GROUP_MAX_GROUP_NAME_LENGTH) { | ||||||
|         type = TOX_GROUPCHAT_TYPE_AV; |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group name."); | ||||||
|     else if (!strcasecmp(argv[1], "text")) |  | ||||||
|         type = TOX_GROUPCHAT_TYPE_TEXT; |  | ||||||
|     else { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio"); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int groupnum = -1; |     char name[TOX_GROUP_MAX_GROUP_NAME_LENGTH]; | ||||||
|  |  | ||||||
|     if (type == TOX_GROUPCHAT_TYPE_TEXT) |     if (argv[1][0] == '\"') {    /* remove opening and closing quotes */ | ||||||
|         groupnum = tox_add_groupchat(m); |         snprintf(name, sizeof(name), "%s", &argv[1][1]); | ||||||
| #ifdef AUDIO |         len -= 2; | ||||||
|     else |         name[len] = '\0'; | ||||||
|         groupnum = toxav_add_av_groupchat(m, NULL, NULL); |     } else { | ||||||
| #endif |         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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|     if (groupnum == -1) { |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize."); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (init_groupchat_win(prompt, m, groupnum, type) == -1) { |     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."); |         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; |         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]) | void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -368,9 +465,9 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|  |  | ||||||
|     if (argc == 0) { |     if (argc == 0) { | ||||||
|         if (log->log_on) |         if (log->log_on) | ||||||
|             msg = "Logging for this window is ON. Type \"/log off\" to disable."; |             msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)"; | ||||||
|         else |         else | ||||||
|             msg = "Logging for this window is OFF. Type \"/log on\" to enable."; |             msg = "Logging for this window is OFF; type \"/log on\" to enable."; | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|         return; |         return; | ||||||
| @@ -413,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]) | void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     char id[TOX_ADDRESS_SIZE * 2 + 1] = {0}; |     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||||
|     char address[TOX_ADDRESS_SIZE]; |     char bin_id[TOX_ADDRESS_SIZE]; | ||||||
|     tox_self_get_address(m, (uint8_t *) address); |     tox_self_get_address(m, (uint8_t *) bin_id); | ||||||
|  |  | ||||||
|     size_t i; |     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID."); | ||||||
|     for (i = 0; i < TOX_ADDRESS_SIZE; ++i) { |         return; | ||||||
|         char xx[3]; |  | ||||||
|         snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff); |  | ||||||
|         strcat(id, xx); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, 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]) | void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -457,6 +609,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|  |  | ||||||
|     tox_self_set_name(m, (uint8_t *) nick, len, NULL); |     tox_self_set_name(m, (uint8_t *) nick, len, NULL); | ||||||
|     prompt_update_nick(prompt, nick); |     prompt_update_nick(prompt, nick); | ||||||
|  |     set_nick_all_groups(m, nick, len); | ||||||
|  |  | ||||||
|     store_data(m, DATA_FILE); |     store_data(m, DATA_FILE); | ||||||
| } | } | ||||||
| @@ -468,18 +621,32 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (argv[1][0] != '\"') { |     prompt_update_statusmessage(prompt, m, argv[1]); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes."); | } | ||||||
|         return; |  | ||||||
|  | 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 */ |     uint32_t old_nospam = tox_self_get_nospam(m); | ||||||
|     char msg[MAX_STR_SIZE]; |     tox_self_set_nospam(m, (uint32_t) nospam); | ||||||
|     snprintf(msg, sizeof(msg), "%s", &argv[1][1]); |  | ||||||
|     int len = strlen(msg) - 1; |  | ||||||
|     msg[len] = '\0'; |  | ||||||
|  |  | ||||||
|     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]) | void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| @@ -554,6 +721,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | |||||||
|  |  | ||||||
|     tox_self_set_status(m, status); |     tox_self_set_status(m, status); | ||||||
|     prompt_update_status(prompt, status); |     prompt_update_status(prompt, status); | ||||||
|  |     set_status_all_groups(m, status); | ||||||
|  |  | ||||||
|     if (have_note) { |     if (have_note) { | ||||||
|         if (argv[2][0] != '\"') { |         if (argv[2][0] != '\"') { | ||||||
|   | |||||||
| @@ -33,10 +33,13 @@ void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE | |||||||
| void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| @@ -49,4 +52,9 @@ void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_S | |||||||
| void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| #endif /* AUDIO */ | #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 */ | #endif /* #define GLOBAL_COMMANDS_H */ | ||||||
|   | |||||||
| @@ -21,59 +21,707 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <stdlib.h> | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
|  | #include "groupchat.h" | ||||||
|  |  | ||||||
| void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | extern GroupChat groupchats[MAX_GROUPCHAT_NUM]; | ||||||
|  |  | ||||||
|  | void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     char title[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) { |     if (argc < 1) { | ||||||
|         int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH); |         size_t num_banned = tox_group_ban_get_list_size(m, self->num, &err); | ||||||
|  |  | ||||||
|         if (tlen != -1) { |         if (err != TOX_ERR_GROUP_BAN_QUERY_OK) { | ||||||
|             title[tlen] = '\0'; |             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list size (error %d).", err); | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); |             return; | ||||||
|         } else { |         } | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set"); |  | ||||||
|  |         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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (argv[1][0] != '\"') { |     cmd_kickban_helper(self, m, argv[1], true); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes."); | } | ||||||
|  |  | ||||||
|  | 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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* remove opening and closing quotes */ |     int ban_id = atoi(argv[1]); | ||||||
|     snprintf(title, sizeof(title), "%s", &argv[1][1]); |  | ||||||
|     int len = strlen(title) - 1; |  | ||||||
|     title[len] = '\0'; |  | ||||||
|  |  | ||||||
|     if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) { |     if (ban_id == 0 && strcmp(argv[1], "0")) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title."); |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be a non-negative interger."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     set_window_title(self, title, len); |     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]; |     char timefrmt[TIME_STR_SIZE]; | ||||||
|     char selfnick[TOX_MAX_NAME_LENGTH]; |  | ||||||
|  |  | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |     get_time_str(timefrmt, sizeof(timefrmt)); | ||||||
|  |  | ||||||
|     tox_self_get_name(m, (uint8_t *) selfnick); |     TOX_ERR_GROUP_SELF_QUERY sn_err; | ||||||
|     size_t sn_len = tox_self_get_name_size(m); |     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'; |     selfnick[sn_len] = '\0'; | ||||||
|  |  | ||||||
|     line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title); |     line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, MAGENTA, "-!- You set the topic to: %s", topic); | ||||||
|  |  | ||||||
|     char tmp_event[MAX_STR_SIZE]; |     char tmp_event[MAX_STR_SIZE]; | ||||||
|     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); |     snprintf(tmp_event, sizeof(tmp_event), "set topic to %s", topic); | ||||||
|     write_to_log(tmp_event, selfnick, self->chatwin->log, true); |     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); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -26,6 +26,23 @@ | |||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  |  | ||||||
| void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | 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 */ | #endif  /* GROUP_COMMANDS_H */ | ||||||
|   | |||||||
							
								
								
									
										1457
									
								
								src/groupchat.c
									
									
									
									
									
								
							
							
						
						
									
										1457
									
								
								src/groupchat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -26,61 +26,42 @@ | |||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| #ifdef AUDIO |  | ||||||
| #include "audio_call.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef AUDIO |  | ||||||
| #ifdef __APPLE__ |  | ||||||
| #include <OpenAL/al.h> |  | ||||||
| #include <OpenAL/alc.h> |  | ||||||
| #else |  | ||||||
| #include <AL/al.h> |  | ||||||
| #include <AL/alc.h> |  | ||||||
| /* compatibility with older versions of OpenAL */ |  | ||||||
| #ifndef ALC_ALL_DEVICES_SPECIFIER |  | ||||||
| #include <AL/alext.h> |  | ||||||
| #endif  /* ALC_ALL_DEVICES_SPECIFIER */ |  | ||||||
| #endif  /* __APPLE__ */ |  | ||||||
| #endif  /* AUDIO */ |  | ||||||
|  |  | ||||||
| #define SIDEBAR_WIDTH 16 | #define SIDEBAR_WIDTH 16 | ||||||
| #define SDBAR_OFST 2    /* Offset for the peer number box at the top of the statusbar */ | #define SDBAR_OFST 2    /* Offset for the peer number box at the top of the statusbar */ | ||||||
| #define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2 | #define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2 | ||||||
| #define GROUP_EVENT_WAIT 3 | #define GROUP_EVENT_WAIT 3 | ||||||
|  |  | ||||||
| #ifdef AUDIO | struct GroupPeer { | ||||||
| struct GAudio { |     bool       active; | ||||||
|     ALCdevice  *dvhandle;    /* Handle of device selected/opened */ |     char       name[TOX_MAX_NAME_LENGTH]; | ||||||
|     ALCcontext *dvctx; |     size_t     name_length; | ||||||
|     ALuint source; |     uint32_t   peer_id; | ||||||
|     ALuint buffers[OPENAL_BUFS]; |     uint8_t    public_key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE]; | ||||||
|  |     TOX_USER_STATUS status; | ||||||
|  |     TOX_GROUP_ROLE  role; | ||||||
|  |     uint64_t   last_active; | ||||||
| }; | }; | ||||||
| #endif  /* AUDIO */ |  | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     int chatwin; |     struct GroupPeer *peer_list; | ||||||
|     bool active; |     char       *name_list;    /* List of peer names, needed for tab completion */ | ||||||
|     uint8_t type; |     uint32_t   num_peers;     /* Number of peers in the chat/name_list array */ | ||||||
|     int num_peers; |     uint32_t   max_idx;       /* Maximum peer list index - 1 */ | ||||||
|     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ |     uint32_t   groupnumber; | ||||||
|     uint64_t start_time; |     int        chatwin; | ||||||
|     uint8_t  *peer_names; |     bool       active; | ||||||
|     uint8_t  *oldpeer_names; |     uint64_t   time_connected;    /* The time we successfully connected to the group */ | ||||||
|     uint16_t *peer_name_lengths; |     int        side_pos;     /* current position of the sidebar - used for scrolling up and down */ | ||||||
|     uint16_t *oldpeer_name_lengths; |  | ||||||
|  |  | ||||||
| #ifdef AUDIO |  | ||||||
|     struct GAudio audio; |  | ||||||
| #endif |  | ||||||
| } GroupChat; | } GroupChat; | ||||||
|  |  | ||||||
| void close_groupchat(ToxWindow *self, Tox *m, int groupnum); | void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum); | ||||||
| int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type); | 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 with or without the peerlist */ | /* destroys and re-creates groupchat window */ | ||||||
| void redraw_groupchat_win(ToxWindow *self); | void redraw_groupchat_win(ToxWindow *self); | ||||||
|  |  | ||||||
| ToxWindow new_group_chat(Tox *m, int groupnum); |  | ||||||
|  |  | ||||||
| #endif /* #define GROUPCHAT_H */ | #endif /* #define GROUPCHAT_H */ | ||||||
|   | |||||||
							
								
								
									
										86
									
								
								src/help.c
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								src/help.c
									
									
									
									
									
								
							| @@ -60,6 +60,9 @@ static void help_init_window(ToxWindow *self, int height, int width) | |||||||
|     int y2, x2; |     int y2, x2; | ||||||
|     getmaxyx(stdscr, y2, x2); |     getmaxyx(stdscr, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     height = MIN(height, y2); |     height = MIN(height, y2); | ||||||
|     width = MIN(width, x2); |     width = MIN(width, x2); | ||||||
|  |  | ||||||
| @@ -150,10 +153,17 @@ static void help_draw_global(ToxWindow *self) | |||||||
|     wprintw(win, "  /connect <ip> <port> <key> : Manually connect to a DHT node\n"); |     wprintw(win, "  /connect <ip> <port> <key> : Manually connect to a DHT node\n"); | ||||||
|     wprintw(win, "  /status <type> <msg>       : Set status with optional note\n"); |     wprintw(win, "  /status <type> <msg>       : Set status with optional note\n"); | ||||||
|     wprintw(win, "  /note <msg>                : Set a personal note\n"); |     wprintw(win, "  /note <msg>                : Set a personal note\n"); | ||||||
|  |     wprintw(win, "  /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, "  /nick <nick>               : Set your nickname\n"); | ||||||
|  |     wprintw(win, "  /nospam <value>            : Change part of your Tox ID to stop spam\n"); | ||||||
|     wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n"); |     wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n"); | ||||||
|     wprintw(win, "  /group <type>              : Create a group chat where type: text | audio\n"); |  | ||||||
|     wprintw(win, "  /myid                      : Print your Tox ID\n"); |     wprintw(win, "  /myid                      : Print your Tox ID\n"); | ||||||
|  | #ifdef QRPNG | ||||||
|  |     wprintw(win, "  /myqr <txt> or <png>       : Print your Tox ID's QR code to a file.\n"); | ||||||
|  | #else | ||||||
|  |     wprintw(win, "  /myqr                      : Print your Tox ID's QR code to a file.\n"); | ||||||
|  | #endif /* QRPNG */ | ||||||
|     wprintw(win, "  /clear                     : Clear window history\n"); |     wprintw(win, "  /clear                     : Clear window history\n"); | ||||||
|     wprintw(win, "  /close                     : Close the current chat window\n"); |     wprintw(win, "  /close                     : Close the current chat window\n"); | ||||||
|     wprintw(win, "  /quit or /exit             : Exit Toxic\n"); |     wprintw(win, "  /quit or /exit             : Exit Toxic\n"); | ||||||
| @@ -167,6 +177,15 @@ static void help_draw_global(ToxWindow *self) | |||||||
|     wprintw(win, "  /sdev <type> <id>          : Set active device\n"); |     wprintw(win, "  /sdev <type> <id>          : Set active device\n"); | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, "\n Video:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|  |     wprintw(win, "  /lsvdev <type>             : List video devices where type: in|out\n"); | ||||||
|  |     wprintw(win, "  /svdev <type> <id>         : Set active video device\n"); | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|     help_draw_bottom_menu(win); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|     box(win, ACS_VLINE, ACS_HLINE); |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
| @@ -183,8 +202,7 @@ static void help_draw_chat(ToxWindow *self) | |||||||
|     wprintw(win, "Chat Commands:\n"); |     wprintw(win, "Chat Commands:\n"); | ||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|     wprintw(win, "  /invite <n>                : Invite contact to a group chat\n"); |     wprintw(win, "  /gaccept <password>        : Accept a group invite with optional password\n"); | ||||||
|     wprintw(win, "  /join                      : Join a pending group chat\n"); |  | ||||||
|     wprintw(win, "  /sendfile <path>           : Send a file\n"); |     wprintw(win, "  /sendfile <path>           : Send a file\n"); | ||||||
|     wprintw(win, "  /savefile <id>             : Receive a file\n"); |     wprintw(win, "  /savefile <id>             : Receive a file\n"); | ||||||
|     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); |     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); | ||||||
| @@ -203,6 +221,13 @@ static void help_draw_chat(ToxWindow *self) | |||||||
|     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n"); |     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n"); | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |     wattron(win, A_BOLD); | ||||||
|  |     wprintw(win, "\n Video:\n"); | ||||||
|  |     wattroff(win, A_BOLD); | ||||||
|  |     wprintw(win, "  /video                     : Toggle video call\n"); | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|     help_draw_bottom_menu(win); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|     box(win, ACS_VLINE, ACS_HLINE); |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
| @@ -224,7 +249,9 @@ static void help_draw_keys(ToxWindow *self) | |||||||
|     wprintw(win, "  Ctrl+F and Ctrl+V         : Scroll window history half a page\n"); |     wprintw(win, "  Ctrl+F and Ctrl+V         : Scroll window history half a page\n"); | ||||||
|     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); |     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); | ||||||
|     wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n"); |     wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n"); | ||||||
|     wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n\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"); |     wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n"); | ||||||
|  |  | ||||||
|     help_draw_bottom_menu(win); |     help_draw_bottom_menu(win); | ||||||
| @@ -243,7 +270,32 @@ static void help_draw_group(ToxWindow *self) | |||||||
|     wprintw(win, "Group commands:\n"); |     wprintw(win, "Group commands:\n"); | ||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|     wprintw(win, "  /title <msg>               : Set group title (show current title if no msg)\n\n"); |     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); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
| @@ -275,32 +327,36 @@ static void help_draw_contacts(ToxWindow *self) | |||||||
|  |  | ||||||
| void help_onKey(ToxWindow *self, wint_t key) | void help_onKey(ToxWindow *self, wint_t key) | ||||||
| { | { | ||||||
|     switch(key) { |     switch (key) { | ||||||
|         case 'x': |         case 'x': | ||||||
|         case T_KEY_ESC: |         case T_KEY_ESC: | ||||||
|             help_exit(self); |             help_exit(self); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'c': |         case 'c': | ||||||
| #ifdef AUDIO | #ifdef VIDEO | ||||||
|             help_init_window(self, 19, 80); |             help_init_window(self, 21, 80); | ||||||
|  | #elif AUDIO | ||||||
|  |             help_init_window(self, 18, 80); | ||||||
| #else | #else | ||||||
|             help_init_window(self, 9, 80); |             help_init_window(self, 10, 80); | ||||||
| #endif | #endif | ||||||
|             self->help->type = HELP_CHAT; |             self->help->type = HELP_CHAT; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'g': |         case 'g': | ||||||
| #ifdef AUDIO | #ifdef VIDEO | ||||||
|             help_init_window(self, 24, 80); |             help_init_window(self, 30, 80); | ||||||
|  | #elif AUDIO | ||||||
|  |             help_init_window(self, 26, 80); | ||||||
| #else | #else | ||||||
|             help_init_window(self, 20, 80); |             help_init_window(self, 22, 80); | ||||||
| #endif | #endif | ||||||
|             self->help->type = HELP_GLOBAL; |             self->help->type = HELP_GLOBAL; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'r': |         case 'r': | ||||||
|             help_init_window(self, 6, 80); |             help_init_window(self, 25, 80); | ||||||
|             self->help->type = HELP_GROUP; |             self->help->type = HELP_GROUP; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
| @@ -310,7 +366,7 @@ void help_onKey(ToxWindow *self, wint_t key) | |||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'k': |         case 'k': | ||||||
|             help_init_window(self, 13, 80); |             help_init_window(self, 15, 80); | ||||||
|             self->help->type = HELP_KEYS; |             self->help->type = HELP_KEYS; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
| @@ -325,7 +381,7 @@ void help_onDraw(ToxWindow *self) | |||||||
| { | { | ||||||
|     curs_set(0); |     curs_set(0); | ||||||
|  |  | ||||||
|     switch(self->help->type) { |     switch (self->help->type) { | ||||||
|         case HELP_MENU: |         case HELP_MENU: | ||||||
|             help_draw_menu(self); |             help_draw_menu(self); | ||||||
|             return; |             return; | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								src/input.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/input.c
									
									
									
									
									
								
							| @@ -42,9 +42,12 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_ | |||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     /* this is the only place we need to do this check */ | ||||||
|  |     if (key == '\n') | ||||||
|  |         key = L'¶'; | ||||||
|  |  | ||||||
|     int cur_len = wcwidth(key); |     int cur_len = wcwidth(key); | ||||||
|  |  | ||||||
|     /* this is the only place we need to do this check */ |  | ||||||
|     if (cur_len == -1) { |     if (cur_len == -1) { | ||||||
|         sound_notify(self, notif_error, 0, NULL); |         sound_notify(self, notif_error, 0, NULL); | ||||||
|         return; |         return; | ||||||
| @@ -266,15 +269,19 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | |||||||
|  |  | ||||||
|     /* TODO: this special case is ugly. |     /* TODO: this special case is ugly. | ||||||
|        maybe convert entire function to if/else and make them all customizable keys? */ |        maybe convert entire function to if/else and make them all customizable keys? */ | ||||||
|     if (!match && key == user_settings->key_toggle_peerlist) { |     if (!match) { | ||||||
|         if (self->is_groupchat) { |         if (key == user_settings->key_toggle_peerlist) { | ||||||
|             self->show_peerlist ^= 1; |             if (self->is_groupchat) { | ||||||
|             redraw_groupchat_win(self); |                 self->show_peerlist ^= 1; | ||||||
|  |                 redraw_groupchat_win(self); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             match = true; | ||||||
|  |         } else if (key == user_settings->key_toggle_pastemode) { | ||||||
|  |             self->chatwin->pastemode ^= 1; | ||||||
|  |             match = true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         match = true; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     return match; |     return match; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -158,17 +158,24 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons | |||||||
|     /* for type-specific formatting in print function */ |     /* for type-specific formatting in print function */ | ||||||
|     switch (type) { |     switch (type) { | ||||||
|         case IN_ACTION: |         case IN_ACTION: | ||||||
|  |  | ||||||
|         /* fallthrough */ |         /* fallthrough */ | ||||||
|         case OUT_ACTION: |         case OUT_ACTION: | ||||||
|             len += strlen(user_settings->line_normal) + 2; |             len += strlen(user_settings->line_normal) + 2; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case IN_MSG: |         case IN_MSG: | ||||||
|  |  | ||||||
|         /* fallthrough */ |         /* fallthrough */ | ||||||
|         case OUT_MSG: |         case OUT_MSG: | ||||||
|             len += strlen(user_settings->line_normal) + 3; |             len += strlen(user_settings->line_normal) + 3; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|  |         case IN_PRVT_MSG: | ||||||
|  |         case OUT_PRVT_MSG: | ||||||
|  |             len += strlen(user_settings->line_special) + 3; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|         case CONNECTION: |         case CONNECTION: | ||||||
|             len += strlen(user_settings->line_join) + 2; |             len += strlen(user_settings->line_join) + 2; | ||||||
|             break; |             break; | ||||||
| @@ -304,10 +311,14 @@ void line_info_print(ToxWindow *self) | |||||||
|  |  | ||||||
|         switch (type) { |         switch (type) { | ||||||
|             case OUT_MSG: |             case OUT_MSG: | ||||||
|  |  | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case OUT_MSG_READ: |             case OUT_MSG_READ: | ||||||
|  |  | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case IN_MSG: |             case IN_MSG: | ||||||
|  |             case IN_PRVT_MSG: | ||||||
|  |             case OUT_PRVT_MSG: | ||||||
|                 wattron(win, COLOR_PAIR(BLUE)); |                 wattron(win, COLOR_PAIR(BLUE)); | ||||||
|                 wprintw(win, "%s ", line->timestr); |                 wprintw(win, "%s ", line->timestr); | ||||||
|                 wattroff(win, COLOR_PAIR(BLUE)); |                 wattroff(win, COLOR_PAIR(BLUE)); | ||||||
| @@ -320,20 +331,33 @@ void line_info_print(ToxWindow *self) | |||||||
|                     nameclr = CYAN; |                     nameclr = CYAN; | ||||||
|  |  | ||||||
|                 wattron(win, COLOR_PAIR(nameclr)); |                 wattron(win, COLOR_PAIR(nameclr)); | ||||||
|                 wprintw(win, "%s %s: ", user_settings->line_normal, line->name1); |                 wprintw(win, "%s %s: ", (type != OUT_PRVT_MSG && type != IN_PRVT_MSG) ? | ||||||
|  |                         user_settings->line_normal : | ||||||
|  |                         user_settings->line_special, | ||||||
|  |                         line->name1); | ||||||
|                 wattroff(win, COLOR_PAIR(nameclr)); |                 wattroff(win, COLOR_PAIR(nameclr)); | ||||||
|  |  | ||||||
|                 if (line->msg[0] == '>') |                 char *msg = line->msg; | ||||||
|                     wattron(win, COLOR_PAIR(GREEN)); |  | ||||||
|                 else if (line->msg[0] == '<') |  | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |  | ||||||
|  |  | ||||||
|                 wprintw(win, "%s", line->msg); |                 while (msg) { | ||||||
|  |                     char *line = strsep(&msg, "\n"); | ||||||
|  |  | ||||||
|                 if (line->msg[0] == '>') |                     if (line[0] == '>') | ||||||
|                     wattroff(win, COLOR_PAIR(GREEN)); |                         wattron(win, COLOR_PAIR(GREEN)); | ||||||
|                 else if (line->msg[0] == '<') |                     else if (line[0] == '<') | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |                         wattron(win, COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |                     wprintw(win, "%s%c", line, msg ? '\n' : '\0'); | ||||||
|  |  | ||||||
|  |                     if (line[0] == '>') | ||||||
|  |                         wattroff(win, COLOR_PAIR(GREEN)); | ||||||
|  |                     else if (line[0] == '<') | ||||||
|  |                         wattroff(win, COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|  |                     // change the \0 set by strsep back to \n | ||||||
|  |                     if (msg) | ||||||
|  |                         msg[-1] = '\n'; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { |                 if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |                     wattron(win, COLOR_PAIR(RED)); | ||||||
| @@ -350,8 +374,10 @@ void line_info_print(ToxWindow *self) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case OUT_ACTION_READ: |             case OUT_ACTION_READ: | ||||||
|  |  | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case OUT_ACTION: |             case OUT_ACTION: | ||||||
|  |  | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case IN_ACTION: |             case IN_ACTION: | ||||||
|                 wattron(win, COLOR_PAIR(BLUE)); |                 wattron(win, COLOR_PAIR(BLUE)); | ||||||
| @@ -538,20 +564,15 @@ bool line_info_onKey(ToxWindow *self, wint_t key) | |||||||
|  |  | ||||||
|     if (key == user_settings->key_half_page_up) { |     if (key == user_settings->key_half_page_up) { | ||||||
|         line_info_page_up(self, hst); |         line_info_page_up(self, hst); | ||||||
|     } |     } else if (key == user_settings->key_half_page_down) { | ||||||
|     else if (key == user_settings->key_half_page_down) { |  | ||||||
|         line_info_page_down(self, hst); |         line_info_page_down(self, hst); | ||||||
|     } |     } else if (key == user_settings->key_scroll_line_up) { | ||||||
|     else if (key == user_settings->key_scroll_line_up) { |  | ||||||
|         line_info_scroll_up(hst); |         line_info_scroll_up(hst); | ||||||
|     } |     } else if (key == user_settings->key_scroll_line_down) { | ||||||
|     else if (key == user_settings->key_scroll_line_down) { |  | ||||||
|         line_info_scroll_down(hst); |         line_info_scroll_down(hst); | ||||||
|     } |     } else if (key == user_settings->key_page_bottom) { | ||||||
|     else if (key == user_settings->key_page_bottom) { |  | ||||||
|         line_info_reset_start(self, hst); |         line_info_reset_start(self, hst); | ||||||
|     } |     } else { | ||||||
|     else { |  | ||||||
|         match = false; |         match = false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,10 +35,12 @@ enum { | |||||||
|     SYS_MSG, |     SYS_MSG, | ||||||
|     IN_MSG, |     IN_MSG, | ||||||
|     OUT_MSG, |     OUT_MSG, | ||||||
|     OUT_MSG_READ,    /* for sent messages that have received a read reply. don't set this with line_info_add */ |     OUT_MSG_READ,    /* for sent messages that have received a read reply. */ | ||||||
|     IN_ACTION, |     IN_ACTION, | ||||||
|     OUT_ACTION, |     OUT_ACTION, | ||||||
|     OUT_ACTION_READ,     /* same as OUT_MSG_READ but for actions */ |     OUT_ACTION_READ,     /* same as OUT_MSG_READ but for actions */ | ||||||
|  |     IN_PRVT_MSG,   /* PRVT should only be used for groups */ | ||||||
|  |     OUT_PRVT_MSG, | ||||||
|     PROMPT, |     PROMPT, | ||||||
|     CONNECTION, |     CONNECTION, | ||||||
|     DISCONNECTION, |     DISCONNECTION, | ||||||
| @@ -50,7 +52,7 @@ struct line_info { | |||||||
|     char name1[TOXIC_MAX_NAME_LENGTH + 1]; |     char name1[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     char name2[TOXIC_MAX_NAME_LENGTH + 1]; |     char name2[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     char msg[MAX_LINE_INFO_MSG_SIZE]; |     char msg[MAX_LINE_INFO_MSG_SIZE]; | ||||||
|     uint64_t timestamp; |     time_t timestamp; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|     uint8_t bold; |     uint8_t bold; | ||||||
|     uint8_t colour; |     uint8_t colour; | ||||||
|   | |||||||
| @@ -262,6 +262,7 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other | |||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| on_error: | on_error: | ||||||
|  |  | ||||||
|     if (log_on) |     if (log_on) | ||||||
|         log_enable(src, selfkey, otherkey, log, LOG_CHAT); |         log_enable(src, selfkey, otherkey, log, LOG_CHAT); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ | |||||||
|  |  | ||||||
| struct chatlog { | struct chatlog { | ||||||
|     FILE *file; |     FILE *file; | ||||||
|     uint64_t lastwrite; |     time_t lastwrite; | ||||||
|     char path[MAX_STR_SIZE]; |     char path[MAX_STR_SIZE]; | ||||||
|     bool log_on;    /* specific to current chat window */ |     bool log_on;    /* specific to current chat window */ | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -29,7 +29,7 @@ struct cqueue_msg { | |||||||
|     int line_id; |     int line_id; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|     uint32_t receipt; |     uint32_t receipt; | ||||||
|     uint64_t last_send_try; |     time_t last_send_try; | ||||||
|     struct cqueue_msg *next; |     struct cqueue_msg *next; | ||||||
|     struct cqueue_msg *prev; |     struct cqueue_msg *prev; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										184
									
								
								src/misc_tools.c
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								src/misc_tools.c
									
									
									
									
									
								
							| @@ -26,6 +26,12 @@ | |||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
| #include <dirent.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 <sys/stat.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| @@ -37,8 +43,6 @@ | |||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| static uint64_t current_unix_time; |  | ||||||
|  |  | ||||||
| void hst_to_net(uint8_t *num, uint16_t numbytes) | void hst_to_net(uint8_t *num, uint16_t numbytes) | ||||||
| { | { | ||||||
| #ifndef WORDS_BIGENDIAN | #ifndef WORDS_BIGENDIAN | ||||||
| @@ -54,19 +58,13 @@ void hst_to_net(uint8_t *num, uint16_t numbytes) | |||||||
|     return; |     return; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Note: The time functions are not thread safe */ | time_t get_unix_time(void) | ||||||
| void update_unix_time(void) |  | ||||||
| { | { | ||||||
|     current_unix_time = (uint64_t) time(NULL); |     return time(NULL); | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t get_unix_time(void) |  | ||||||
| { |  | ||||||
|     return current_unix_time; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | /* Returns 1 if connection has timed out, 0 otherwise */ | ||||||
| int timed_out(uint64_t timestamp, uint64_t timeout) | int timed_out(time_t timestamp, time_t timeout) | ||||||
| { | { | ||||||
|     return timestamp + timeout <= get_unix_time(); |     return timestamp + timeout <= get_unix_time(); | ||||||
| } | } | ||||||
| @@ -75,12 +73,12 @@ int timed_out(uint64_t timestamp, uint64_t timeout) | |||||||
| struct tm *get_time(void) | struct tm *get_time(void) | ||||||
| { | { | ||||||
|     struct tm *timeinfo; |     struct tm *timeinfo; | ||||||
|     uint64_t t = get_unix_time(); |     time_t t = get_unix_time(); | ||||||
|     timeinfo = localtime((const time_t*) &t); |     timeinfo = localtime((const time_t *) &t); | ||||||
|     return timeinfo; |     return timeinfo; | ||||||
| } | } | ||||||
|  |  | ||||||
| /*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) | void get_time_str(char *buf, int bufsize) | ||||||
| { | { | ||||||
|     if (user_settings->timestamps == TIMESTAMPS_OFF) { |     if (user_settings->timestamps == TIMESTAMPS_OFF) { | ||||||
| @@ -93,7 +91,7 @@ void get_time_str(char *buf, int bufsize) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||||
| void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs) | void get_elapsed_time_str(char *buf, int bufsize, time_t secs) | ||||||
| { | { | ||||||
|     if (!secs) |     if (!secs) | ||||||
|         return; |         return; | ||||||
| @@ -110,6 +108,24 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs) | |||||||
|         snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); |         snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Converts seconds to string in format H hours, m minutes, s seconds */ | ||||||
|  | void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs) | ||||||
|  | { | ||||||
|  |     if (!secs) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     long int seconds = secs % 60; | ||||||
|  |     long int minutes = (secs % 3600) / 60; | ||||||
|  |     long int hours = secs / 3600; | ||||||
|  |  | ||||||
|  |     if (!minutes && !hours) | ||||||
|  |         snprintf(buf, bufsize, "%ld seconds", seconds); | ||||||
|  |     else if (!hours) | ||||||
|  |         snprintf(buf, bufsize, "%ld minutes, %ld seconds", minutes, seconds); | ||||||
|  |     else | ||||||
|  |         snprintf(buf, bufsize, "%ld hours, %ld minutes, %ld seconds", hours, minutes, seconds); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. |  * 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. |  * output_size must be exactly half of hex_len. | ||||||
| @@ -149,6 +165,24 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr) | |||||||
|     return 0; |     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 */ | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
| int string_is_empty(const char *string) | int string_is_empty(const char *string) | ||||||
| { | { | ||||||
| @@ -158,6 +192,15 @@ int string_is_empty(const char *string) | |||||||
|     return string[0] == '\0'; |     return string[0] == '\0'; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
|  | int wstring_is_empty(const wchar_t *string) | ||||||
|  | { | ||||||
|  |     if (!string) | ||||||
|  |         return true; | ||||||
|  |  | ||||||
|  |     return string[0] == L'\0'; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* convert a multibyte string to a wide character string and puts in buf. */ | /* convert a multibyte string to a wide character string and puts in buf. */ | ||||||
| int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | ||||||
| { | { | ||||||
| @@ -166,7 +209,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | |||||||
|     if (n < len) |     if (n < len) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     if ((len = mbstowcs(buf, string, n)) == (size_t) -1) |     if ((len = mbstowcs(buf, string, n)) == (size_t) - 1) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     return len; |     return len; | ||||||
| @@ -180,7 +223,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n) | |||||||
|     if (n < len) |     if (n < len) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     if ((len = wcstombs(buf, string, n)) == (size_t) -1) |     if ((len = wcstombs(buf, string, n)) == (size_t) - 1) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     return len; |     return len; | ||||||
| @@ -207,11 +250,11 @@ int valid_nick(const char *nick) | |||||||
|  |  | ||||||
|     for (i = 0; nick[i]; ++i) { |     for (i = 0; nick[i]; ++i) { | ||||||
|         if ((nick[i] == ' ' && nick[i + 1] == ' ') |         if ((nick[i] == ' ' && nick[i + 1] == ' ') | ||||||
|             || nick[i] == '/' |                 || nick[i] == '/' | ||||||
|             || nick[i] == '\n' |                 || nick[i] == '\n' | ||||||
|             || nick[i] == '\t' |                 || nick[i] == '\t' | ||||||
|             || nick[i] == '\v' |                 || nick[i] == '\v' | ||||||
|             || nick[i] == '\r') |                 || nick[i] == '\r') | ||||||
|  |  | ||||||
|             return 0; |             return 0; | ||||||
|     } |     } | ||||||
| @@ -263,6 +306,27 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname) | |||||||
|     return strlen(namebuf); |     return strlen(namebuf); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Gets the base directory of path and puts it in dir. | ||||||
|  |  * dir must have at least as much space as path_len + 1. | ||||||
|  |  * | ||||||
|  |  * Returns the length of the base directory. | ||||||
|  |  */ | ||||||
|  | size_t get_base_dir(const char *path, size_t path_len, char *dir) | ||||||
|  | { | ||||||
|  |     if (path_len == 0 || path == NULL) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     size_t dir_len = char_rfind(path, '/', path_len); | ||||||
|  |  | ||||||
|  |     if (dir_len != 0 && dir_len < path_len) | ||||||
|  |         ++dir_len;  /* Leave trailing slash */ | ||||||
|  |  | ||||||
|  |     memcpy(dir, path, dir_len); | ||||||
|  |     dir[dir_len] = '\0'; | ||||||
|  |  | ||||||
|  |     return dir_len; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* converts str to all lowercase */ | /* converts str to all lowercase */ | ||||||
| void str_to_lower(char *str) | void str_to_lower(char *str) | ||||||
| { | { | ||||||
| @@ -293,13 +357,21 @@ size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* same as get_nick_truncate but for groupchats */ | /* same as get_nick_truncate but for groupchats */ | ||||||
| int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum) | int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum) | ||||||
| { | { | ||||||
|     int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf); |     TOX_ERR_GROUP_PEER_QUERY err; | ||||||
|  |     size_t len = tox_group_peer_get_name_size(m, groupnum, peer_id, &err); | ||||||
|  |  | ||||||
|     if (len == -1) { |     if (err != TOX_ERR_GROUP_PEER_QUERY_OK) { | ||||||
|         strcpy(buf, UNKNOWN_NAME); |         strcpy(buf, UNKNOWN_NAME); | ||||||
|         len = strlen(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); |     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||||
| @@ -309,19 +381,28 @@ int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* copies data to msg buffer. | /* copies data to msg buffer. | ||||||
|    returns length of msg, which will be no larger than size-1 */ |    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) | size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length) | ||||||
| { | { | ||||||
|     size_t len = MIN(length, size - 1); |     if (length > size - 1) { | ||||||
|     memcpy(msg, data, len); |         msg[0] = '\0'; | ||||||
|     msg[len] = '\0'; |         return 0; | ||||||
|     return len; |     } | ||||||
|  |  | ||||||
|  |     memcpy(msg, data, length); | ||||||
|  |     msg[length] = '\0'; | ||||||
|  |     return length; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* returns index of the first instance of ch in s starting at idx. | /* 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) | int char_find(int idx, const char *s, char ch) | ||||||
| { | { | ||||||
|  |     if (!s) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int i = idx; |     int i = idx; | ||||||
|  |  | ||||||
|     for (i = idx; s[i]; ++i) { |     for (i = idx; s[i]; ++i) { | ||||||
| @@ -332,10 +413,14 @@ int char_find(int idx, const char *s, char ch) | |||||||
|     return i; |     return i; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* returns index of the last instance of ch in s starting at len | /* returns index of the last instance of ch in s starting at len. | ||||||
|    returns 0 if char not found (skips 0th index) */ |    returns 0 if char not found or s is NULL (skips 0th index). */ | ||||||
| int char_rfind(const char *s, char ch, int len) | int char_rfind(const char *s, char ch, int len) | ||||||
| { | { | ||||||
|  |     if (!s) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |  | ||||||
|     for (i = len; i > 0; --i) { |     for (i = len; i > 0; --i) { | ||||||
| @@ -422,3 +507,36 @@ void set_window_title(ToxWindow *self, const char *title, int len) | |||||||
|  |  | ||||||
|     snprintf(self->name, sizeof(self->name), "%s", cpy); |     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; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -53,24 +53,34 @@ int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size | |||||||
| /* convert a hex string to bytes. returns 0 on success, -1 on failure */ | /* 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); | int hex_string_to_bytes(char *buf, int size, const char *keystr); | ||||||
|  |  | ||||||
|  | /* Converts a binary representation of a Tox ID into a string. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size); | ||||||
|  |  | ||||||
| /* get the current unix time (not thread safe) */ | /* get the current unix time (not thread safe) */ | ||||||
| uint64_t get_unix_time(void); | time_t get_unix_time(void); | ||||||
|  |  | ||||||
| /* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */ | /* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */ | ||||||
| void get_time_str(char *buf, int bufsize); | void get_time_str(char *buf, int bufsize); | ||||||
|  |  | ||||||
| /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||||
| void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs); | void get_elapsed_time_str(char *buf, int bufsize, time_t secs); | ||||||
|  |  | ||||||
|  | /* 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) */ | /* get the current local time (not thread safe) */ | ||||||
| struct tm *get_time(void); | struct tm *get_time(void); | ||||||
|  |  | ||||||
| /* updates current unix time (should be run once per do_toxic loop) */ |  | ||||||
| void update_unix_time(void); |  | ||||||
|  |  | ||||||
| /* Returns 1 if the string is empty, 0 otherwise */ | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
| int string_is_empty(const char *string); | int string_is_empty(const char *string); | ||||||
|  |  | ||||||
|  | /* Same as above but for wide character strings */ | ||||||
|  | int wstring_is_empty(const wchar_t *string); | ||||||
|  |  | ||||||
| /* convert a multibyte string to a wide character string (must provide buffer) */ | /* convert a multibyte string to a wide character string (must provide buffer) */ | ||||||
| int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||||
|  |  | ||||||
| @@ -81,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); | int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||||
|  |  | ||||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | /* Returns 1 if connection has timed out, 0 otherwise */ | ||||||
| int timed_out(uint64_t timestamp, uint64_t timeout); | int timed_out(time_t timestamp, time_t timeout); | ||||||
|  |  | ||||||
| /* Colours the window tab according to type. Beeps if is_beep is true */ | /* Colours the window tab according to type. Beeps if is_beep is true */ | ||||||
| void alert_window(ToxWindow *self, int type, bool is_beep); | void alert_window(ToxWindow *self, int type, bool is_beep); | ||||||
| @@ -103,6 +113,14 @@ void filter_str(char *str, size_t len); | |||||||
| /* gets base file name from path or original file name if no path is supplied */ | /* gets base file name from path or original file name if no path is supplied */ | ||||||
| size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname); | size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname); | ||||||
|  |  | ||||||
|  | /* Gets the base directory of path and puts it in dir. | ||||||
|  |  * dir must have at least as much space as path_len. | ||||||
|  |  * | ||||||
|  |  * Returns the length of the base directory on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | size_t get_base_dir(const char *path, size_t path_len, char *dir); | ||||||
|  |  | ||||||
| /* converts str to all lowercase */ | /* converts str to all lowercase */ | ||||||
| void str_to_lower(char *str); | void str_to_lower(char *str); | ||||||
|  |  | ||||||
| @@ -111,18 +129,19 @@ void str_to_lower(char *str); | |||||||
| size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | ||||||
|  |  | ||||||
| /* same as get_nick_truncate but for groupchats */ | /* same as get_nick_truncate but for groupchats */ | ||||||
| int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum); | int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum); | ||||||
|  |  | ||||||
| /* copies data to msg buffer. | /* copies data to msg buffer. | ||||||
|    returns length of msg, which will be no larger than size-1 */ |    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); | 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 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); | int char_find(int idx, const char *s, char ch); | ||||||
|  |  | ||||||
| /* returns index of the last instance of ch in s | /* returns index of the last instance of ch in s starting at len. | ||||||
|    returns 0 if char not found */ |    returns 0 if char not found or s is NULL (skips 0th index). */ | ||||||
| int char_rfind(const char *s, char ch, int len); | int char_rfind(const char *s, char ch, int len); | ||||||
|  |  | ||||||
| /* Converts bytes to appropriate unit and puts in buf as a string */ | /* Converts bytes to appropriate unit and puts in buf as a string */ | ||||||
| @@ -143,4 +162,16 @@ int check_file_signature(const char *signature, size_t size, FILE *fp); | |||||||
| /* sets window title in tab bar. */ | /* sets window title in tab bar. */ | ||||||
| void set_window_title(ToxWindow *self, const char *title, int len); | 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 */ | #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; | ||||||
|  | } | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| /*  dns.c
 | /*  name_lookup.h
 | ||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. |  *  Copyright (C) 2015 Toxic All Rights Reserved. | ||||||
|  * |  * | ||||||
|  *  This file is part of Toxic. |  *  This file is part of Toxic. | ||||||
|  * |  * | ||||||
| @@ -20,13 +20,17 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| /* Does DNS lookup for addr and puts resulting tox id in id_bin.
 | #ifndef NAME_LOOKUP | ||||||
|    Return 0 on success, -1 on failure. */ | #define NAME_LOOKUP | ||||||
| 
 | 
 | ||||||
| #ifndef DNS_H | /* Initializes http based name lookups. Note: This function must be called only once before additional
 | ||||||
| #define DNS_H |  * threads are spawned. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | int name_lookup_init(int curl_init_status); | ||||||
| 
 | 
 | ||||||
| /* creates new thread for dns3 lookup. Only allows one lookup at a time. */ | int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message); | ||||||
| void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg); |  | ||||||
| 
 | 
 | ||||||
| #endif /* #define DNS_H */ | #endif /* NAME_LOOKUP */ | ||||||
							
								
								
									
										173
									
								
								src/notify.c
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								src/notify.c
									
									
									
									
									
								
							| @@ -31,31 +31,31 @@ | |||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  |  | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
| #include "device.h" | #include "audio_device.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "xtra.h" | #include "xtra.h" | ||||||
|  |  | ||||||
| #if defined(AUDIO) || defined(SOUND_NOTIFY) | #if defined(AUDIO) || defined(SOUND_NOTIFY) | ||||||
|     #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|         #include <OpenAL/al.h> | #include <OpenAL/al.h> | ||||||
|         #include <OpenAL/alc.h> | #include <OpenAL/alc.h> | ||||||
|     #else | #else | ||||||
|         #include <AL/al.h> | #include <AL/al.h> | ||||||
|         #include <AL/alc.h> | #include <AL/alc.h> | ||||||
|         /* compatibility with older versions of OpenAL */ | /* compatibility with older versions of OpenAL */ | ||||||
|         #ifndef ALC_ALL_DEVICES_SPECIFIER | #ifndef ALC_ALL_DEVICES_SPECIFIER | ||||||
|             #include <AL/alext.h> | #include <AL/alext.h> | ||||||
|         #endif | #endif | ||||||
|     #endif | #endif | ||||||
|     #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|         #include <AL/alut.h> /* freealut packet */ | #include <AL/alut.h> /* freealut packet */ | ||||||
|     #endif | #endif | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|     #include <libnotify/notify.h> | #include <libnotify/notify.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define MAX_BOX_MSG_LEN 127 | #define MAX_BOX_MSG_LEN 127 | ||||||
| @@ -75,7 +75,7 @@ struct Control { | |||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     uint32_t device_idx; /* index of output device */ |     uint32_t device_idx; /* index of output device */ | ||||||
|     char* sounds[SOUNDS_SIZE]; |     char *sounds[SOUNDS_SIZE]; | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } Control = {0}; | } Control = {0}; | ||||||
|  |  | ||||||
| @@ -88,7 +88,7 @@ struct _ActiveNotifications { | |||||||
|     bool active; |     bool active; | ||||||
|     int *id_indicator; |     int *id_indicator; | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|     NotifyNotification* box; |     NotifyNotification *box; | ||||||
|     char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1]; |     char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1]; | ||||||
|     char title[64]; |     char title[64]; | ||||||
|     size_t size; |     size_t size; | ||||||
| @@ -188,15 +188,17 @@ void graceful_clear() | |||||||
|  |  | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||||
|             if (actives[i].active) { |             if (actives[i].active) { | ||||||
|             #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|  |  | ||||||
|                 if (actives[i].box) { |                 if (actives[i].box) { | ||||||
|                     GError* ignore; |                     GError *ignore; | ||||||
|                     notify_notification_close(actives[i].box, &ignore); |                     notify_notification_close(actives[i].box, &ignore); | ||||||
|                     actives[i].box = NULL; |                     actives[i].box = NULL; | ||||||
|                 } |                 } | ||||||
|             #endif |  | ||||||
|  |  | ||||||
|                 if(actives[i].id_indicator) | #endif | ||||||
|  |  | ||||||
|  |                 if (actives[i].id_indicator) | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||||
|  |  | ||||||
|                 if ( actives[i].looping ) { |                 if ( actives[i].looping ) { | ||||||
| @@ -221,11 +223,11 @@ void graceful_clear() | |||||||
|     control_unlock(); |     control_unlock(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void* do_playing(void* _p) | void *do_playing(void *_p) | ||||||
| { | { | ||||||
|     (void)_p; |     (void)_p; | ||||||
|  |  | ||||||
|     while(true) { |     while (true) { | ||||||
|         control_lock(); |         control_lock(); | ||||||
|  |  | ||||||
|         if (!Control.poll_active) { |         if (!Control.poll_active) { | ||||||
| @@ -234,65 +236,73 @@ void* do_playing(void* _p) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         bool has_looping = false; |         bool has_looping = false; | ||||||
|  |         bool test_active_notify = false; | ||||||
|         int i; |         int i; | ||||||
|  |  | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||||
|  |  | ||||||
|             if (actives[i].looping) has_looping = true; |             if (actives[i].looping) has_looping = true; | ||||||
|  |  | ||||||
|             if (actives[i].active && !actives[i].looping |             test_active_notify = actives[i].active && !actives[i].looping; | ||||||
|                 #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|                     && !actives[i].box |             test_active_notify = test_active_notify && !actives[i].box; | ||||||
|                 #endif | #endif | ||||||
|             ) { |  | ||||||
|                 if(actives[i].id_indicator) |             if (test_active_notify) { | ||||||
|  |                 if (actives[i].id_indicator) | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||||
|  |  | ||||||
|                 if (!is_playing(actives[i].source)) { |                 if (!is_playing(actives[i].source)) { | ||||||
|                 /* Close */ |                     /* Close */ | ||||||
|                     alSourceStop(actives[i].source); |                     alSourceStop(actives[i].source); | ||||||
|                     alDeleteSources(1, &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)); |                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         #ifdef BOX_NOTIFY |  | ||||||
|             else if (actives[i].box && time(NULL) >= actives[i].n_timeout) | #ifdef BOX_NOTIFY | ||||||
|             { |             else if (actives[i].box && time(NULL) >= actives[i].n_timeout) { | ||||||
|                 GError* ignore; |                 GError *ignore; | ||||||
|                 notify_notification_close(actives[i].box, &ignore); |                 notify_notification_close(actives[i].box, &ignore); | ||||||
|                 actives[i].box = NULL; |                 actives[i].box = NULL; | ||||||
|                 if(actives[i].id_indicator) |  | ||||||
|  |                 if (actives[i].id_indicator) | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||||
|  |  | ||||||
|                 if (!actives[i].looping && !is_playing(actives[i].source)) { |                 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); |                     alSourceStop(actives[i].source); | ||||||
|                     alDeleteSources(1, &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)); |                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         #endif |  | ||||||
|  | #endif | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/ |         /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/ | ||||||
|         if (device_opened && !has_looping && |         if (device_opened && !has_looping && | ||||||
|            (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) { |                 (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) { | ||||||
|             m_close_device(); |             m_close_device(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         has_looping = false; |         has_looping = false; | ||||||
|  |  | ||||||
|         control_unlock(); |         control_unlock(); | ||||||
|         usleep(10000); |         usleep(10000); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| int play_source(uint32_t source, uint32_t buffer, bool looping) | int play_source(uint32_t source, uint32_t buffer, bool looping) | ||||||
| { | { | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |  | ||||||
|     for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++); |     for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++); | ||||||
|  |  | ||||||
|     if ( i == ACTIVE_NOTIFS_MAX ) { |     if ( i == ACTIVE_NOTIFS_MAX ) { | ||||||
|         return -1; /* Full */ |         return -1; /* Full */ | ||||||
|     } |     } | ||||||
| @@ -308,11 +318,11 @@ int play_source(uint32_t source, uint32_t buffer, bool looping) | |||||||
| } | } | ||||||
|  |  | ||||||
| #elif BOX_NOTIFY | #elif BOX_NOTIFY | ||||||
| void* do_playing(void* _p) | void *do_playing(void *_p) | ||||||
| { | { | ||||||
|     (void)_p; |     (void)_p; | ||||||
|  |  | ||||||
|     while(true) { |     while (true) { | ||||||
|         control_lock(); |         control_lock(); | ||||||
|  |  | ||||||
|         if (!Control.poll_active) { |         if (!Control.poll_active) { | ||||||
| @@ -323,20 +333,22 @@ void* do_playing(void* _p) | |||||||
|         int i; |         int i; | ||||||
|  |  | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||||
|             if (actives[i].box && time(NULL) >= actives[i].n_timeout) |             if (actives[i].box && time(NULL) >= actives[i].n_timeout) { | ||||||
|             { |                 GError *ignore; | ||||||
|                 GError* ignore; |  | ||||||
|                 notify_notification_close(actives[i].box, &ignore); |                 notify_notification_close(actives[i].box, &ignore); | ||||||
|                 actives[i].box = NULL; |                 actives[i].box = NULL; | ||||||
|                 if(actives[i].id_indicator) |  | ||||||
|  |                 if (actives[i].id_indicator) | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||||
|  |  | ||||||
|                 memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |                 memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         control_unlock(); |         control_unlock(); | ||||||
|         usleep(10000); |         usleep(10000); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -347,7 +359,7 @@ void graceful_clear() | |||||||
|  |  | ||||||
|     for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |     for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||||
|         if (actives[i].box) { |         if (actives[i].box) { | ||||||
|             GError* ignore; |             GError *ignore; | ||||||
|             notify_notification_close(actives[i].box, &ignore); |             notify_notification_close(actives[i].box, &ignore); | ||||||
|             actives[i].box = NULL; |             actives[i].box = NULL; | ||||||
|         } |         } | ||||||
| @@ -378,6 +390,7 @@ int init_notify(int login_cooldown, int notification_timeout) | |||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||||
|  |  | ||||||
|     if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) |     if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
| @@ -419,7 +432,9 @@ void terminate_notify() | |||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |  | ||||||
|     for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]); |     for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]); | ||||||
|  |  | ||||||
|     alutExit(); |     alutExit(); | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
| @@ -429,7 +444,7 @@ void terminate_notify() | |||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
| int set_sound(Notification sound, const char* value) | int set_sound(Notification sound, const char *value) | ||||||
| { | { | ||||||
|     if (sound == silent) return 0; |     if (sound == silent) return 0; | ||||||
|  |  | ||||||
| @@ -457,10 +472,11 @@ int play_sound_internal(Notification what, bool loop) | |||||||
|     alSourcei(source, AL_LOOPING, loop); |     alSourcei(source, AL_LOOPING, loop); | ||||||
|  |  | ||||||
|     int rc = play_source(source, buffer, loop); |     int rc = play_source(source, buffer, loop); | ||||||
|  |  | ||||||
|     if (rc < 0) { |     if (rc < 0) { | ||||||
|         alSourceStop(source); |         alSourceStop(source); | ||||||
|         alDeleteSources(1, &source); |         alDeleteSources(1, &source); | ||||||
|         alDeleteBuffers(1,&buffer); |         alDeleteBuffers(1, &buffer); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -472,7 +488,8 @@ int play_notify_sound(Notification notif, uint64_t flags) | |||||||
|     int rc = -1; |     int rc = -1; | ||||||
|  |  | ||||||
|     if (flags & NT_BEEP) beep(); |     if (flags & NT_BEEP) beep(); | ||||||
|     else if (notif != silent) { |  | ||||||
|  |     if (notif != silent) { | ||||||
|         if ( !Control.poll_active || !Control.sounds[notif] ) |         if ( !Control.poll_active || !Control.sounds[notif] ) | ||||||
|             return -1; |             return -1; | ||||||
|  |  | ||||||
| @@ -487,17 +504,21 @@ void stop_sound(int id) | |||||||
| { | { | ||||||
|     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) { |     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) { | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|  |  | ||||||
|         if (actives[id].box) { |         if (actives[id].box) { | ||||||
|             GError* ignore; |             GError *ignore; | ||||||
|             notify_notification_close(actives[id].box, &ignore); |             notify_notification_close(actives[id].box, &ignore); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|         if (actives[id].id_indicator) |         if (actives[id].id_indicator) | ||||||
|             *actives[id].id_indicator = -1; |             *actives[id].id_indicator = -1; | ||||||
|  |  | ||||||
| //         alSourcei(actives[id].source, AL_LOOPING, false); | //         alSourcei(actives[id].source, AL_LOOPING, false); | ||||||
|         alSourceStop(actives[id].source); |         alSourceStop(actives[id].source); | ||||||
|         alDeleteSources(1, &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)); |         memset(&actives[id], 0, sizeof(struct _ActiveNotifications)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -508,6 +529,7 @@ static int m_play_sound(Notification notif, uint64_t flags) | |||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     return play_notify_sound(notif, flags); |     return play_notify_sound(notif, flags); | ||||||
| #else | #else | ||||||
|  |  | ||||||
|     if (notif != silent) |     if (notif != silent) | ||||||
|         beep(); |         beep(); | ||||||
|  |  | ||||||
| @@ -516,12 +538,12 @@ static int m_play_sound(Notification notif, uint64_t flags) | |||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef BOX_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 | #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); |     tab_notify(self, flags); | ||||||
|  |  | ||||||
| @@ -540,6 +562,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in | |||||||
|  |  | ||||||
|     if (id == -1) { |     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 ) { |         if ( id == ACTIVE_NOTIFS_MAX ) { | ||||||
|             control_unlock(); |             control_unlock(); | ||||||
|             return -1; /* Full */ |             return -1; /* Full */ | ||||||
| @@ -558,7 +581,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in | |||||||
|     return id; |     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); |     tab_notify(self, flags); | ||||||
|  |  | ||||||
| @@ -566,6 +589,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id) | |||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1; |     if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1; | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
| @@ -578,8 +602,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id) | |||||||
|  |  | ||||||
|     alSourceStop(actives[id].source); |     alSourceStop(actives[id].source); | ||||||
|     alDeleteSources(1, &actives[id].source); |     alDeleteSources(1, &actives[id].source); | ||||||
|     alDeleteBuffers(1,&actives[id].buffer); |     alDeleteBuffers(1, &actives[id].buffer); | ||||||
|  |  | ||||||
|  |  | ||||||
|     alGenSources(1, &actives[id].source); |     alGenSources(1, &actives[id].source); | ||||||
|     alGenBuffers(1, &actives[id].buffer); |     alGenBuffers(1, &actives[id].buffer); | ||||||
| @@ -593,6 +616,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id) | |||||||
|  |  | ||||||
|     return id; |     return id; | ||||||
| #else | #else | ||||||
|  |  | ||||||
|     if (notif != silent) |     if (notif != silent) | ||||||
|         beep(); |         beep(); | ||||||
|  |  | ||||||
| @@ -600,7 +624,8 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id) | |||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const 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)) { |     if (notifications_are_disabled(flags)) { | ||||||
|         tab_notify(self, flags); |         tab_notify(self, flags); | ||||||
| @@ -614,9 +639,11 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi | |||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|  |  | ||||||
|     if (id == -1) { /* Could not play */ |     if (id == -1) { /* Could not play */ | ||||||
|  |  | ||||||
|         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); |         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); | ||||||
|  |  | ||||||
|         if ( id == ACTIVE_NOTIFS_MAX ) { |         if ( id == ACTIVE_NOTIFS_MAX ) { | ||||||
|             control_unlock(); |             control_unlock(); | ||||||
|             return -1; /* Full */ |             return -1; /* Full */ | ||||||
| @@ -624,17 +651,23 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi | |||||||
|  |  | ||||||
|         actives[id].active = 1; |         actives[id].active = 1; | ||||||
|         actives[id].id_indicator = id_indicator; |         actives[id].id_indicator = id_indicator; | ||||||
|  |  | ||||||
|         if (id_indicator) *id_indicator = id; |         if (id_indicator) *id_indicator = id; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #else | #else | ||||||
|  |  | ||||||
|     if (id == -1) |     if (id == -1) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
| #endif    /* SOUND_NOTIFY */ | #endif    /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
|     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); |     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); | ||||||
|  |  | ||||||
|     if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); |     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__); |     vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||||
|     va_end (__ARGS__); |     va_end (__ARGS__); | ||||||
|  |  | ||||||
| @@ -657,7 +690,7 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi | |||||||
| #endif   /* BOX_NOTIFY */ | #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)) { |     if (notifications_are_disabled(flags)) { | ||||||
|         tab_notify(self, flags); |         tab_notify(self, flags); | ||||||
| @@ -676,7 +709,8 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con | |||||||
|         return -1; |         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__); |     vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||||
|     va_end (__ARGS__); |     va_end (__ARGS__); | ||||||
|  |  | ||||||
| @@ -689,7 +723,8 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con | |||||||
|     char formated[128 * 129] = {'\0'}; |     char formated[128 * 129] = {'\0'}; | ||||||
|  |  | ||||||
|     int i = 0; |     int i = 0; | ||||||
|     for (; i <actives[id].size; i ++) { |  | ||||||
|  |     for (; i < actives[id].size; i ++) { | ||||||
|         strcat(formated, actives[id].messages[i]); |         strcat(formated, actives[id].messages[i]); | ||||||
|         strcat(formated, "\n"); |         strcat(formated, "\n"); | ||||||
|     } |     } | ||||||
| @@ -707,7 +742,7 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con | |||||||
| #endif | #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); |     tab_notify(self, flags); | ||||||
|  |  | ||||||
| @@ -719,7 +754,9 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const | |||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
|     int id; |     int id; | ||||||
|  |  | ||||||
|     for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); |     for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); | ||||||
|  |  | ||||||
|     if ( id == ACTIVE_NOTIFS_MAX ) { |     if ( id == ACTIVE_NOTIFS_MAX ) { | ||||||
|         control_unlock(); |         control_unlock(); | ||||||
|         return -1; /* Full */ |         return -1; /* Full */ | ||||||
| @@ -731,9 +768,11 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); |     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); | ||||||
|  |  | ||||||
|     if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); |     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__); |     vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||||
|     va_end (__ARGS__); |     va_end (__ARGS__); | ||||||
|  |  | ||||||
| @@ -757,7 +796,7 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const | |||||||
| #endif | #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); |     tab_notify(self, flags); | ||||||
|  |  | ||||||
| @@ -773,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__); |     vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||||
|     va_end (__ARGS__); |     va_end (__ARGS__); | ||||||
|  |  | ||||||
| @@ -786,7 +826,8 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form | |||||||
|     char formated[128 * 129] = {'\0'}; |     char formated[128 * 129] = {'\0'}; | ||||||
|  |  | ||||||
|     int i = 0; |     int i = 0; | ||||||
|     for (; i <actives[id].size; i ++) { |  | ||||||
|  |     for (; i < actives[id].size; i ++) { | ||||||
|         strcat(formated, actives[id].messages[i]); |         strcat(formated, actives[id].messages[i]); | ||||||
|         strcat(formated, "\n"); |         strcat(formated, "\n"); | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								src/notify.h
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/notify.h
									
									
									
									
									
								
							| @@ -26,8 +26,7 @@ | |||||||
| #include <inttypes.h> | #include <inttypes.h> | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| typedef enum _Notification | typedef enum _Notification { | ||||||
| { |  | ||||||
|     silent = -1, |     silent = -1, | ||||||
|     notif_error, |     notif_error, | ||||||
|     self_log_in, |     self_log_in, | ||||||
| @@ -63,18 +62,19 @@ typedef enum _Flags { | |||||||
| int init_notify(int login_cooldown, int notification_timeout); | int init_notify(int login_cooldown, int notification_timeout); | ||||||
| void terminate_notify(); | void terminate_notify(); | ||||||
|  |  | ||||||
| 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); | ||||||
| int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id); | int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id); | ||||||
|  |  | ||||||
| void stop_sound(int id); | void stop_sound(int id); | ||||||
|  |  | ||||||
| int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...); | int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title, | ||||||
| int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...); |                const char *format, ...); | ||||||
| int box_silent_notify(ToxWindow* self, 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_notify2(ToxWindow* self, 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 | #ifdef SOUND_NOTIFY | ||||||
| int set_sound(Notification sound, const char* value); | int set_sound(Notification sound, const char *value); | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
| #endif /* NOTIFY_H */ | #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__ */ | ||||||
							
								
								
									
										81
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								src/prompt.c
									
									
									
									
									
								
							| @@ -49,12 +49,13 @@ extern struct Winthread Winthread; | |||||||
|  |  | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
| FriendRequests FrndRequests; | FriendRequests FrndRequests; | ||||||
|  | #ifdef VIDEO | ||||||
| #ifdef AUDIO | #define AC_NUM_GLOB_COMMANDS 23 | ||||||
| #define AC_NUM_GLOB_COMMANDS 18 | #elif AUDIO | ||||||
|  | #define AC_NUM_GLOB_COMMANDS 21 | ||||||
| #else | #else | ||||||
| #define AC_NUM_GLOB_COMMANDS 16 | #define AC_NUM_GLOB_COMMANDS 19 | ||||||
| #endif /* AUDIO */ | #endif | ||||||
|  |  | ||||||
| /* Array of global command names used for tab completion. */ | /* Array of global command names used for tab completion. */ | ||||||
| static 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] = { | ||||||
| @@ -67,10 +68,13 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { | |||||||
|     { "/exit"       }, |     { "/exit"       }, | ||||||
|     { "/group"      }, |     { "/group"      }, | ||||||
|     { "/help"       }, |     { "/help"       }, | ||||||
|  |     { "/join"       }, | ||||||
|     { "/log"        }, |     { "/log"        }, | ||||||
|     { "/myid"       }, |     { "/myid"       }, | ||||||
|  |     { "/myqr"       }, | ||||||
|     { "/nick"       }, |     { "/nick"       }, | ||||||
|     { "/note"       }, |     { "/note"       }, | ||||||
|  |     { "/nospam"     }, | ||||||
|     { "/quit"       }, |     { "/quit"       }, | ||||||
|     { "/requests"   }, |     { "/requests"   }, | ||||||
|     { "/status"     }, |     { "/status"     }, | ||||||
| @@ -81,6 +85,14 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { | |||||||
|     { "/sdev"        }, |     { "/sdev"        }, | ||||||
|  |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |  | ||||||
|  |     { "/lsvdev"      }, | ||||||
|  |     { "/svdev"       }, | ||||||
|  |  | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void kill_prompt_window(ToxWindow *self) | void kill_prompt_window(ToxWindow *self) | ||||||
| @@ -175,16 +187,19 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|     getyx(self->window, y, x); |     getyx(self->window, y, x); | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (x2 <= 0) |     if (x2 <= 0 || y2 <= 0) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|  |     if (ctx->pastemode && key == '\r') | ||||||
|  |         key = '\n'; | ||||||
|  |  | ||||||
|     /* ignore non-menu related input if active */ |     /* ignore non-menu related input if active */ | ||||||
|     if (self->help->active) { |     if (self->help->active) { | ||||||
|         help_onKey(self, key); |         help_onKey(self, key); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ltr) {    /* char is printable */ |     if (ltr || key == '\n') {    /* char is printable */ | ||||||
|         input_new_char(self, key, x, y, x2, y2); |         input_new_char(self, key, x, y, x2, y2); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -200,11 +215,11 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|  |  | ||||||
|             if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) |             if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) | ||||||
|                 diff = dir_match(self, m, ctx->line, L"/avatar"); |                 diff = dir_match(self, m, ctx->line, L"/avatar"); | ||||||
|             else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0){ |             else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { | ||||||
|                 const char status_cmd_list[3][8] = { |                 const char status_cmd_list[3][8] = { | ||||||
|                   {"online"}, |                     {"online"}, | ||||||
|                   {"away"}, |                     {"away"}, | ||||||
|                   {"busy"}, |                     {"busy"}, | ||||||
|                 }; |                 }; | ||||||
|                 diff = complete_line(self, status_cmd_list, 3, 8); |                 diff = complete_line(self, status_cmd_list, 3, 8); | ||||||
|             } else |             } else | ||||||
| @@ -221,19 +236,21 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|         } else { |         } else { | ||||||
|             sound_notify(self, notif_error, 0, NULL); |             sound_notify(self, notif_error, 0, NULL); | ||||||
|         } |         } | ||||||
|     } else if (key == '\n') { |     } else if (key == '\r') { | ||||||
|         rm_trailing_spaces_buf(ctx); |         rm_trailing_spaces_buf(ctx); | ||||||
|  |  | ||||||
|         char line[MAX_STR_SIZE] = {0}; |         if (!wstring_is_empty(ctx->line)) { | ||||||
|  |  | ||||||
|         if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) |  | ||||||
|             memset(&line, 0, sizeof(line)); |  | ||||||
|  |  | ||||||
|         if (!string_is_empty(line)) |  | ||||||
|             add_line_to_hist(ctx); |             add_line_to_hist(ctx); | ||||||
|  |             wstrsubst(ctx->line, L'¶', L'\n'); | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); |             char line[MAX_STR_SIZE] = {0}; | ||||||
|         execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); |  | ||||||
|  |             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); |         wclear(ctx->linewin); | ||||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); |         wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||||
| @@ -246,6 +263,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | |||||||
|     int x2, y2; |     int x2, y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
| @@ -281,10 +301,12 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | |||||||
|                 status_text = "Online"; |                 status_text = "Online"; | ||||||
|                 colour = GREEN; |                 colour = GREEN; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_AWAY: |             case TOX_USER_STATUS_AWAY: | ||||||
|                 status_text = "Away"; |                 status_text = "Away"; | ||||||
|                 colour = YELLOW; |                 colour = YELLOW; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_BUSY: |             case TOX_USER_STATUS_BUSY: | ||||||
|                 status_text = "Busy"; |                 status_text = "Busy"; | ||||||
|                 colour = RED; |                 colour = RED; | ||||||
| @@ -315,7 +337,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|         pthread_mutex_lock(&Winthread.lock); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|         size_t slen = tox_self_get_status_message_size(m); |         size_t slen = tox_self_get_status_message_size(m); | ||||||
|         tox_self_get_status_message (m, (uint8_t*) statusmsg); |         tox_self_get_status_message (m, (uint8_t *) statusmsg); | ||||||
|         statusmsg[slen] = '\0'; |         statusmsg[slen] = '\0'; | ||||||
|         snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); |         snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); | ||||||
|         statusbar->statusmsg_len = strlen(statusbar->statusmsg); |         statusbar->statusmsg_len = strlen(statusbar->statusmsg); | ||||||
| @@ -349,7 +371,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | |||||||
|     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); |     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); | ||||||
|     wmove(self->window, y + 1, new_x); |     wmove(self->window, y + 1, new_x); | ||||||
|  |  | ||||||
|     wrefresh(self->window); |     wnoutrefresh(self->window); | ||||||
|  |  | ||||||
|     if (self->help->active) |     if (self->help->active) | ||||||
|         help_onDraw(self); |         help_onDraw(self); | ||||||
| @@ -369,6 +391,10 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu | |||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |     get_time_str(timefrmt, sizeof(timefrmt)); | ||||||
|     const char *msg; |     const char *msg; | ||||||
|  |  | ||||||
|  |     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) { |     if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) { | ||||||
|         msg = "has come online"; |         msg = "has come online"; | ||||||
|         line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); |         line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); | ||||||
| @@ -380,8 +406,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu | |||||||
|         else |         else | ||||||
|             box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, |             box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, | ||||||
|                        "Toxic", "%s has come online", nick ); |                        "Toxic", "%s has come online", nick ); | ||||||
|     } |     } else if (connection_status == TOX_CONNECTION_NONE) { | ||||||
|     else if (connection_status == TOX_CONNECTION_NONE) { |  | ||||||
|         msg = "has gone offline"; |         msg = "has gone offline"; | ||||||
|         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); |         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); | ||||||
|         write_to_log(msg, nick, ctx->log, true); |         write_to_log(msg, nick, ctx->log, true); | ||||||
| @@ -421,6 +446,10 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m) | |||||||
| { | { | ||||||
|     int x2, y2; |     int x2, y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) | ||||||
|  |         exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES); | ||||||
|  |  | ||||||
|     (void) y2; |     (void) y2; | ||||||
|  |  | ||||||
|     /* Init statusbar info */ |     /* Init statusbar info */ | ||||||
| @@ -478,6 +507,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m) | |||||||
|     int y2, x2; |     int y2, x2; | ||||||
|     getmaxyx(self->window, 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; |     ChatContext *ctx = self->chatwin; | ||||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); |     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); | ||||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); |     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); | ||||||
| @@ -510,6 +542,7 @@ ToxWindow new_prompt(void) | |||||||
|     ToxWindow ret; |     ToxWindow ret; | ||||||
|     memset(&ret, 0, sizeof(ret)); |     memset(&ret, 0, sizeof(ret)); | ||||||
|  |  | ||||||
|  |     ret.num = -1; | ||||||
|     ret.active = true; |     ret.active = true; | ||||||
|     ret.is_prompt = true; |     ret.is_prompt = true; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										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 */ | ||||||
							
								
								
									
										45
									
								
								src/qr_code.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/qr_code.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | /*  qr_code.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 QR_CODE | ||||||
|  | #define QR_CODE | ||||||
|  |  | ||||||
|  | #define QRCODE_FILENAME_EXT ".QRcode" | ||||||
|  |  | ||||||
|  | /* 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); | ||||||
|  |  | ||||||
|  | #ifdef QRPNG | ||||||
|  | #define QRCODE_FILENAME_EXT_PNG ".QRcode.png" | ||||||
|  | /* 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); | ||||||
|  | #endif /* QRPNG */ | ||||||
|  |  | ||||||
|  | #endif /* QR_CODE */ | ||||||
							
								
								
									
										229
									
								
								src/settings.c
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								src/settings.c
									
									
									
									
									
								
							| @@ -32,39 +32,46 @@ | |||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     #include "device.h" | #include "audio_device.h" | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
|  |  | ||||||
| #ifndef PACKAGE_DATADIR | #ifndef PACKAGE_DATADIR | ||||||
|     #define PACKAGE_DATADIR "." | #define PACKAGE_DATADIR "." | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define NO_SOUND "silent" | #define NO_SOUND "silent" | ||||||
|  |  | ||||||
| static struct ui_strings { | static struct ui_strings { | ||||||
|     const char* self; |     const char *self; | ||||||
|     const char* timestamps; |     const char *timestamps; | ||||||
|     const char* time_format; |     const char *time_format; | ||||||
|     const char* timestamp_format; |     const char *timestamp_format; | ||||||
|     const char* log_timestamp_format; |     const char *log_timestamp_format; | ||||||
|     const char* alerts; |     const char *alerts; | ||||||
|     const char* native_colors; |     const char *bell_on_message; | ||||||
|     const char* autolog; |     const char *bell_on_filetrans; | ||||||
|     const char* history_size; |     const char *bell_on_filetrans_accept; | ||||||
|     const char* show_typing_self; |     const char *bell_on_invite; | ||||||
|     const char* show_typing_other; |     const char *native_colors; | ||||||
|     const char* show_welcome_msg; |     const char *autolog; | ||||||
|  |     const char *history_size; | ||||||
|  |     const char *show_typing_self; | ||||||
|  |     const char *show_typing_other; | ||||||
|  |     const char *show_welcome_msg; | ||||||
|  |     const char *show_connection_msg; | ||||||
|  |     const char *nodeslist_update_freq; | ||||||
|  |  | ||||||
|     const char* line_join; |     const char *line_join; | ||||||
|     const char* line_quit; |     const char *line_quit; | ||||||
|     const char* line_alert; |     const char *line_alert; | ||||||
|     const char* line_normal; |     const char *line_normal; | ||||||
|  |     const char *line_special; | ||||||
|  |  | ||||||
|     const char* mplex_away; |     const char *mplex_away; | ||||||
|     const char* mplex_away_note; |     const char *mplex_away_note; | ||||||
| } ui_strings = { | } ui_strings = { | ||||||
|     "ui", |     "ui", | ||||||
|     "timestamps", |     "timestamps", | ||||||
| @@ -72,21 +79,28 @@ static struct ui_strings { | |||||||
|     "timestamp_format", |     "timestamp_format", | ||||||
|     "log_timestamp_format", |     "log_timestamp_format", | ||||||
|     "alerts", |     "alerts", | ||||||
|  |     "bell_on_message", | ||||||
|  |     "bell_on_filetrans", | ||||||
|  |     "bell_on_filetrans_accept", | ||||||
|  |     "bell_on_invite", | ||||||
|     "native_colors", |     "native_colors", | ||||||
|     "autolog", |     "autolog", | ||||||
|     "history_size", |     "history_size", | ||||||
|     "show_typing_self", |     "show_typing_self", | ||||||
|     "show_typing_other", |     "show_typing_other", | ||||||
|     "show_welcome_msg", |     "show_welcome_msg", | ||||||
|  |     "show_connection_msg", | ||||||
|  |     "nodeslist_update_freq", | ||||||
|     "line_join", |     "line_join", | ||||||
|     "line_quit", |     "line_quit", | ||||||
|     "line_alert", |     "line_alert", | ||||||
|     "line_normal", |     "line_normal", | ||||||
|  |     "line_special", | ||||||
|     "mplex_away", |     "mplex_away", | ||||||
|     "mplex_away_note", |     "mplex_away_note", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void ui_defaults(struct user_settings* settings) | static void ui_defaults(struct user_settings *settings) | ||||||
| { | { | ||||||
|     settings->timestamps = TIMESTAMPS_ON; |     settings->timestamps = TIMESTAMPS_ON; | ||||||
|     snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT); |     snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT); | ||||||
| @@ -94,16 +108,23 @@ static void ui_defaults(struct user_settings* settings) | |||||||
|  |  | ||||||
|     settings->autolog = AUTOLOG_OFF; |     settings->autolog = AUTOLOG_OFF; | ||||||
|     settings->alerts = ALERTS_ENABLED; |     settings->alerts = ALERTS_ENABLED; | ||||||
|  |     settings->bell_on_message = 0; | ||||||
|  |     settings->bell_on_filetrans = 0; | ||||||
|  |     settings->bell_on_filetrans_accept = 0; | ||||||
|  |     settings->bell_on_invite = 0; | ||||||
|     settings->colour_theme = DFLT_COLS; |     settings->colour_theme = DFLT_COLS; | ||||||
|     settings->history_size = 700; |     settings->history_size = 700; | ||||||
|     settings->show_typing_self = SHOW_TYPING_ON; |     settings->show_typing_self = SHOW_TYPING_ON; | ||||||
|     settings->show_typing_other = SHOW_TYPING_ON; |     settings->show_typing_other = SHOW_TYPING_ON; | ||||||
|     settings->show_welcome_msg = SHOW_WELCOME_MSG_ON; |     settings->show_welcome_msg = SHOW_WELCOME_MSG_ON; | ||||||
|  |     settings->show_connection_msg = SHOW_CONNECTION_MSG_ON; | ||||||
|  |     settings->nodeslist_update_freq = 7; | ||||||
|  |  | ||||||
|     snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN); |     snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN); | ||||||
|     snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); |     snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); | ||||||
|     snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT); |     snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT); | ||||||
|     snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL); |     snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL); | ||||||
|  |     snprintf(settings->line_special, LINE_HINT_MAX + 1, "%s", LINE_SPECIAL); | ||||||
|  |  | ||||||
|     settings->mplex_away = MPLEX_ON; |     settings->mplex_away = MPLEX_ON; | ||||||
|     snprintf (settings->mplex_away_note, |     snprintf (settings->mplex_away_note, | ||||||
| @@ -113,17 +134,18 @@ static void ui_defaults(struct user_settings* settings) | |||||||
| } | } | ||||||
|  |  | ||||||
| static const struct keys_strings { | static const struct keys_strings { | ||||||
|     const char* self; |     const char *self; | ||||||
|     const char* next_tab; |     const char *next_tab; | ||||||
|     const char* prev_tab; |     const char *prev_tab; | ||||||
|     const char* scroll_line_up; |     const char *scroll_line_up; | ||||||
|     const char* scroll_line_down; |     const char *scroll_line_down; | ||||||
|     const char* half_page_up; |     const char *half_page_up; | ||||||
|     const char* half_page_down; |     const char *half_page_down; | ||||||
|     const char* page_bottom; |     const char *page_bottom; | ||||||
|     const char* peer_list_up; |     const char *peer_list_up; | ||||||
|     const char* peer_list_down; |     const char *peer_list_down; | ||||||
|     const char* toggle_peerlist; |     const char *toggle_peerlist; | ||||||
|  |     const char *toggle_pastemode; | ||||||
| } key_strings = { | } key_strings = { | ||||||
|     "keys", |     "keys", | ||||||
|     "next_tab", |     "next_tab", | ||||||
| @@ -136,10 +158,11 @@ static const struct keys_strings { | |||||||
|     "peer_list_up", |     "peer_list_up", | ||||||
|     "peer_list_down", |     "peer_list_down", | ||||||
|     "toggle_peerlist", |     "toggle_peerlist", | ||||||
|  |     "toggle_paste_mode", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* defines from toxic.h */ | /* defines from toxic.h */ | ||||||
| static void key_defaults(struct user_settings* settings) | static void key_defaults(struct user_settings *settings) | ||||||
| { | { | ||||||
|     settings->key_next_tab = T_KEY_NEXT; |     settings->key_next_tab = T_KEY_NEXT; | ||||||
|     settings->key_prev_tab = T_KEY_PREV; |     settings->key_prev_tab = T_KEY_PREV; | ||||||
| @@ -151,33 +174,37 @@ static void key_defaults(struct user_settings* settings) | |||||||
|     settings->key_peer_list_up = T_KEY_C_LB; |     settings->key_peer_list_up = T_KEY_C_LB; | ||||||
|     settings->key_peer_list_down = T_KEY_C_RB; |     settings->key_peer_list_down = T_KEY_C_RB; | ||||||
|     settings->key_toggle_peerlist = T_KEY_C_B; |     settings->key_toggle_peerlist = T_KEY_C_B; | ||||||
|  |     settings->key_toggle_pastemode = T_KEY_C_T; | ||||||
| } | } | ||||||
|  |  | ||||||
| static const struct tox_strings { | static const struct tox_strings { | ||||||
|     const char* self; |     const char *self; | ||||||
|     const char* download_path; |     const char *download_path; | ||||||
|     const char* chatlogs_path; |     const char *chatlogs_path; | ||||||
|     const char* avatar_path; |     const char *avatar_path; | ||||||
|  |     const char *password_eval; | ||||||
| } tox_strings = { | } tox_strings = { | ||||||
|     "tox", |     "tox", | ||||||
|     "download_path", |     "download_path", | ||||||
|     "chatlogs_path", |     "chatlogs_path", | ||||||
|     "avatar_path", |     "avatar_path", | ||||||
|  |     "password_eval", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void tox_defaults(struct user_settings* settings) | static void tox_defaults(struct user_settings *settings) | ||||||
| { | { | ||||||
|     strcpy(settings->download_path, ""); |     strcpy(settings->download_path, ""); | ||||||
|     strcpy(settings->chatlogs_path, ""); |     strcpy(settings->chatlogs_path, ""); | ||||||
|     strcpy(settings->avatar_path, ""); |     strcpy(settings->avatar_path, ""); | ||||||
|  |     strcpy(settings->password_eval, ""); | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| static const struct audio_strings { | static const struct audio_strings { | ||||||
|     const char* self; |     const char *self; | ||||||
|     const char* input_device; |     const char *input_device; | ||||||
|     const char* output_device; |     const char *output_device; | ||||||
|     const char* VAD_treshold; |     const char *VAD_treshold; | ||||||
| } audio_strings = { | } audio_strings = { | ||||||
|     "audio", |     "audio", | ||||||
|     "input_device", |     "input_device", | ||||||
| @@ -185,7 +212,7 @@ static const struct audio_strings { | |||||||
|     "VAD_treshold", |     "VAD_treshold", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void audio_defaults(struct user_settings* settings) | static void audio_defaults(struct user_settings *settings) | ||||||
| { | { | ||||||
|     settings->audio_in_dev = 0; |     settings->audio_in_dev = 0; | ||||||
|     settings->audio_out_dev = 0; |     settings->audio_out_dev = 0; | ||||||
| @@ -195,17 +222,17 @@ static void audio_defaults(struct user_settings* settings) | |||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
| static const struct sound_strings { | static const struct sound_strings { | ||||||
|     const char* self; |     const char *self; | ||||||
|     const char* notif_error; |     const char *notif_error; | ||||||
|     const char* self_log_in; |     const char *self_log_in; | ||||||
|     const char* self_log_out; |     const char *self_log_out; | ||||||
|     const char* user_log_in; |     const char *user_log_in; | ||||||
|     const char* user_log_out; |     const char *user_log_out; | ||||||
|     const char* call_incoming; |     const char *call_incoming; | ||||||
|     const char* call_outgoing; |     const char *call_outgoing; | ||||||
|     const char* generic_message; |     const char *generic_message; | ||||||
|     const char* transfer_pending; |     const char *transfer_pending; | ||||||
|     const char* transfer_completed; |     const char *transfer_completed; | ||||||
| } sound_strings = { | } sound_strings = { | ||||||
|     "sounds", |     "sounds", | ||||||
|     "notif_error", |     "notif_error", | ||||||
| @@ -221,11 +248,12 @@ static const struct sound_strings { | |||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int key_parse(const char** bind){ | static int key_parse(const char **bind) | ||||||
|  | { | ||||||
|     int len = strlen(*bind); |     int len = strlen(*bind); | ||||||
|  |  | ||||||
|     if (len > 5) { |     if (len > 5) { | ||||||
|         if(strncasecmp(*bind, "ctrl+", 5) == 0) |         if (strncasecmp(*bind, "ctrl+", 5) == 0 && toupper(bind[0][5]) != 'M')  /* ctrl+m cannot be used */ | ||||||
|             return toupper(bind[0][5]) - 'A' + 1; |             return toupper(bind[0][5]) - 'A' + 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -238,6 +266,15 @@ static int key_parse(const char** bind){ | |||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void set_key_binding(int *key, const char **bind) | ||||||
|  | { | ||||||
|  |     int code = key_parse(bind); | ||||||
|  |  | ||||||
|  |     if (code != -1) { | ||||||
|  |         *key = code; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| int settings_load(struct user_settings *s, const char *patharg) | int settings_load(struct user_settings *s, const char *patharg) | ||||||
| { | { | ||||||
|     config_t cfg[1]; |     config_t cfg[1]; | ||||||
| @@ -286,6 +323,7 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|         config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps); |         config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps); | ||||||
|  |  | ||||||
|         int time = 24; |         int time = 24; | ||||||
|  |  | ||||||
|         if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) { |         if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) { | ||||||
|             if (time == 12) { |             if (time == 12) { | ||||||
|                 snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p"); |                 snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p"); | ||||||
| @@ -302,26 +340,53 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts); |         config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts); | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_bool(setting, ui_strings.bell_on_message, &s->bell_on_message)) { | ||||||
|  |             s->bell_on_message = s->bell_on_message ? NT_BEEP : 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans, &s->bell_on_filetrans)) { | ||||||
|  |             s->bell_on_filetrans = s->bell_on_filetrans ? NT_BEEP : 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans_accept, &s->bell_on_filetrans_accept)) { | ||||||
|  |             s->bell_on_filetrans_accept = s->bell_on_filetrans_accept ? NT_BEEP : 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_bool(setting, ui_strings.bell_on_invite, &s->bell_on_invite)) { | ||||||
|  |             s->bell_on_invite = s->bell_on_invite ? NT_BEEP : 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog); |         config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog); | ||||||
|         config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme); |         config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme); | ||||||
|         config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size); |  | ||||||
|         config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self); |         config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self); | ||||||
|         config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other); |         config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other); | ||||||
|         config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg); |         config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg); | ||||||
|  |         config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg); | ||||||
|  |  | ||||||
|  |         config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size); | ||||||
|  |         config_setting_lookup_int(setting, ui_strings.nodeslist_update_freq, &s->nodeslist_update_freq); | ||||||
|  |  | ||||||
|         if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) { |         if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) { | ||||||
|             snprintf(s->line_join, sizeof(s->line_join), "%s", str); |             snprintf(s->line_join, sizeof(s->line_join), "%s", str); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ( config_setting_lookup_string(setting, ui_strings.line_quit, &str) ) { |         if ( config_setting_lookup_string(setting, ui_strings.line_quit, &str) ) { | ||||||
|             snprintf(s->line_quit, sizeof(s->line_quit), "%s", str); |             snprintf(s->line_quit, sizeof(s->line_quit), "%s", str); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ( config_setting_lookup_string(setting, ui_strings.line_alert, &str) ) { |         if ( config_setting_lookup_string(setting, ui_strings.line_alert, &str) ) { | ||||||
|             snprintf(s->line_alert, sizeof(s->line_alert), "%s", str); |             snprintf(s->line_alert, sizeof(s->line_alert), "%s", str); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ( config_setting_lookup_string(setting, ui_strings.line_normal, &str) ) { |         if ( config_setting_lookup_string(setting, ui_strings.line_normal, &str) ) { | ||||||
|             snprintf(s->line_normal, sizeof(s->line_normal), "%s", str); |             snprintf(s->line_normal, sizeof(s->line_normal), "%s", str); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if ( config_setting_lookup_string(setting, ui_strings.line_special, &str) ) { | ||||||
|  |             snprintf(s->line_special, sizeof(s->line_special), "%s", str); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away); |         config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) { |         if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) { | ||||||
| @@ -359,34 +424,56 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|             if (len >= sizeof(s->avatar_path)) |             if (len >= sizeof(s->avatar_path)) | ||||||
|                 s->avatar_path[0] = '\0'; |                 s->avatar_path[0] = '\0'; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if ( config_setting_lookup_string(setting, tox_strings.password_eval, &str) ) { | ||||||
|  |             snprintf(s->password_eval, sizeof(s->password_eval), "%s", str); | ||||||
|  |             int len = strlen(str); | ||||||
|  |  | ||||||
|  |             if (len >= sizeof(s->password_eval)) | ||||||
|  |                 s->password_eval[0] = '\0'; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* keys */ |     /* keys */ | ||||||
|     if ((setting = config_lookup(cfg, key_strings.self)) != NULL) { |     if ((setting = config_lookup(cfg, key_strings.self)) != NULL) { | ||||||
|         const char* tmp = NULL; |         const char *tmp = NULL; | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) | ||||||
|             s->key_next_tab = key_parse(&tmp); |             set_key_binding(&s->key_next_tab, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) | ||||||
|             s->key_prev_tab = key_parse(&tmp); |             set_key_binding(&s->key_prev_tab, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) | ||||||
|             s->key_scroll_line_up = key_parse(&tmp); |             set_key_binding(&s->key_scroll_line_up, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) | ||||||
|             s->key_scroll_line_down= key_parse(&tmp); |             set_key_binding(&s->key_scroll_line_down, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) | ||||||
|             s->key_half_page_up = key_parse(&tmp); |             set_key_binding(&s->key_half_page_up, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) | ||||||
|             s->key_half_page_down = key_parse(&tmp); |             set_key_binding(&s->key_half_page_down, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) | ||||||
|             s->key_page_bottom = key_parse(&tmp); |             set_key_binding(&s->key_page_bottom, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) | ||||||
|             s->key_peer_list_up = key_parse(&tmp); |             set_key_binding(&s->key_peer_list_up, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) | ||||||
|             s->key_peer_list_down = key_parse(&tmp); |             set_key_binding(&s->key_peer_list_down, &tmp); | ||||||
|  |  | ||||||
|         if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp)) |         if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp)) | ||||||
|             s->key_toggle_peerlist = key_parse(&tmp); |             set_key_binding(&s->key_toggle_peerlist, &tmp); | ||||||
|  |  | ||||||
|  |         if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp)) | ||||||
|  |             set_key_binding(&s->key_toggle_pastemode, &tmp); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|     if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) { |     if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) { | ||||||
|         config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev); |         config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev); | ||||||
|         s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev; |         s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev; | ||||||
| @@ -396,9 +483,11 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|  |  | ||||||
|         config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold); |         config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|  |  | ||||||
|     if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) { |     if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) { | ||||||
|         if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) || |         if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) || | ||||||
|                 !set_sound(notif_error, str) ) { |                 !set_sound(notif_error, str) ) { | ||||||
| @@ -447,8 +536,7 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) |             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||||
|                 set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav"); |                 set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav"); | ||||||
|         } |         } | ||||||
|     } |     } else { | ||||||
|     else { |  | ||||||
|         set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav"); |         set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav"); | ||||||
|         set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav"); |         set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav"); | ||||||
|         set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav"); |         set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav"); | ||||||
| @@ -458,6 +546,7 @@ int settings_load(struct user_settings *s, const char *patharg) | |||||||
|         set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav"); |         set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav"); | ||||||
|         set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav"); |         set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     config_destroy(cfg); |     config_destroy(cfg); | ||||||
|   | |||||||
| @@ -30,11 +30,19 @@ | |||||||
| /* Represents line_* hints max strlen */ | /* Represents line_* hints max strlen */ | ||||||
| #define LINE_HINT_MAX 3 | #define LINE_HINT_MAX 3 | ||||||
|  |  | ||||||
|  | #define PASSWORD_EVAL_MAX 512 | ||||||
|  |  | ||||||
| /* holds user setting values */ | /* holds user setting values */ | ||||||
| struct user_settings { | struct user_settings { | ||||||
|     int autolog;           /* boolean */ |     int autolog;           /* boolean */ | ||||||
|     int alerts;            /* boolean */ |     int alerts;            /* boolean */ | ||||||
|  |  | ||||||
|  |     /* boolean (is set to NT_BEEP or 0 after loading) */ | ||||||
|  |     int bell_on_message; | ||||||
|  |     int bell_on_filetrans; | ||||||
|  |     int bell_on_filetrans_accept; | ||||||
|  |     int bell_on_invite; | ||||||
|  |  | ||||||
|     int timestamps;        /* boolean */ |     int timestamps;        /* boolean */ | ||||||
|     char timestamp_format[TIME_STR_SIZE]; |     char timestamp_format[TIME_STR_SIZE]; | ||||||
|     char log_timestamp_format[TIME_STR_SIZE]; |     char log_timestamp_format[TIME_STR_SIZE]; | ||||||
| @@ -44,15 +52,19 @@ struct user_settings { | |||||||
|     int show_typing_self;  /* boolean */ |     int show_typing_self;  /* boolean */ | ||||||
|     int show_typing_other; /* boolean */ |     int show_typing_other; /* boolean */ | ||||||
|     int show_welcome_msg;  /* boolean */ |     int show_welcome_msg;  /* boolean */ | ||||||
|  |     int show_connection_msg;  /* boolean */ | ||||||
|  |     int nodeslist_update_freq;  /* int (<= 0 to disable updates) */ | ||||||
|  |  | ||||||
|     char line_join[LINE_HINT_MAX + 1]; |     char line_join[LINE_HINT_MAX + 1]; | ||||||
|     char line_quit[LINE_HINT_MAX + 1]; |     char line_quit[LINE_HINT_MAX + 1]; | ||||||
|     char line_alert[LINE_HINT_MAX + 1]; |     char line_alert[LINE_HINT_MAX + 1]; | ||||||
|     char line_normal[LINE_HINT_MAX + 1]; |     char line_normal[LINE_HINT_MAX + 1]; | ||||||
|  |     char line_special[LINE_HINT_MAX + 1]; | ||||||
|  |  | ||||||
|     char download_path[PATH_MAX]; |     char download_path[PATH_MAX]; | ||||||
|     char chatlogs_path[PATH_MAX]; |     char chatlogs_path[PATH_MAX]; | ||||||
|     char avatar_path[PATH_MAX]; |     char avatar_path[PATH_MAX]; | ||||||
|  |     char password_eval[PASSWORD_EVAL_MAX]; | ||||||
|  |  | ||||||
|     int key_next_tab; |     int key_next_tab; | ||||||
|     int key_prev_tab; |     int key_prev_tab; | ||||||
| @@ -64,6 +76,7 @@ struct user_settings { | |||||||
|     int key_peer_list_up; |     int key_peer_list_up; | ||||||
|     int key_peer_list_down; |     int key_peer_list_down; | ||||||
|     int key_toggle_peerlist; |     int key_toggle_peerlist; | ||||||
|  |     int key_toggle_pastemode; | ||||||
|  |  | ||||||
|     int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */ |     int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */ | ||||||
|     char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH]; |     char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH]; | ||||||
| @@ -94,6 +107,9 @@ enum { | |||||||
|     SHOW_WELCOME_MSG_OFF = 0, |     SHOW_WELCOME_MSG_OFF = 0, | ||||||
|     SHOW_WELCOME_MSG_ON = 1, |     SHOW_WELCOME_MSG_ON = 1, | ||||||
|  |  | ||||||
|  |     SHOW_CONNECTION_MSG_OFF = 0, | ||||||
|  |     SHOW_CONNECTION_MSG_ON = 1, | ||||||
|  |  | ||||||
|     DFLT_HST_SIZE = 700, |     DFLT_HST_SIZE = 700, | ||||||
|  |  | ||||||
|     MPLEX_OFF = 0, |     MPLEX_OFF = 0, | ||||||
| @@ -104,6 +120,7 @@ enum { | |||||||
| #define LINE_QUIT    "<--" | #define LINE_QUIT    "<--" | ||||||
| #define LINE_ALERT   "-!-" | #define LINE_ALERT   "-!-" | ||||||
| #define LINE_NORMAL  "---" | #define LINE_NORMAL  "---" | ||||||
|  | #define LINE_SPECIAL ">>>" | ||||||
| #define TIMESTAMP_DEFAULT      "%H:%M:%S" | #define TIMESTAMP_DEFAULT      "%H:%M:%S" | ||||||
| #define LOG_TIMESTAMP_DEFAULT  "%Y/%m/%d [%H:%M:%S]" | #define LOG_TIMESTAMP_DEFAULT  "%Y/%m/%d [%H:%M:%S]" | ||||||
| #define MPLEX_AWAY_NOTE "Detached from screen" | #define MPLEX_AWAY_NOTE "Detached from screen" | ||||||
|   | |||||||
| @@ -52,8 +52,7 @@ extern struct Winthread Winthread; | |||||||
| #define PATH_SEP_S "/" | #define PATH_SEP_S "/" | ||||||
| #define PATH_SEP_C '/' | #define PATH_SEP_C '/' | ||||||
|  |  | ||||||
| typedef enum | typedef enum { | ||||||
| { |  | ||||||
|     MPLEX_NONE, |     MPLEX_NONE, | ||||||
|     MPLEX_SCREEN, |     MPLEX_SCREEN, | ||||||
|     MPLEX_TMUX, |     MPLEX_TMUX, | ||||||
| @@ -97,13 +96,14 @@ static char *read_into_dyn_buffer (FILE *stream) | |||||||
|     char *dyn_buffer = NULL; |     char *dyn_buffer = NULL; | ||||||
|     int dyn_buffer_size = 1; /* account for the \0 */ |     int dyn_buffer_size = 1; /* account for the \0 */ | ||||||
|  |  | ||||||
|     while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) |     while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) { | ||||||
|     { |  | ||||||
|         int length = dyn_buffer_size + strlen (input_ptr); |         int length = dyn_buffer_size + strlen (input_ptr); | ||||||
|  |  | ||||||
|         if (dyn_buffer) |         if (dyn_buffer) | ||||||
|             dyn_buffer = (char*) realloc (dyn_buffer, length); |             dyn_buffer = (char *) realloc (dyn_buffer, length); | ||||||
|         else |         else | ||||||
|             dyn_buffer = (char*) malloc (length); |             dyn_buffer = (char *) malloc (length); | ||||||
|  |  | ||||||
|         strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr); |         strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr); | ||||||
|         dyn_buffer_size = length; |         dyn_buffer_size = length; | ||||||
|     } |     } | ||||||
| @@ -116,26 +116,29 @@ static char *extract_socket_path (const char *info) | |||||||
|     const char *search_str = " Socket"; |     const char *search_str = " Socket"; | ||||||
|     const char *pos = strstr (info, search_str); |     const char *pos = strstr (info, search_str); | ||||||
|     char *end = NULL; |     char *end = NULL; | ||||||
|     char* path = NULL; |     char *path = NULL; | ||||||
|  |  | ||||||
|     if (!pos) |     if (!pos) | ||||||
|         return NULL; |         return NULL; | ||||||
|  |  | ||||||
|     pos += strlen (search_str); |     pos += strlen (search_str); | ||||||
|     pos = strchr (pos, PATH_SEP_C); |     pos = strchr (pos, PATH_SEP_C); | ||||||
|  |  | ||||||
|     if (!pos) |     if (!pos) | ||||||
|         return NULL; |         return NULL; | ||||||
|  |  | ||||||
|     end = strchr (pos, '\n'); |     end = strchr (pos, '\n'); | ||||||
|  |  | ||||||
|     if (!end) |     if (!end) | ||||||
|         return NULL; |         return NULL; | ||||||
|  |  | ||||||
|     *end = '\0'; |     *end = '\0'; | ||||||
|     end = strrchr (pos, '.'); |     end = strrchr (pos, '.'); | ||||||
|  |  | ||||||
|     if (!end) |     if (!end) | ||||||
|         return NULL; |         return NULL; | ||||||
|  |  | ||||||
|     path = (char*) malloc (end - pos + 1); |     path = (char *) malloc (end - pos + 1); | ||||||
|     *end = '\0'; |     *end = '\0'; | ||||||
|     return strcpy (path, pos); |     return strcpy (path, pos); | ||||||
| } | } | ||||||
| @@ -147,14 +150,17 @@ static int detect_gnu_screen () | |||||||
|     char *dyn_buffer = NULL; |     char *dyn_buffer = NULL; | ||||||
|  |  | ||||||
|     socket_name = getenv ("STY"); |     socket_name = getenv ("STY"); | ||||||
|  |  | ||||||
|     if (!socket_name) |     if (!socket_name) | ||||||
|         goto nomplex; |         goto nomplex; | ||||||
|  |  | ||||||
|     session_info_stream = popen ("env LC_ALL=C screen -ls", "r"); |     session_info_stream = popen ("env LC_ALL=C screen -ls", "r"); | ||||||
|  |  | ||||||
|     if (!session_info_stream) |     if (!session_info_stream) | ||||||
|         goto nomplex; |         goto nomplex; | ||||||
|  |  | ||||||
|     dyn_buffer = read_into_dyn_buffer (session_info_stream); |     dyn_buffer = read_into_dyn_buffer (session_info_stream); | ||||||
|  |  | ||||||
|     if (!dyn_buffer) |     if (!dyn_buffer) | ||||||
|         goto nomplex; |         goto nomplex; | ||||||
|  |  | ||||||
| @@ -162,6 +168,7 @@ static int detect_gnu_screen () | |||||||
|     session_info_stream = NULL; |     session_info_stream = NULL; | ||||||
|  |  | ||||||
|     socket_path = extract_socket_path (dyn_buffer); |     socket_path = extract_socket_path (dyn_buffer); | ||||||
|  |  | ||||||
|     if (!socket_path) |     if (!socket_path) | ||||||
|         goto nomplex; |         goto nomplex; | ||||||
|  |  | ||||||
| @@ -181,23 +188,29 @@ static int detect_gnu_screen () | |||||||
|     return 1; |     return 1; | ||||||
|  |  | ||||||
| nomplex: | nomplex: | ||||||
|  |  | ||||||
|     if (session_info_stream) |     if (session_info_stream) | ||||||
|         pclose (session_info_stream); |         pclose (session_info_stream); | ||||||
|  |  | ||||||
|     if (dyn_buffer) |     if (dyn_buffer) | ||||||
|         free (dyn_buffer); |         free (dyn_buffer); | ||||||
|  |  | ||||||
|     if (socket_path) |     if (socket_path) | ||||||
|         free(socket_path); |         free(socket_path); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int detect_tmux () | static int detect_tmux () | ||||||
| { | { | ||||||
|     char *tmux_env = getenv ("TMUX"), *pos; |     char *tmux_env = getenv ("TMUX"), *pos; | ||||||
|  |  | ||||||
|     if (!tmux_env) |     if (!tmux_env) | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
|     /* find second separator */ |     /* find second separator */ | ||||||
|     pos = strrchr (tmux_env, ','); |     pos = strrchr (tmux_env, ','); | ||||||
|  |  | ||||||
|     if (!pos) |     if (!pos) | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
| @@ -230,6 +243,7 @@ static int gnu_screen_is_detached () | |||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
|     struct stat sb; |     struct stat sb; | ||||||
|  |  | ||||||
|     if (stat (mplex_data, &sb) != 0) |     if (stat (mplex_data, &sb) != 0) | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
| @@ -257,10 +271,12 @@ static int tmux_is_detached () | |||||||
|     const int numstr_len = strlen (mplex_data); |     const int numstr_len = strlen (mplex_data); | ||||||
|  |  | ||||||
|     session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r"); |     session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r"); | ||||||
|  |  | ||||||
|     if (!session_info_stream) |     if (!session_info_stream) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
|     dyn_buffer = read_into_dyn_buffer (session_info_stream); |     dyn_buffer = read_into_dyn_buffer (session_info_stream); | ||||||
|  |  | ||||||
|     if (!dyn_buffer) |     if (!dyn_buffer) | ||||||
|         goto fail; |         goto fail; | ||||||
|  |  | ||||||
| @@ -268,7 +284,7 @@ static int tmux_is_detached () | |||||||
|     session_info_stream = NULL; |     session_info_stream = NULL; | ||||||
|  |  | ||||||
|     /* prepare search string, for finding the current session's entry */ |     /* prepare search string, for finding the current session's entry */ | ||||||
|     search_str = (char*) malloc (numstr_len + 4); |     search_str = (char *) malloc (numstr_len + 4); | ||||||
|     search_str[0] = '\n'; |     search_str[0] = '\n'; | ||||||
|     strcpy (search_str + 1, mplex_data); |     strcpy (search_str + 1, mplex_data); | ||||||
|     strcat (search_str, ": "); |     strcat (search_str, ": "); | ||||||
| @@ -295,12 +311,16 @@ static int tmux_is_detached () | |||||||
|     return attached_pos == NULL  ||  attached_pos > nl_pos; |     return attached_pos == NULL  ||  attached_pos > nl_pos; | ||||||
|  |  | ||||||
| fail: | fail: | ||||||
|  |  | ||||||
|     if (session_info_stream) |     if (session_info_stream) | ||||||
|         pclose (session_info_stream); |         pclose (session_info_stream); | ||||||
|  |  | ||||||
|     if (dyn_buffer) |     if (dyn_buffer) | ||||||
|         free (dyn_buffer); |         free (dyn_buffer); | ||||||
|  |  | ||||||
|     if (search_str) |     if (search_str) | ||||||
|         free (search_str); |         free (search_str); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -332,26 +352,21 @@ static void mplex_timer_handler (Tox *m) | |||||||
|     current_status = tox_self_get_status (m); |     current_status = tox_self_get_status (m); | ||||||
|     pthread_mutex_unlock (&Winthread.lock); |     pthread_mutex_unlock (&Winthread.lock); | ||||||
|  |  | ||||||
|     if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) |     if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) { | ||||||
|     { |  | ||||||
|         auto_away_active = false; |         auto_away_active = false; | ||||||
|         new_status = prev_status; |         new_status = prev_status; | ||||||
|         new_note = prev_note; |         new_note = prev_note; | ||||||
|     } |     } else if (current_status == TOX_USER_STATUS_NONE && detached) { | ||||||
|     else |  | ||||||
|     if (current_status == TOX_USER_STATUS_NONE && detached) |  | ||||||
|     { |  | ||||||
|         auto_away_active = true; |         auto_away_active = true; | ||||||
|         prev_status = current_status; |         prev_status = current_status; | ||||||
|         new_status = TOX_USER_STATUS_AWAY; |         new_status = TOX_USER_STATUS_AWAY; | ||||||
|         pthread_mutex_lock (&Winthread.lock); |         pthread_mutex_lock (&Winthread.lock); | ||||||
|         size_t slen = tox_self_get_status_message_size(m); |         size_t slen = tox_self_get_status_message_size(m); | ||||||
|         tox_self_get_status_message (m, (uint8_t*) prev_note); |         tox_self_get_status_message (m, (uint8_t *) prev_note); | ||||||
|         prev_note[slen] = '\0'; |         prev_note[slen] = '\0'; | ||||||
|         pthread_mutex_unlock (&Winthread.lock); |         pthread_mutex_unlock (&Winthread.lock); | ||||||
|         new_note = user_settings->mplex_away_note; |         new_note = user_settings->mplex_away_note; | ||||||
|     } |     } else | ||||||
|     else |  | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     char argv[3][MAX_STR_SIZE]; |     char argv[3][MAX_STR_SIZE]; | ||||||
|   | |||||||
							
								
								
									
										545
									
								
								src/toxic.c
									
									
									
									
									
								
							
							
						
						
									
										545
									
								
								src/toxic.c
									
									
									
									
									
								
							| @@ -39,7 +39,9 @@ | |||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
| #include <termios.h> | #include <termios.h> | ||||||
|  | #include <ctype.h> | ||||||
|  |  | ||||||
|  | #include <curl/curl.h> | ||||||
| #include <tox/tox.h> | #include <tox/tox.h> | ||||||
| #include <tox/toxencryptsave.h> | #include <tox/toxencryptsave.h> | ||||||
|  |  | ||||||
| @@ -47,6 +49,7 @@ | |||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "friendlist.h" | #include "friendlist.h" | ||||||
|  | #include "groupchat.h" | ||||||
| #include "prompt.h" | #include "prompt.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "file_transfers.h" | #include "file_transfers.h" | ||||||
| @@ -54,22 +57,27 @@ | |||||||
| #include "settings.h" | #include "settings.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
| #include "device.h" | #include "audio_device.h" | ||||||
| #include "message_queue.h" | #include "message_queue.h" | ||||||
| #include "execute.h" | #include "execute.h" | ||||||
| #include "term_mplex.h" | #include "term_mplex.h" | ||||||
|  | #include "name_lookup.h" | ||||||
|  | #include "bootstrap.h" | ||||||
|  |  | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
|     #include "xtra.h" | #include "xtra.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| #include "audio_call.h" | #include "audio_call.h" | ||||||
| ToxAv *av; | #ifdef VIDEO | ||||||
|  | #include "video_call.h" | ||||||
|  | #endif /* VIDEO */ | ||||||
|  | ToxAV *av; | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #ifndef PACKAGE_DATADIR | #ifndef PACKAGE_DATADIR | ||||||
|     #define PACKAGE_DATADIR "." | #define PACKAGE_DATADIR "." | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| /* Export for use in Callbacks */ | /* Export for use in Callbacks */ | ||||||
| @@ -77,16 +85,16 @@ char *DATA_FILE = NULL; | |||||||
| char *BLOCK_FILE = NULL; | char *BLOCK_FILE = NULL; | ||||||
| ToxWindow *prompt = NULL; | ToxWindow *prompt = NULL; | ||||||
|  |  | ||||||
| #define DATANAME  "data" | #define DATANAME  "toxic_profile.tox" | ||||||
| #define BLOCKNAME "data-blocklist" | #define BLOCKNAME "toxic_blocklist" | ||||||
|  |  | ||||||
| #define AUTOSAVE_FREQ 60 | #define AUTOSAVE_FREQ 600 | ||||||
| #define MIN_PASSWORD_LEN 6 | #define MIN_PASSWORD_LEN 6 | ||||||
| #define MAX_PASSWORD_LEN 64 | #define MAX_PASSWORD_LEN 64 | ||||||
|  |  | ||||||
| struct Winthread Winthread; | struct Winthread Winthread; | ||||||
| struct cqueue_thread cqueue_thread; | struct cqueue_thread cqueue_thread; | ||||||
| struct audio_thread audio_thread; | struct av_thread av_thread; | ||||||
| struct arg_opts arg_opts; | struct arg_opts arg_opts; | ||||||
| struct user_settings *user_settings = NULL; | struct user_settings *user_settings = NULL; | ||||||
|  |  | ||||||
| @@ -149,12 +157,16 @@ void exit_toxic_success(Tox *m) | |||||||
|     terminate_notify(); |     terminate_notify(); | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  | #ifdef VIDEO | ||||||
|  |     terminate_video(); | ||||||
|  | #endif /* VIDEO */ | ||||||
|     terminate_audio(); |     terminate_audio(); | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|     free_global_data(); |     free_global_data(); | ||||||
|     tox_kill(m); |     tox_kill(m); | ||||||
|     endwin(); |     endwin(); | ||||||
|  |     curl_global_cleanup(); | ||||||
|  |  | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
|     /* We have to terminate xtra last coz reasons |     /* We have to terminate xtra last coz reasons | ||||||
| @@ -191,6 +203,7 @@ static void init_term(void) | |||||||
|     cbreak(); |     cbreak(); | ||||||
|     keypad(stdscr, 1); |     keypad(stdscr, 1); | ||||||
|     noecho(); |     noecho(); | ||||||
|  |     nonl(); | ||||||
|     timeout(100); |     timeout(100); | ||||||
|  |  | ||||||
|     if (has_colors()) { |     if (has_colors()) { | ||||||
| @@ -270,146 +283,15 @@ static void print_init_messages(ToxWindow *toxwin) | |||||||
|         line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]); |         line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]); | ||||||
| } | } | ||||||
|  |  | ||||||
| #define MIN_NODE_LINE  50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */ | static void load_groups(Tox *m) | ||||||
| #define MAX_NODE_LINE  256 /* Approx max number of chars in a sever line (name + port + key) */ |  | ||||||
| #define MAXNODES 50 |  | ||||||
| #define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7) |  | ||||||
|  |  | ||||||
| static struct toxNodes { |  | ||||||
|     int lines; |  | ||||||
|     char nodes[MAXNODES][NODELEN]; |  | ||||||
|     uint16_t ports[MAXNODES]; |  | ||||||
|     char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; |  | ||||||
| } toxNodes; |  | ||||||
|  |  | ||||||
| static int load_nodelist(const char *filename) |  | ||||||
| { | { | ||||||
|     if (!filename) |     size_t i; | ||||||
|         return 1; |     size_t numgroups = tox_group_get_number_groups(m); | ||||||
|  |  | ||||||
|     FILE *fp = fopen(filename, "r"); |     for (i = 0; i < numgroups; ++i) { | ||||||
|  |         if (init_groupchat_win(m, i, NULL, 0) == -1) | ||||||
|     if (fp == NULL) |             tox_group_leave(m, i, NULL, 0, NULL); | ||||||
|         return 1; |  | ||||||
|  |  | ||||||
|     char line[MAX_NODE_LINE]; |  | ||||||
|  |  | ||||||
|     while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) { |  | ||||||
|         size_t line_len = strlen(line); |  | ||||||
|  |  | ||||||
|         if (line_len >= MIN_NODE_LINE && line_len <= MAX_NODE_LINE) { |  | ||||||
|             const char *name = strtok(line, " "); |  | ||||||
|             const char *port = strtok(NULL, " "); |  | ||||||
|             const char *key_ascii = strtok(NULL, " "); |  | ||||||
|  |  | ||||||
|             if (name == NULL || port == NULL || key_ascii == NULL) |  | ||||||
|                 continue; |  | ||||||
|  |  | ||||||
|             size_t key_len = strlen(key_ascii); |  | ||||||
|             size_t name_len = strlen(name); |  | ||||||
|  |  | ||||||
|             if (key_len < TOX_PUBLIC_KEY_SIZE * 2 || name_len >= NODELEN) |  | ||||||
|                 continue; |  | ||||||
|  |  | ||||||
|             snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name); |  | ||||||
|             toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0; |  | ||||||
|             toxNodes.ports[toxNodes.lines] = atoi(port); |  | ||||||
|  |  | ||||||
|             /* remove possible trailing newline from key string */ |  | ||||||
|             char real_ascii_key[TOX_PUBLIC_KEY_SIZE * 2 + 1]; |  | ||||||
|             memcpy(real_ascii_key, key_ascii, TOX_PUBLIC_KEY_SIZE * 2); |  | ||||||
|             key_len = TOX_PUBLIC_KEY_SIZE * 2; |  | ||||||
|             real_ascii_key[key_len] = '\0'; |  | ||||||
|  |  | ||||||
|             if (hex_string_to_bin(real_ascii_key, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1) |  | ||||||
|                 continue; |  | ||||||
|  |  | ||||||
|             toxNodes.lines++; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fclose(fp); |  | ||||||
|  |  | ||||||
|     if (toxNodes.lines < 1) |  | ||||||
|         return 1; |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Bootstraps and adds as TCP relay. |  | ||||||
|  * Returns 0 if both actions are successful. |  | ||||||
|  * Returns -1 otherwise. |  | ||||||
|  */ |  | ||||||
| int init_connection_helper(Tox *m, int line) |  | ||||||
| { |  | ||||||
|     TOX_ERR_BOOTSTRAP err; |  | ||||||
|     tox_bootstrap(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err); |  | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_BOOTSTRAP_OK) { |  | ||||||
|         fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     tox_add_tcp_relay(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err); |  | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_BOOTSTRAP_OK) { |  | ||||||
|         fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Connects to a random DHT node listed in the DHTnodes file |  | ||||||
|  * |  | ||||||
|  * return codes: |  | ||||||
|  * 0: success |  | ||||||
|  * 1: failed to open node file |  | ||||||
|  * 2: no line of sufficient length in node file |  | ||||||
|  * 3: failed to resolve name to IP |  | ||||||
|  * 4: nodelist file contains no acceptable line |  | ||||||
|  */ |  | ||||||
| static bool srvlist_loaded = false; |  | ||||||
|  |  | ||||||
| #define NUM_INIT_NODES 5 |  | ||||||
|  |  | ||||||
| int init_connection(Tox *m) |  | ||||||
| { |  | ||||||
|     if (toxNodes.lines > 0) { /* already loaded nodelist */ |  | ||||||
|         init_connection_helper(m, rand() % toxNodes.lines); |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* only once: |  | ||||||
|      * - load the nodelist |  | ||||||
|      * - connect to "everyone" inside |  | ||||||
|      */ |  | ||||||
|     if (!srvlist_loaded) { |  | ||||||
|         srvlist_loaded = true; |  | ||||||
|         int res; |  | ||||||
|  |  | ||||||
|         if (!arg_opts.nodes_path[0]) |  | ||||||
|             res = load_nodelist(PACKAGE_DATADIR "/DHTnodes"); |  | ||||||
|         else |  | ||||||
|             res = load_nodelist(arg_opts.nodes_path); |  | ||||||
|  |  | ||||||
|         if (res != 0) |  | ||||||
|             return res; |  | ||||||
|  |  | ||||||
|         res = 3; |  | ||||||
|         int i; |  | ||||||
|         int n = MIN(NUM_INIT_NODES, toxNodes.lines); |  | ||||||
|  |  | ||||||
|         for (i = 0; i < n; ++i) { |  | ||||||
|             if (init_connection_helper(m, rand() % toxNodes.lines) == 0) |  | ||||||
|                 res = 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return res; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* empty nodelist file */ |  | ||||||
|     return 4; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void load_friendlist(Tox *m) | static void load_friendlist(Tox *m) | ||||||
| @@ -450,8 +332,10 @@ static int password_prompt(char *buf, int size) | |||||||
|     /* eat overflowed stdin and return error */ |     /* eat overflowed stdin and return error */ | ||||||
|     if (buf[--len] != '\n') { |     if (buf[--len] != '\n') { | ||||||
|         int ch; |         int ch; | ||||||
|  |  | ||||||
|         while ((ch = getchar()) != '\n' && ch > 0) |         while ((ch = getchar()) != '\n' && ch > 0) | ||||||
|             ; |             ; | ||||||
|  |  | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -459,6 +343,49 @@ static int password_prompt(char *buf, int size) | |||||||
|     return len; |     return len; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Get the password from the eval command. | ||||||
|  |  * return length of password on success, 0 on failure | ||||||
|  |  */ | ||||||
|  | static int password_eval(char *buf, int size) | ||||||
|  | { | ||||||
|  |     buf[0] = '\0'; | ||||||
|  |  | ||||||
|  |     /* Run password_eval command */ | ||||||
|  |     FILE *f = popen(user_settings->password_eval, "r"); | ||||||
|  |  | ||||||
|  |     if (f == NULL) { | ||||||
|  |         fprintf(stderr, "Executing password_eval failed\n"); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* Get output from command */ | ||||||
|  |     char *ret = fgets(buf, size, f); | ||||||
|  |  | ||||||
|  |     if (ret == NULL) { | ||||||
|  |         fprintf(stderr, "Reading password from password_eval command failed\n"); | ||||||
|  |         pclose(f); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* Get exit status */ | ||||||
|  |     int status = pclose(f); | ||||||
|  |  | ||||||
|  |     if (status != 0) { | ||||||
|  |         fprintf(stderr, "password_eval command returned error %d\n", status); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* Removez whitespace or \n at end */ | ||||||
|  |     int i, len = strlen(buf); | ||||||
|  |  | ||||||
|  |     for (i = len - 1; i > 0 && isspace(buf[i]); i--) { | ||||||
|  |         buf[i] = 0; | ||||||
|  |         len--; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Ask user if they would like to encrypt the data file and set password */ | /* Ask user if they would like to encrypt the data file and set password */ | ||||||
| static void first_time_encrypt(const char *msg) | static void first_time_encrypt(const char *msg) | ||||||
| { | { | ||||||
| @@ -469,7 +396,7 @@ static void first_time_encrypt(const char *msg) | |||||||
|         printf("%s ", msg); |         printf("%s ", msg); | ||||||
|  |  | ||||||
|         if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n") |         if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n") | ||||||
|             || !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n")) |                 || !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n")) | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|     } while (fgets(ch, sizeof(ch), stdin)); |     } while (fgets(ch, sizeof(ch), stdin)); | ||||||
| @@ -522,29 +449,46 @@ static void first_time_encrypt(const char *msg) | |||||||
|     system("clear"); |     system("clear"); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Store Tox data to given location | /* Store Tox profile data to path. | ||||||
|  * |  * | ||||||
|  * Return 0 if stored successfully or ignoring data file. |  * Return 0 if stored successfully. | ||||||
|  * Return -1 on error |  * Return -1 on error. | ||||||
|  */ |  */ | ||||||
|  | #define TEMP_PROFILE_EXT ".tmp" | ||||||
| int store_data(Tox *m, const char *path) | int store_data(Tox *m, const char *path) | ||||||
| { | { | ||||||
|     if (path == NULL) |     if (path == NULL) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     FILE *fp = fopen(path, "wb"); |     char temp_path[strlen(path) + strlen(TEMP_PROFILE_EXT) + 1]; | ||||||
|  |     snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_PROFILE_EXT); | ||||||
|  |  | ||||||
|     if (fp == NULL) |     FILE *fp = fopen(temp_path, "wb"); | ||||||
|  |  | ||||||
|  |     if (fp == NULL) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     size_t data_len = tox_get_savedata_size(m); |     size_t data_len = tox_get_savedata_size(m); | ||||||
|     char data[data_len]; |     char *data = malloc(data_len * sizeof(char)); | ||||||
|  |  | ||||||
|  |     if (data == NULL) { | ||||||
|  |         fclose(fp); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     tox_get_savedata(m, (uint8_t *) data); |     tox_get_savedata(m, (uint8_t *) data); | ||||||
|  |  | ||||||
|     if (user_password.data_is_encrypted && !arg_opts.unencrypt_data) { |     if (user_password.data_is_encrypted && !arg_opts.unencrypt_data) { | ||||||
|         size_t enc_len = data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH; |         size_t enc_len = data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH; | ||||||
|         char enc_data[enc_len]; |         char *enc_data = malloc(enc_len * sizeof(char)); | ||||||
|  |  | ||||||
|  |         if (enc_data == NULL) { | ||||||
|  |             fclose(fp); | ||||||
|  |             free(data); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         TOX_ERR_ENCRYPTION err; |         TOX_ERR_ENCRYPTION err; | ||||||
|         tox_pass_encrypt((uint8_t *) data, data_len, (uint8_t *) user_password.pass, user_password.len, |         tox_pass_encrypt((uint8_t *) data, data_len, (uint8_t *) user_password.pass, user_password.len, | ||||||
| @@ -553,21 +497,36 @@ int store_data(Tox *m, const char *path) | |||||||
|         if (err != TOX_ERR_ENCRYPTION_OK) { |         if (err != TOX_ERR_ENCRYPTION_OK) { | ||||||
|             fprintf(stderr, "tox_pass_encrypt() failed with error %d\n", err); |             fprintf(stderr, "tox_pass_encrypt() failed with error %d\n", err); | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             free(data); | ||||||
|  |             free(enc_data); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (fwrite(enc_data, enc_len, 1, fp) != 1) { |         if (fwrite(enc_data, enc_len, 1, fp) != 1) { | ||||||
|  |             fprintf(stderr, "Failed to write profile data.\n"); | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             free(data); | ||||||
|  |             free(enc_data); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         free(enc_data); | ||||||
|     } else {  /* data will not be encrypted */ |     } else {  /* data will not be encrypted */ | ||||||
|         if (fwrite(data, data_len, 1, fp) != 1) { |         if (fwrite(data, data_len, 1, fp) != 1) { | ||||||
|  |             fprintf(stderr, "Failed to write profile data.\n"); | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|  |             free(data); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fclose(fp); |     fclose(fp); | ||||||
|  |     free(data); | ||||||
|  |  | ||||||
|  |     if (rename(temp_path, path) != 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -582,15 +541,24 @@ static void init_tox_callbacks(Tox *m) | |||||||
|     tox_callback_friend_status(m, on_statuschange, NULL); |     tox_callback_friend_status(m, on_statuschange, NULL); | ||||||
|     tox_callback_friend_status_message(m, on_statusmessagechange, NULL); |     tox_callback_friend_status_message(m, on_statusmessagechange, NULL); | ||||||
|     tox_callback_friend_read_receipt(m, on_read_receipt, NULL); |     tox_callback_friend_read_receipt(m, on_read_receipt, NULL); | ||||||
|     tox_callback_group_invite(m, on_groupinvite, NULL); |  | ||||||
|     tox_callback_group_message(m, on_groupmessage, NULL); |  | ||||||
|     tox_callback_group_action(m, on_groupaction, NULL); |  | ||||||
|     tox_callback_group_namelist_change(m, on_group_namelistchange, NULL); |  | ||||||
|     tox_callback_group_title(m, on_group_titlechange, NULL); |  | ||||||
|     tox_callback_file_recv(m, on_file_recv, NULL); |     tox_callback_file_recv(m, on_file_recv, NULL); | ||||||
|     tox_callback_file_chunk_request(m, on_file_chunk_request, NULL); |     tox_callback_file_chunk_request(m, on_file_chunk_request, NULL); | ||||||
|     tox_callback_file_recv_control(m, on_file_control, NULL); |     tox_callback_file_recv_control(m, on_file_control, NULL); | ||||||
|     tox_callback_file_recv_chunk(m, on_file_recv_chunk, NULL); |     tox_callback_file_recv_chunk(m, on_file_recv_chunk, NULL); | ||||||
|  |     tox_callback_group_invite(m, on_group_invite, NULL); | ||||||
|  |     tox_callback_group_message(m, on_group_message, NULL); | ||||||
|  |     tox_callback_group_private_message(m, on_group_private_message, NULL); | ||||||
|  |     tox_callback_group_peer_join(m, on_group_peer_join, NULL); | ||||||
|  |     tox_callback_group_peer_exit(m, on_group_peer_exit, NULL); | ||||||
|  |     tox_callback_group_peer_name(m, on_group_nick_change, NULL); | ||||||
|  |     tox_callback_group_peer_status(m, on_group_status_change, NULL); | ||||||
|  |     tox_callback_group_topic(m, on_group_topic_change, NULL); | ||||||
|  |     tox_callback_group_peer_limit(m, on_group_peer_limit, NULL); | ||||||
|  |     tox_callback_group_privacy_state(m, on_group_privacy_state, NULL); | ||||||
|  |     tox_callback_group_password(m, on_group_password, NULL); | ||||||
|  |     tox_callback_group_self_join(m, on_group_self_join, NULL); | ||||||
|  |     tox_callback_group_join_fail(m, on_group_rejected, NULL); | ||||||
|  |     tox_callback_group_moderation(m, on_group_moderation, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void init_tox_options(struct Tox_Options *tox_opts) | static void init_tox_options(struct Tox_Options *tox_opts) | ||||||
| @@ -642,14 +610,14 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW | |||||||
|  |  | ||||||
|         if (len == 0) { |         if (len == 0) { | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|             exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); |             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         char data[len]; |         char data[len]; | ||||||
|  |  | ||||||
|         if (fread(data, sizeof(data), 1, fp) != 1) { |         if (fread(data, sizeof(data), 1, fp) != 1) { | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|             exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); |             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         bool is_encrypted = tox_is_data_encrypted((uint8_t *) data); |         bool is_encrypted = tox_is_data_encrypted((uint8_t *) data); | ||||||
| @@ -657,7 +625,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW | |||||||
|         /* attempt to encrypt an already encrypted data file */ |         /* attempt to encrypt an already encrypted data file */ | ||||||
|         if (arg_opts.encrypt_data && is_encrypted) { |         if (arg_opts.encrypt_data && is_encrypted) { | ||||||
|             fclose(fp); |             fclose(fp); | ||||||
|             exit_toxic_err("failed in load_toxic", FATALERR_ENCRYPT); |             exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (arg_opts.unencrypt_data && is_encrypted) |         if (arg_opts.unencrypt_data && is_encrypted) | ||||||
| @@ -670,14 +638,23 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW | |||||||
|                 user_password.data_is_encrypted = true; |                 user_password.data_is_encrypted = true; | ||||||
|  |  | ||||||
|             size_t pwlen = 0; |             size_t pwlen = 0; | ||||||
|             system("clear");   // TODO: is this portable? |             int pweval = user_settings->password_eval[0]; | ||||||
|             printf("Enter password (q to quit) "); |  | ||||||
|  |             if (!pweval) { | ||||||
|  |                 system("clear");   // TODO: is this portable? | ||||||
|  |                 printf("Enter password (q to quit) "); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             size_t plain_len = len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; |             size_t plain_len = len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; | ||||||
|             char plain[plain_len]; |             char plain[plain_len]; | ||||||
|  |  | ||||||
|             while (true) { |             while (true) { | ||||||
|                 pwlen = password_prompt(user_password.pass, sizeof(user_password.pass)); |                 if (pweval) { | ||||||
|  |                     pwlen = password_eval(user_password.pass, sizeof(user_password.pass)); | ||||||
|  |                 } else { | ||||||
|  |                     pwlen = password_prompt(user_password.pass, sizeof(user_password.pass)); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 user_password.len = pwlen; |                 user_password.len = pwlen; | ||||||
|  |  | ||||||
|                 if (strcasecmp(user_password.pass, "q") == 0) { |                 if (strcasecmp(user_password.pass, "q") == 0) { | ||||||
| @@ -689,6 +666,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW | |||||||
|                     system("clear"); |                     system("clear"); | ||||||
|                     sleep(1); |                     sleep(1); | ||||||
|                     printf("Invalid password. Try again. "); |                     printf("Invalid password. Try again. "); | ||||||
|  |                     pweval = 0; | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -713,6 +691,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW | |||||||
|                     system("clear"); |                     system("clear"); | ||||||
|                     sleep(1); |                     sleep(1); | ||||||
|                     printf("Invalid password. Try again. "); |                     printf("Invalid password. Try again. "); | ||||||
|  |                     pweval = 0; | ||||||
|                 } else { |                 } else { | ||||||
|                     fclose(fp); |                     fclose(fp); | ||||||
|                     exit_toxic_err("tox_pass_decrypt() failed", pwerr); |                     exit_toxic_err("tox_pass_decrypt() failed", pwerr); | ||||||
| @@ -734,7 +713,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW | |||||||
|         fclose(fp); |         fclose(fp); | ||||||
|     } else {   /* Data file does not/should not exist */ |     } else {   /* Data file does not/should not exist */ | ||||||
|         if (file_exists(data_path)) |         if (file_exists(data_path)) | ||||||
|             exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); |             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); | ||||||
|  |  | ||||||
|         tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE; |         tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE; | ||||||
|  |  | ||||||
| @@ -744,7 +723,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW | |||||||
|             return NULL; |             return NULL; | ||||||
|  |  | ||||||
|         if (store_data(m, data_path) == -1) |         if (store_data(m, data_path) == -1) | ||||||
|             exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); |             exit_toxic_err("failed in load_tox", FATALERR_FILEOP); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return m; |     return m; | ||||||
| @@ -780,33 +759,9 @@ static Tox *load_toxic(char *data_path) | |||||||
|     return m; |     return m; | ||||||
| } | } | ||||||
|  |  | ||||||
| #define TRY_BOOTSTRAP_INTERVAL 5 | static void do_toxic(Tox *m) | ||||||
| static uint64_t last_bootstrap_time = 0; |  | ||||||
|  |  | ||||||
| static void do_bootstrap(Tox *m) |  | ||||||
| { |  | ||||||
|     static int conn_err = 0; |  | ||||||
|  |  | ||||||
|     if (!timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     if (tox_self_get_connection_status(m) != TOX_CONNECTION_NONE) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     if (conn_err != 0) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     last_bootstrap_time = get_unix_time(); |  | ||||||
|     conn_err = init_connection(m); |  | ||||||
|  |  | ||||||
|     if (conn_err != 0) |  | ||||||
|         line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Auto-connect failed with error code %d", conn_err); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void do_toxic(Tox *m, ToxWindow *prompt) |  | ||||||
| { | { | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|     update_unix_time(); |  | ||||||
|  |  | ||||||
|     if (arg_opts.no_connect) { |     if (arg_opts.no_connect) { | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
| @@ -814,8 +769,7 @@ static void do_toxic(Tox *m, ToxWindow *prompt) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     tox_iterate(m); |     tox_iterate(m); | ||||||
|     do_bootstrap(m); |     do_tox_connection(m); | ||||||
|     check_file_transfer_timeouts(m); |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -860,7 +814,7 @@ void *thread_cqueue(void *data) | |||||||
|             ToxWindow *toxwin = get_window_ptr(i); |             ToxWindow *toxwin = get_window_ptr(i); | ||||||
|  |  | ||||||
|             if (toxwin != NULL && toxwin->is_chat |             if (toxwin != NULL && toxwin->is_chat | ||||||
|                 && tox_friend_get_connection_status(m, toxwin->num, NULL) != TOX_CONNECTION_NONE) |                     && tox_friend_get_connection_status(m, toxwin->num, NULL) != TOX_CONNECTION_NONE) | ||||||
|                 cqueue_try_send(toxwin, m); |                 cqueue_try_send(toxwin, m); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -871,16 +825,16 @@ void *thread_cqueue(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| void *thread_audio(void *data) | void *thread_av(void *data) | ||||||
| { | { | ||||||
|     ToxAv *av = (ToxAv *) data; |     ToxAV *av = (ToxAV *) data; | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         pthread_mutex_lock(&Winthread.lock); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|         toxav_do(av); |         toxav_iterate(av); | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|         usleep(toxav_do_interval(av) * 1000); |         usleep(toxav_iteration_interval(av) * 1000); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| #endif  /* AUDIO */ | #endif  /* AUDIO */ | ||||||
| @@ -899,7 +853,7 @@ static void print_usage(void) | |||||||
|     fprintf(stderr, "  -o, --noconnect          Do not connect to the DHT network\n"); |     fprintf(stderr, "  -o, --noconnect          Do not connect to the DHT network\n"); | ||||||
|     fprintf(stderr, "  -p, --SOCKS5-proxy       Use SOCKS5 proxy: Requires [IP] [port]\n"); |     fprintf(stderr, "  -p, --SOCKS5-proxy       Use SOCKS5 proxy: Requires [IP] [port]\n"); | ||||||
|     fprintf(stderr, "  -P, --HTTP-proxy         Use HTTP proxy: Requires [IP] [port]\n"); |     fprintf(stderr, "  -P, --HTTP-proxy         Use HTTP proxy: Requires [IP] [port]\n"); | ||||||
|     fprintf(stderr, "  -r, --dnslist            Use specified DNSservers file\n"); |     fprintf(stderr, "  -r, --namelist           Use specified name lookup server list\n"); | ||||||
|     fprintf(stderr, "  -t, --force-tcp          Force toxic to use a TCP connection (use with proxies)\n"); |     fprintf(stderr, "  -t, --force-tcp          Force toxic to use a TCP connection (use with proxies)\n"); | ||||||
|     fprintf(stderr, "  -T, --tcp-server         Act as a TCP relay server: Requires [port]\n"); |     fprintf(stderr, "  -T, --tcp-server         Act as a TCP relay server: Requires [port]\n"); | ||||||
|     fprintf(stderr, "  -u, --unencrypt-data     Unencrypt an encrypted data file\n"); |     fprintf(stderr, "  -u, --unencrypt-data     Unencrypt an encrypted data file\n"); | ||||||
| @@ -927,7 +881,7 @@ static void parse_args(int argc, char *argv[]) | |||||||
|         {"nodes", required_argument, 0, 'n'}, |         {"nodes", required_argument, 0, 'n'}, | ||||||
|         {"help", no_argument, 0, 'h'}, |         {"help", no_argument, 0, 'h'}, | ||||||
|         {"noconnect", no_argument, 0, 'o'}, |         {"noconnect", no_argument, 0, 'o'}, | ||||||
|         {"dnslist", required_argument, 0, 'r'}, |         {"namelist", required_argument, 0, 'r'}, | ||||||
|         {"force-tcp", no_argument, 0, 't'}, |         {"force-tcp", no_argument, 0, 't'}, | ||||||
|         {"tcp-server", required_argument, 0, 'T'}, |         {"tcp-server", required_argument, 0, 'T'}, | ||||||
|         {"SOCKS5-proxy", required_argument, 0, 'p'}, |         {"SOCKS5-proxy", required_argument, 0, 'p'}, | ||||||
| @@ -938,6 +892,7 @@ static void parse_args(int argc, char *argv[]) | |||||||
|  |  | ||||||
|     const char *opts_str = "4bdehotuxc:f:n:r:p:P:T:"; |     const char *opts_str = "4bdehotuxc:f:n:r:p:P:T:"; | ||||||
|     int opt, indexptr; |     int opt, indexptr; | ||||||
|  |     long int port = 0; | ||||||
|  |  | ||||||
|     while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) { |     while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) { | ||||||
|         switch (opt) { |         switch (opt) { | ||||||
| @@ -996,10 +951,6 @@ static void parse_args(int argc, char *argv[]) | |||||||
|  |  | ||||||
|             case 'n': |             case 'n': | ||||||
|                 snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg); |                 snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg); | ||||||
|  |  | ||||||
|                 if (!file_exists(arg_opts.nodes_path)) |  | ||||||
|                     queue_init_message("DHTnodes file not found"); |  | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'o': |             case 'o': | ||||||
| @@ -1011,27 +962,37 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5; |                 arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5; | ||||||
|                 snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); |                 snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); | ||||||
|  |  | ||||||
|                 if (++optind > argc || argv[optind-1][0] == '-') |                 if (++optind > argc || argv[optind - 1][0] == '-') | ||||||
|                     exit_toxic_err("Proxy error", FATALERR_PROXY); |                     exit_toxic_err("Proxy error", FATALERR_PROXY); | ||||||
|  |  | ||||||
|                 arg_opts.proxy_port = (uint16_t) atoi(argv[optind-1]); |                 port = strtol(argv[optind - 1], NULL, 10); | ||||||
|  |  | ||||||
|  |                 if (port <= 0 || port > MAX_PORT_RANGE) | ||||||
|  |                     exit_toxic_err("Proxy error", FATALERR_PROXY); | ||||||
|  |  | ||||||
|  |                 arg_opts.proxy_port = port; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'P': |             case 'P': | ||||||
|                 arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP; |                 arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP; | ||||||
|                 snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); |                 snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); | ||||||
|  |  | ||||||
|                 if (++optind > argc || argv[optind-1][0] == '-') |                 if (++optind > argc || argv[optind - 1][0] == '-') | ||||||
|                     exit_toxic_err("Proxy error", FATALERR_PROXY); |                     exit_toxic_err("Proxy error", FATALERR_PROXY); | ||||||
|  |  | ||||||
|                 arg_opts.proxy_port = (uint16_t) atoi(argv[optind-1]); |                 port = strtol(argv[optind - 1], NULL, 10); | ||||||
|  |  | ||||||
|  |                 if (port <= 0 || port > MAX_PORT_RANGE) | ||||||
|  |                     exit_toxic_err("Proxy error", FATALERR_PROXY); | ||||||
|  |  | ||||||
|  |                 arg_opts.proxy_port = port; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'r': |             case 'r': | ||||||
|                 snprintf(arg_opts.dns_path, sizeof(arg_opts.dns_path), "%s", optarg); |                 snprintf(arg_opts.nameserver_path, sizeof(arg_opts.nameserver_path), "%s", optarg); | ||||||
|  |  | ||||||
|                 if (!file_exists(arg_opts.dns_path)) |                 if (!file_exists(arg_opts.nameserver_path)) | ||||||
|                     queue_init_message("DNSservers file not found"); |                     queue_init_message("nameserver list not found"); | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
| @@ -1040,7 +1001,12 @@ static void parse_args(int argc, char *argv[]) | |||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'T': |             case 'T': | ||||||
|                 arg_opts.tcp_port = (uint16_t) atoi(optarg); |                 port = strtol(optarg, NULL, 10); | ||||||
|  |  | ||||||
|  |                 if (port <= 0 || port > MAX_PORT_RANGE) | ||||||
|  |                     port = 14191; | ||||||
|  |  | ||||||
|  |                 arg_opts.tcp_port = port; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case 'u': |             case 'u': | ||||||
| @@ -1055,26 +1021,72 @@ static void parse_args(int argc, char *argv[]) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int init_default_data_files(void) | /* Looks for an old default profile data file and blocklist, and renames them to the new default names. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
|  | #define OLD_DATA_NAME "data" | ||||||
|  | #define OLD_DATA_BLOCKLIST_NAME "data-blocklist" | ||||||
|  | static int rename_old_profile(const char *user_config_dir) | ||||||
| { | { | ||||||
|     if (arg_opts.use_custom_data) |     char old_data_file[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_NAME) + 1]; | ||||||
|  |     snprintf(old_data_file, sizeof(old_data_file), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_NAME); | ||||||
|  |  | ||||||
|  |     if (!file_exists(old_data_file)) | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
|  |     if (file_exists(DATA_FILE)) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     if (rename(old_data_file, DATA_FILE) != 0) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     queue_init_message("Data file has been moved to %s", DATA_FILE); | ||||||
|  |  | ||||||
|  |     char old_data_blocklist[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_BLOCKLIST_NAME) + 1]; | ||||||
|  |     snprintf(old_data_blocklist, sizeof(old_data_blocklist), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_BLOCKLIST_NAME); | ||||||
|  |  | ||||||
|  |     if (!file_exists(old_data_blocklist)) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     if (file_exists(BLOCK_FILE)) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     if (rename(old_data_blocklist, BLOCK_FILE) != 0) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Initializes the default config directory and data files used by toxic. | ||||||
|  |  * | ||||||
|  |  * Exits the process with an error on failure. | ||||||
|  |  */ | ||||||
|  | static void init_default_data_files(void) | ||||||
|  | { | ||||||
|  |     if (arg_opts.use_custom_data) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     char *user_config_dir = get_user_config_dir(); |     char *user_config_dir = get_user_config_dir(); | ||||||
|  |  | ||||||
|  |     if (user_config_dir == NULL) | ||||||
|  |         exit_toxic_err("failed in init_default_data_files()", FATALERR_FILEOP); | ||||||
|  |  | ||||||
|     int config_err = create_user_config_dirs(user_config_dir); |     int config_err = create_user_config_dirs(user_config_dir); | ||||||
|  |  | ||||||
|     if (config_err) { |     if (config_err == -1) { | ||||||
|         DATA_FILE = strdup(DATANAME); |         DATA_FILE = strdup(DATANAME); | ||||||
|         BLOCK_FILE = strdup(BLOCKNAME); |         BLOCK_FILE = strdup(BLOCKNAME); | ||||||
|  |  | ||||||
|         if (DATA_FILE == NULL || BLOCK_FILE == NULL) |         if (DATA_FILE == NULL || BLOCK_FILE == NULL) | ||||||
|             exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY); |             exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY); | ||||||
|     } else { |     } else { | ||||||
|         DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1); |         DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1); | ||||||
|         BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1); |         BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1); | ||||||
|  |  | ||||||
|         if (DATA_FILE == NULL || BLOCK_FILE == NULL) |         if (DATA_FILE == NULL || BLOCK_FILE == NULL) | ||||||
|             exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY); |             exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY); | ||||||
|  |  | ||||||
|         strcpy(DATA_FILE, user_config_dir); |         strcpy(DATA_FILE, user_config_dir); | ||||||
|         strcat(DATA_FILE, CONFIGDIR); |         strcat(DATA_FILE, CONFIGDIR); | ||||||
| @@ -1085,43 +1097,27 @@ static int init_default_data_files(void) | |||||||
|         strcat(BLOCK_FILE, BLOCKNAME); |         strcat(BLOCK_FILE, BLOCKNAME); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /* For backwards compatibility with old toxic profile names. TODO: remove this some day */ | ||||||
|  |     if (rename_old_profile(user_config_dir) == -1) | ||||||
|  |         queue_init_message("Warning: Profile backwards compatibility failed."); | ||||||
|  |  | ||||||
|     free(user_config_dir); |     free(user_config_dir); | ||||||
|     return config_err; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define REC_TOX_DO_LOOPS_PER_SEC 25 |  | ||||||
|  |  | ||||||
| /* Adjusts usleep value so that tox_do runs close to the recommended number of times per second */ |  | ||||||
| static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, uint64_t cur_time, useconds_t msleepval) |  | ||||||
| { |  | ||||||
|     useconds_t new_sleep = MAX(msleepval, 3); |  | ||||||
|     ++(*loopcount); |  | ||||||
|  |  | ||||||
|     if (*looptimer == cur_time) |  | ||||||
|         return new_sleep; |  | ||||||
|  |  | ||||||
|     if (*loopcount != REC_TOX_DO_LOOPS_PER_SEC) |  | ||||||
|         new_sleep *= (double) *loopcount / REC_TOX_DO_LOOPS_PER_SEC; |  | ||||||
|  |  | ||||||
|     *looptimer = cur_time; |  | ||||||
|     *loopcount = 0; |  | ||||||
|     return new_sleep; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // this doesn't do anything (yet) | // this doesn't do anything (yet) | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
| void DnD_callback(const char* asdv, DropType dt) | void DnD_callback(const char *asdv, DropType dt) | ||||||
| { | { | ||||||
|     // if (dt != DT_plain) |     // if (dt != DT_plain) | ||||||
|     //     return; |     //     return; | ||||||
|  |  | ||||||
|     // pthread_mutex_lock(&Winthread.lock); |     // pthread_mutex_lock(&Winthread.lock); | ||||||
|     // line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv); |     // line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv); | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     // pthread_mutex_unlock(&Winthread.lock); | ||||||
| } | } | ||||||
| #endif /* X11 */ | #endif /* X11 */ | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) | int main(int argc, char **argv) | ||||||
| { | { | ||||||
|     parse_args(argc, argv); |     parse_args(argc, argv); | ||||||
|  |  | ||||||
| @@ -1138,11 +1134,9 @@ int main(int argc, char *argv[]) | |||||||
|     /* Make sure all written files are read/writeable only by the current user. */ |     /* Make sure all written files are read/writeable only by the current user. */ | ||||||
|     umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); |     umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); | ||||||
|  |  | ||||||
|     int config_err = init_default_data_files(); |     init_default_data_files(); | ||||||
|     bool datafile_exists = file_exists(DATA_FILE); |  | ||||||
|  |  | ||||||
|     if (datafile_exists) |     bool datafile_exists = file_exists(DATA_FILE); | ||||||
|         last_bootstrap_time = get_unix_time(); |  | ||||||
|  |  | ||||||
|     if (!datafile_exists && !arg_opts.unencrypt_data) |     if (!datafile_exists && !arg_opts.unencrypt_data) | ||||||
|         first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)"); |         first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)"); | ||||||
| @@ -1157,11 +1151,27 @@ int main(int argc, char *argv[]) | |||||||
|         exit_toxic_err("failed in main", FATALERR_MEMORY); |         exit_toxic_err("failed in main", FATALERR_MEMORY); | ||||||
|  |  | ||||||
|     const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL; |     const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL; | ||||||
|     int settings_err = settings_load(user_settings, p); |  | ||||||
|  |     if (settings_load(user_settings, p) == -1) { | ||||||
|  |         queue_init_message("Failed to load user settings"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int curl_init = curl_global_init(CURL_GLOBAL_ALL); | ||||||
|  |     int nameserver_ret = name_lookup_init(curl_init); | ||||||
|  |  | ||||||
|  |     if (nameserver_ret == -1) { | ||||||
|  |         queue_init_message("curl failed to initialize; name lookup service is disabled."); | ||||||
|  |     } else if (nameserver_ret == -2) { | ||||||
|  |         queue_init_message("Name lookup server list could not be found."); | ||||||
|  |     } else if (nameserver_ret == -3) { | ||||||
|  |         queue_init_message("Name lookup server list does not contain any valid entries."); | ||||||
|  |     } | ||||||
|  |  | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
|  |  | ||||||
|     if (init_xtra(DnD_callback) == -1) |     if (init_xtra(DnD_callback) == -1) | ||||||
|         queue_init_message("X failed to initialize"); |         queue_init_message("X failed to initialize"); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     Tox *m = load_toxic(DATA_FILE); |     Tox *m = load_toxic(DATA_FILE); | ||||||
| @@ -1186,19 +1196,24 @@ int main(int argc, char *argv[]) | |||||||
|     if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) |     if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) | ||||||
|         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); |         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|     av = init_audio(prompt, m); |     av = init_audio(prompt, m); | ||||||
|  |  | ||||||
|     /* audio thread */ | #ifdef VIDEO | ||||||
|     if (pthread_create(&audio_thread.tid, NULL, thread_audio, (void *) av) != 0) |     init_video(prompt, m); | ||||||
|  |  | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  |     /* AV thread */ | ||||||
|  |     if (pthread_create(&av_thread.tid, NULL, thread_av, (void *) av) != 0) | ||||||
|         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); |         exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); | ||||||
|  |  | ||||||
|     set_primary_device(input, user_settings->audio_in_dev); |     set_primary_device(input, user_settings->audio_in_dev); | ||||||
|     set_primary_device(output, user_settings->audio_out_dev); |     set_primary_device(output, user_settings->audio_out_dev); | ||||||
|  |  | ||||||
| #elif SOUND_NOTIFY | #elif SOUND_NOTIFY | ||||||
|  |  | ||||||
|     if ( init_devices() == de_InternalError ) |     if ( init_devices() == de_InternalError ) | ||||||
|         queue_init_message("Failed to init audio devices"); |         queue_init_message("Failed to init audio devices"); | ||||||
|  |  | ||||||
| @@ -1206,21 +1221,18 @@ int main(int argc, char *argv[]) | |||||||
|  |  | ||||||
|     init_notify(60, 3000); |     init_notify(60, 3000); | ||||||
|  |  | ||||||
|     const char *msg; |  | ||||||
|  |  | ||||||
|     if (config_err) { |  | ||||||
|         msg = "Unable to determine configuration directory. Defaulting to 'data' for data file..."; |  | ||||||
|         queue_init_message("%s", msg); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (settings_err == -1) |  | ||||||
|         queue_init_message("Failed to load user settings"); |  | ||||||
|  |  | ||||||
|     /* screen/tmux auto-away timer */ |     /* screen/tmux auto-away timer */ | ||||||
|     if (init_mplex_away_timer(m) == -1) |     if (init_mplex_away_timer(m) == -1) | ||||||
|         queue_init_message("Failed to init mplex auto-away."); |         queue_init_message("Failed to init mplex auto-away."); | ||||||
|  |  | ||||||
|  |     int nodeslist_ret = load_DHT_nodeslist(); | ||||||
|  |  | ||||||
|  |     if (nodeslist_ret != 0) { | ||||||
|  |         queue_init_message("DHT nodeslist failed to load (error %d)", nodeslist_ret); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |     load_groups(m); | ||||||
|     print_init_messages(prompt); |     print_init_messages(prompt); | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
| @@ -1231,26 +1243,25 @@ int main(int argc, char *argv[]) | |||||||
|     snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path); |     snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path); | ||||||
|     execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); |     execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); | ||||||
|  |  | ||||||
|     uint64_t last_save = (uint64_t) time(NULL); |     time_t last_save = get_unix_time(); | ||||||
|     uint64_t looptimer = last_save; |  | ||||||
|     useconds_t msleepval = 40000; |  | ||||||
|     uint64_t loopcount = 0; |  | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         do_toxic(m, prompt); |         do_toxic(m); | ||||||
|         uint64_t cur_time = get_unix_time(); |  | ||||||
|  |         time_t cur_time = get_unix_time(); | ||||||
|  |  | ||||||
|         if (timed_out(last_save, AUTOSAVE_FREQ)) { |         if (timed_out(last_save, AUTOSAVE_FREQ)) { | ||||||
|             pthread_mutex_lock(&Winthread.lock); |             pthread_mutex_lock(&Winthread.lock); | ||||||
|  |  | ||||||
|             if (store_data(m, DATA_FILE) != 0) |             if (store_data(m, DATA_FILE) != 0) | ||||||
|                 line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file"); |                 line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file"); | ||||||
|  |  | ||||||
|             pthread_mutex_unlock(&Winthread.lock); |             pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|             last_save = cur_time; |             last_save = cur_time; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         msleepval = optimal_msleepval(&looptimer, &loopcount, cur_time, msleepval); |         usleep(tox_iteration_interval(m) * 1000); | ||||||
|         usleep(msleepval); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								src/toxic.h
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/toxic.h
									
									
									
									
									
								
							| @@ -49,6 +49,10 @@ | |||||||
| #define KEY_IDENT_DIGITS 3    /* number of hex digits to display for the pub-key based identifier */ | #define KEY_IDENT_DIGITS 3    /* number of hex digits to display for the pub-key based identifier */ | ||||||
| #define TIME_STR_SIZE 32 | #define TIME_STR_SIZE 32 | ||||||
|  |  | ||||||
|  | #ifndef MAX_PORT_RANGE | ||||||
|  | #define MAX_PORT_RANGE 65535 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| /* ASCII key codes */ | /* ASCII key codes */ | ||||||
| #define T_KEY_ESC        0x1B     /* ESC key */ | #define T_KEY_ESC        0x1B     /* ESC key */ | ||||||
| #define T_KEY_KILL       0x0B     /* ctrl-k */ | #define T_KEY_KILL       0x0B     /* ctrl-k */ | ||||||
| @@ -66,6 +70,7 @@ | |||||||
| #define T_KEY_C_L        0x0C     /* ctrl-l */ | #define T_KEY_C_L        0x0C     /* ctrl-l */ | ||||||
| #define T_KEY_C_W        0x17     /* ctrl-w */ | #define T_KEY_C_W        0x17     /* ctrl-w */ | ||||||
| #define T_KEY_C_B        0x02     /* ctrl-b */ | #define T_KEY_C_B        0x02     /* ctrl-b */ | ||||||
|  | #define T_KEY_C_T        0x14     /* ctrl-t */ | ||||||
| #define T_KEY_TAB        0x09     /* TAB key */ | #define T_KEY_TAB        0x09     /* TAB key */ | ||||||
|  |  | ||||||
| #define ONLINE_CHAR "*" | #define ONLINE_CHAR "*" | ||||||
| @@ -84,6 +89,7 @@ typedef enum _FATAL_ERRS { | |||||||
|     FATALERR_PROXY = -10,           /* Tox network failed to init using a proxy */ |     FATALERR_PROXY = -10,           /* Tox network failed to init using a proxy */ | ||||||
|     FATALERR_ENCRYPT = -11,         /* Data file encryption failure */ |     FATALERR_ENCRYPT = -11,         /* Data file encryption failure */ | ||||||
|     FATALERR_TOX_INIT = -12,        /* Tox instance failed to initialize */ |     FATALERR_TOX_INIT = -12,        /* Tox instance failed to initialize */ | ||||||
|  |     FATALERR_CURSES = -13,          /* Unrecoverable Ncurses error */ | ||||||
| } FATAL_ERRS; | } FATAL_ERRS; | ||||||
|  |  | ||||||
| /* Fixes text color problem on some terminals. | /* Fixes text color problem on some terminals. | ||||||
| @@ -101,18 +107,21 @@ int store_data(Tox *m, const char *path); | |||||||
| /* callbacks */ | /* callbacks */ | ||||||
| void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata); | void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata); | ||||||
| void on_connectionchange(Tox *m, uint32_t friendnumber, TOX_CONNECTION status, void *userdata); | void on_connectionchange(Tox *m, uint32_t friendnumber, TOX_CONNECTION status, void *userdata); | ||||||
| void on_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length, void *userdata); | void on_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length, | ||||||
|  |                 void *userdata); | ||||||
| void on_action(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); | void on_action(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); | ||||||
| void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); | void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); | ||||||
| void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata); | void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata); | ||||||
| void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); | void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); | ||||||
| void on_friendadded(Tox *m, uint32_t friendnumber, bool sort); | void on_friendadded(Tox *m, uint32_t friendnumber, bool sort); | ||||||
| void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void *userdata); | void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, | ||||||
| void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void *userdata); |                            void *userdata); | ||||||
| void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length, void *userdata); | void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length, | ||||||
| void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata); |                     void *userdata); | ||||||
| void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, void *userdata); | void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, | ||||||
| void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void *userdata); |                           void *userdata); | ||||||
|  | void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, | ||||||
|  |                            void *userdata); | ||||||
| void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data, | void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data, | ||||||
|                         size_t length, void *userdata); |                         size_t length, void *userdata); | ||||||
| void on_file_control (Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control, void *userdata); | void on_file_control (Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control, void *userdata); | ||||||
| @@ -120,5 +129,25 @@ void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t k | |||||||
|                   const uint8_t *filename, size_t filename_length, void *userdata); |                   const uint8_t *filename, size_t filename_length, void *userdata); | ||||||
| void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata); | void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata); | ||||||
| void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata); | void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata); | ||||||
|  | void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length, void *userdata); | ||||||
|  | void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type, | ||||||
|  |                       const uint8_t *message, size_t length, void *userdata); | ||||||
|  | void on_group_private_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *message, size_t length, | ||||||
|  |                               void *userdata); | ||||||
|  | void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peernumber, void *userdata); | ||||||
|  | void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *partmsg, size_t length, | ||||||
|  |                         void *userdata); | ||||||
|  | void on_group_topic_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *topic, size_t length, | ||||||
|  |                            void *userdata); | ||||||
|  | void on_group_peer_limit(Tox *m, uint32_t groupnumber, uint32_t peer_limit, void *userdata); | ||||||
|  | void on_group_privacy_state(Tox *m, uint32_t groupnumber, TOX_GROUP_PRIVACY_STATE privacy_state, void *userdata); | ||||||
|  | void on_group_password(Tox *m, uint32_t groupnumber, const uint8_t *password, size_t length, void *userdata); | ||||||
|  | void on_group_nick_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *newname, size_t length, | ||||||
|  |                           void *userdata); | ||||||
|  | void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_USER_STATUS status, void *userdata); | ||||||
|  | void on_group_self_join(Tox *m, uint32_t groupnumber, void *userdata); | ||||||
|  | void on_group_rejected(Tox *m, uint32_t groupnumber, TOX_GROUP_JOIN_FAIL type, void *userdata); | ||||||
|  | void on_group_moderation(Tox *m, uint32_t groupnumber, uint32_t source_peernum, uint32_t target_peernum, | ||||||
|  |                          TOX_GROUP_MOD_EVENT type, void *userdata); | ||||||
|  |  | ||||||
| #endif  /* #define TOXIC_H */ | #endif  /* #define TOXIC_H */ | ||||||
|   | |||||||
| @@ -136,13 +136,13 @@ int del_word_buf(ChatContext *ctx) | |||||||
|     int i = ctx->pos, count = 0; |     int i = ctx->pos, count = 0; | ||||||
|  |  | ||||||
|     /* traverse past empty space */ |     /* traverse past empty space */ | ||||||
|     while (i > 0 && ctx->line[i-1] == L' ') { |     while (i > 0 && ctx->line[i - 1] == L' ') { | ||||||
|         ++count; |         ++count; | ||||||
|         --i; |         --i; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* traverse past last entered word */ |     /* traverse past last entered word */ | ||||||
|     while (i > 0 && ctx->line[i-1] != L' ') { |     while (i > 0 && ctx->line[i - 1] != L' ') { | ||||||
|         ++count; |         ++count; | ||||||
|         --i; |         --i; | ||||||
|     } |     } | ||||||
| @@ -166,19 +166,19 @@ void reset_buf(ChatContext *ctx) | |||||||
|     ctx->start = 0; |     ctx->start = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Removes trailing spaces from line. */ | /* Removes trailing spaces and newlines from line. */ | ||||||
| void rm_trailing_spaces_buf(ChatContext *ctx) | void rm_trailing_spaces_buf(ChatContext *ctx) | ||||||
| { | { | ||||||
|     if (ctx->len <= 0) |     if (ctx->len <= 0) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     if (ctx->line[ctx->len - 1] != ' ') |     if (ctx->line[ctx->len - 1] != ' ' && ctx->line[ctx->len - 1] != L'¶') | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = ctx->len - 1; i >= 0; --i) { |     for (i = ctx->len - 1; i >= 0; --i) { | ||||||
|         if (ctx->line[i] != ' ') |         if (ctx->line[i] != ' ' && ctx->line[i] != L'¶') | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -242,3 +242,21 @@ void fetch_hist_item(ChatContext *ctx, int key_dir) | |||||||
|     ctx->pos = h_len; |     ctx->pos = h_len; | ||||||
|     ctx->len = h_len; |     ctx->len = h_len; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void strsubst(char *str, char old, char new) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; str[i] != '\0'; ++i) | ||||||
|  |         if (str[i] == old) | ||||||
|  |             str[i] = new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void wstrsubst(wchar_t *str, wchar_t old, wchar_t new) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; str[i] != L'\0'; ++i) | ||||||
|  |         if (str[i] == old) | ||||||
|  |             str[i] = new; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -66,4 +66,8 @@ void add_line_to_hist(ChatContext *ctx); | |||||||
|    resets line if at end of history */ |    resets line if at end of history */ | ||||||
| void fetch_hist_item(ChatContext *ctx, int key_dir); | void fetch_hist_item(ChatContext *ctx, int key_dir); | ||||||
|  |  | ||||||
|  | /* Substitutes all occurrences of old with new. */ | ||||||
|  | void strsubst(char *str, char old, char new); | ||||||
|  | void wstrsubst(wchar_t *str, wchar_t old, wchar_t new); | ||||||
|  |  | ||||||
| #endif /* #define TOXIC_STRINGS_H */ | #endif /* #define TOXIC_STRINGS_H */ | ||||||
|   | |||||||
							
								
								
									
										426
									
								
								src/video_call.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								src/video_call.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,426 @@ | |||||||
|  | /*  video_call.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 "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  | #include "video_call.h" | ||||||
|  | #include "video_device.h" | ||||||
|  | #include "chat_commands.h" | ||||||
|  | #include "global_commands.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "notify.h" | ||||||
|  |  | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <curses.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <assert.h> | ||||||
|  |  | ||||||
|  | #define default_video_bit_rate 5000 | ||||||
|  |  | ||||||
|  | void receive_video_frame_cb( ToxAV *av, uint32_t friend_number, | ||||||
|  |                              uint16_t width, uint16_t height, | ||||||
|  |                              uint8_t const *y, uint8_t const *u, uint8_t const *v, | ||||||
|  |                              int32_t ystride, int32_t ustride, int32_t vstride, | ||||||
|  |                              void *user_data ); | ||||||
|  |  | ||||||
|  | void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, | ||||||
|  |                                uint32_t video_bit_rate, void *user_data); | ||||||
|  |  | ||||||
|  | static void print_err (ToxWindow *self, const char *error_str) | ||||||
|  | { | ||||||
|  |     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ToxAV *init_video(ToxWindow *self, Tox *tox) | ||||||
|  | { | ||||||
|  |     CallControl.video_errors = ve_None; | ||||||
|  |  | ||||||
|  |     CallControl.video_enabled = true; | ||||||
|  |     CallControl.video_bit_rate = 0; | ||||||
|  |     CallControl.video_frame_duration = 10; | ||||||
|  |  | ||||||
|  |     if ( !CallControl.av ) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video failed to init with ToxAV instance"); | ||||||
|  |  | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( init_video_devices(CallControl.av) == vde_InternalError ) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices"); | ||||||
|  |  | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     toxav_callback_video_receive_frame(CallControl.av, receive_video_frame_cb, &CallControl); | ||||||
|  |     toxav_callback_bit_rate_status(CallControl.av, video_bit_rate_status_cb, &CallControl); | ||||||
|  |  | ||||||
|  |     return CallControl.av; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void terminate_video() | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_CALLS; ++i) { | ||||||
|  |         Call *this_call = &CallControl.calls[i]; | ||||||
|  |  | ||||||
|  |         stop_video_transmission(this_call, i); | ||||||
|  |  | ||||||
|  |         if ( this_call->vout_idx != -1 ) | ||||||
|  |             close_video_device(vdt_output, this_call->vout_idx); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     terminate_video_devices(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void read_video_device_callback(int16_t width, int16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, | ||||||
|  |                                 void *data) | ||||||
|  | { | ||||||
|  |     uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */ | ||||||
|  |     Call *this_call = &CallControl.calls[friend_number]; | ||||||
|  |     TOXAV_ERR_SEND_FRAME error; | ||||||
|  |  | ||||||
|  |     /* Drop frame if video sending is disabled */ | ||||||
|  |     if ( CallControl.video_bit_rate == 0 || this_call->vin_idx == -1 ) { | ||||||
|  |         line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( toxav_video_send_frame(CallControl.av, friend_number, width, height, y, u, v, &error ) == false ) { | ||||||
|  |         line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to send video frame"); | ||||||
|  |  | ||||||
|  |         if ( error == TOXAV_ERR_SEND_FRAME_NULL ) | ||||||
|  |             line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to capture video frame"); | ||||||
|  |         else if ( error == TOXAV_ERR_SEND_FRAME_INVALID ) | ||||||
|  |             line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video frame"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void write_video_device_callback(uint32_t friend_number, uint16_t width, uint16_t height, | ||||||
|  |                                  uint8_t const *y, uint8_t const *u, uint8_t const *v, | ||||||
|  |                                  int32_t ystride, int32_t ustride, int32_t vstride, | ||||||
|  |                                  void *user_data) | ||||||
|  | { | ||||||
|  |     write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call) | ||||||
|  | { | ||||||
|  |     if ( !self || !av) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     CallControl.video_bit_rate = default_video_bit_rate; | ||||||
|  |  | ||||||
|  |     if ( toxav_bit_rate_set(CallControl.av, self->num, -1, CallControl.video_bit_rate, NULL) == false ) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( open_primary_video_device(vdt_input, &call->vin_idx) != vde_None ) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( register_video_device_callback(self->num, call->vin_idx, read_video_device_callback, &self->num) != vde_None ) { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input video handler!"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int stop_video_transmission(Call *call, int friend_number) | ||||||
|  | { | ||||||
|  |     CallControl.video_bit_rate = 0; | ||||||
|  |     toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL); | ||||||
|  |  | ||||||
|  |     if ( call->vin_idx != -1 ) { | ||||||
|  |         close_video_device(vdt_input, call->vin_idx); | ||||||
|  |         call->vin_idx = -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | /* | ||||||
|  |  * End of transmission | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Callbacks | ||||||
|  |  */ | ||||||
|  | void receive_video_frame_cb(ToxAV *av, uint32_t friend_number, | ||||||
|  |                             uint16_t width, uint16_t height, | ||||||
|  |                             uint8_t const *y, uint8_t const *u, uint8_t const *v, | ||||||
|  |                             int32_t ystride, int32_t ustride, int32_t vstride, | ||||||
|  |                             void *user_data) | ||||||
|  | { | ||||||
|  |     write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, | ||||||
|  |                               uint32_t video_bit_rate, void *user_data) | ||||||
|  | { | ||||||
|  |     CallControl.video_bit_rate = video_bit_rate; | ||||||
|  |     toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void callback_recv_video_starting(uint32_t friend_number) | ||||||
|  | { | ||||||
|  |     Call *this_call = &CallControl.calls[friend_number]; | ||||||
|  |  | ||||||
|  |     if ( this_call->vout_idx != -1 ) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     open_primary_video_device(vdt_output, &this_call->vout_idx); | ||||||
|  | } | ||||||
|  | void callback_recv_video_end(uint32_t friend_number) | ||||||
|  | { | ||||||
|  |     Call *this_call = &CallControl.calls[friend_number]; | ||||||
|  |  | ||||||
|  |     close_video_device(vdt_output, this_call->vout_idx); | ||||||
|  |     this_call->vout_idx = -1; | ||||||
|  | } | ||||||
|  | void callback_video_starting(uint32_t friend_number) | ||||||
|  | { | ||||||
|  |     ToxWindow *windows = CallControl.prompt; | ||||||
|  |     Call *this_call = &CallControl.calls[friend_number]; | ||||||
|  |  | ||||||
|  |     TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK; | ||||||
|  |     toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_SHOW_VIDEO, &error); | ||||||
|  |  | ||||||
|  |     if (error == TOXAV_ERR_CALL_CONTROL_OK) { | ||||||
|  |         size_t i; | ||||||
|  |  | ||||||
|  |         for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |             if ( windows[i].is_call && windows[i].num == friend_number ) { | ||||||
|  |                 if ( 0 != start_video_transmission(&windows[i], CallControl.av, this_call) ) { | ||||||
|  |                     line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!"); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting."); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | void callback_video_end(uint32_t friend_number) | ||||||
|  | { | ||||||
|  |     stop_video_transmission(&CallControl.calls[friend_number], friend_number); | ||||||
|  | } | ||||||
|  | /* | ||||||
|  |  * End of Callbacks | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Commands from chat_commands.h | ||||||
|  |  */ | ||||||
|  | void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     const char *error_str; | ||||||
|  |     Call *this_call = &CallControl.calls[self->num]; | ||||||
|  |  | ||||||
|  |     if ( argc != 0 ) { | ||||||
|  |         error_str = "Unknown arguments."; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( !CallControl.av ) { | ||||||
|  |         error_str = "ToxAV not supported!"; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( !self->stb->connection ) { | ||||||
|  |         error_str = "Friend is offline."; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( !self->is_call ) { | ||||||
|  |         error_str = "Not in call!"; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( this_call->vin_idx == -1 ) | ||||||
|  |         callback_video_starting(self->num); | ||||||
|  |     else | ||||||
|  |         callback_video_end(self->num); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  | on_error: | ||||||
|  |     print_err (self, error_str); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     const char *error_str; | ||||||
|  |  | ||||||
|  |     if ( argc != 1 ) { | ||||||
|  |         if ( argc < 1 ) error_str = "Type must be specified!"; | ||||||
|  |         else error_str = "Only one argument allowed!"; | ||||||
|  |  | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     VideoDeviceType type; | ||||||
|  |  | ||||||
|  |     if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */ | ||||||
|  |         type = vdt_input; | ||||||
|  |  | ||||||
|  |     else if ( strcasecmp(argv[1], "out") == 0 ) /* Output devices */ | ||||||
|  |         type = vdt_output; | ||||||
|  |  | ||||||
|  |     else { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     print_video_devices(self, type); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  | on_error: | ||||||
|  |     print_err (self, error_str); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* This changes primary video device only */ | ||||||
|  | void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     const char *error_str; | ||||||
|  |  | ||||||
|  |     if ( argc != 2 ) { | ||||||
|  |         if ( argc < 1 ) error_str = "Type must be specified!"; | ||||||
|  |         else if ( argc < 2 ) error_str = "Must have id!"; | ||||||
|  |         else error_str = "Only two arguments allowed!"; | ||||||
|  |  | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     VideoDeviceType type; | ||||||
|  |  | ||||||
|  |     if ( strcmp(argv[1], "in") == 0 ) /* Input devices */ | ||||||
|  |         type = vdt_input; | ||||||
|  |  | ||||||
|  |     else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */ | ||||||
|  |         type = vdt_output; | ||||||
|  |  | ||||||
|  |     else { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     char *end; | ||||||
|  |     long int selection = strtol(argv[2], &end, 10); | ||||||
|  |  | ||||||
|  |     if ( *end ) { | ||||||
|  |         error_str = "Invalid input"; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( set_primary_video_device(type, selection) == vde_InvalidSelection ) { | ||||||
|  |         error_str = "Invalid selection!"; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  | on_error: | ||||||
|  |     print_err (self, error_str); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
|  | { | ||||||
|  |     const char *error_str; | ||||||
|  |  | ||||||
|  |     if ( argc != 2 ) { | ||||||
|  |         if ( argc < 1 ) error_str = "Type must be specified!"; | ||||||
|  |         else if ( argc < 2 ) error_str = "Must have id!"; | ||||||
|  |         else error_str = "Only two arguments allowed!"; | ||||||
|  |  | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     VideoDeviceType type; | ||||||
|  |  | ||||||
|  |     if ( strcmp(argv[1], "in") == 0 ) /* Input devices */ | ||||||
|  |         type = vdt_input; | ||||||
|  |  | ||||||
|  |     else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */ | ||||||
|  |         type = vdt_output; | ||||||
|  |  | ||||||
|  |     else { | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     char *end; | ||||||
|  |     long int selection = strtol(argv[2], &end, 10); | ||||||
|  |  | ||||||
|  |     if ( *end ) { | ||||||
|  |         error_str = "Invalid input"; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( video_selection_valid(type, selection) == vde_InvalidSelection ) { | ||||||
|  |         error_str = "Invalid selection!"; | ||||||
|  |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* If call is active, change device */ | ||||||
|  |     if ( self->is_call ) { | ||||||
|  |         Call *this_call = &CallControl.calls[self->num]; | ||||||
|  |  | ||||||
|  |         if ( this_call->ttas ) { | ||||||
|  |  | ||||||
|  |             if ( type == vdt_output ) { | ||||||
|  |             } else { | ||||||
|  |                 /* TODO: check for failure */ | ||||||
|  |                 close_video_device(vdt_input, this_call->vin_idx); | ||||||
|  |                 open_video_device(vdt_input, selection, &this_call->vin_idx); | ||||||
|  |                 register_video_device_callback(self->num, this_call->vin_idx, read_video_device_callback, &self->num); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     self->video_device_selection[type] = selection; | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  | on_error: | ||||||
|  |     print_err (self, error_str); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void stop_video_stream(ToxWindow *self) | ||||||
|  | { | ||||||
|  |     Call *this_call = &CallControl.calls[self->num]; | ||||||
|  |  | ||||||
|  |     if (this_call && this_call->vin_idx != -1) | ||||||
|  |         stop_video_transmission(this_call, self->num); | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								src/video_call.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/video_call.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | /*  video_call.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef VIDEO_CALL_H | ||||||
|  | #define VIDEO_CALL_H | ||||||
|  |  | ||||||
|  | #include <tox/toxav.h> | ||||||
|  |  | ||||||
|  | #include "audio_call.h" | ||||||
|  |  | ||||||
|  | #include "video_device.h" | ||||||
|  |  | ||||||
|  | /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | ||||||
|  | ToxAV *init_video(ToxWindow *self, Tox *tox); | ||||||
|  | void terminate_video(); | ||||||
|  | int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call); | ||||||
|  | int stop_video_transmission(Call *call, int friend_number); | ||||||
|  | void stop_video_stream(ToxWindow *self); | ||||||
|  |  | ||||||
|  | void callback_recv_video_starting(uint32_t friend_number); | ||||||
|  | void callback_recv_video_end(uint32_t friend_number); | ||||||
|  | void callback_video_starting(uint32_t friend_number); | ||||||
|  | void callback_video_end(uint32_t friend_number); | ||||||
|  |  | ||||||
|  | #endif /* VIDEO_CALL_H */ | ||||||
							
								
								
									
										824
									
								
								src/video_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										824
									
								
								src/video_device.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,824 @@ | |||||||
|  | /*  video_device.c | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "video_device.h" | ||||||
|  | #include "video_call.h" | ||||||
|  |  | ||||||
|  | #include <sys/ioctl.h> | ||||||
|  | #include <X11/Xlib.h> | ||||||
|  | #include <X11/Xutil.h> | ||||||
|  | #include <X11/Xos.h> | ||||||
|  |  | ||||||
|  | #include <vpx/vpx_image.h> | ||||||
|  |  | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <linux/videodev2.h> | ||||||
|  | #else /* __OSX__ */ | ||||||
|  | #import "osx_video.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #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> | ||||||
|  |  | ||||||
|  | #define inline__ inline __attribute__((always_inline)) | ||||||
|  |  | ||||||
|  | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
|  | struct VideoBuffer { | ||||||
|  |     void *start; | ||||||
|  |     size_t length; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | typedef struct VideoDevice { | ||||||
|  |     VideoDataHandleCallback cb;             /* Use this to handle data from input device usually */ | ||||||
|  |     void *cb_data;                          /* Data to be passed to callback */ | ||||||
|  |     int32_t friend_number;                  /* ToxAV friend number */ | ||||||
|  |  | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  |     int fd;                                 /* File descriptor of video device selected/opened */ | ||||||
|  |     struct v4l2_format fmt; | ||||||
|  |     struct VideoBuffer *buffers; | ||||||
|  |     uint32_t n_buffers; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     uint32_t ref_count; | ||||||
|  |     int32_t selection; | ||||||
|  |     pthread_mutex_t mutex[1]; | ||||||
|  |     uint16_t video_width; | ||||||
|  |     uint16_t video_height; | ||||||
|  |  | ||||||
|  |     vpx_image_t input; | ||||||
|  |  | ||||||
|  |     Display *x_display; | ||||||
|  |     Window x_window; | ||||||
|  |     GC x_gc; | ||||||
|  |  | ||||||
|  | } VideoDevice; | ||||||
|  |  | ||||||
|  | const char *dvideo_device_names[2];              /* Default device */ | ||||||
|  | const char *video_devices_names[2][MAX_DEVICES]; /* Container of available devices */ | ||||||
|  | static int size[2];                        /* Size of above containers */ | ||||||
|  | VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}};     /* Running devices */ | ||||||
|  | uint32_t primary_video_device[2];          /* Primary device */ | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  | static ToxAV *av = NULL; | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  | /* q_mutex */ | ||||||
|  | #define lock pthread_mutex_lock(&video_mutex); | ||||||
|  | #define unlock pthread_mutex_unlock(&video_mutex); | ||||||
|  | pthread_mutex_t video_mutex; | ||||||
|  |  | ||||||
|  | bool video_thread_running = true, | ||||||
|  |      video_thread_paused = true;                /* Thread control */ | ||||||
|  |  | ||||||
|  | void *video_thread_poll(void *); | ||||||
|  |  | ||||||
|  | static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y, | ||||||
|  |                         const uint8_t *u, const uint8_t *v, unsigned int ystride, | ||||||
|  |                         unsigned int ustride, unsigned int vstride, uint8_t *out) | ||||||
|  | { | ||||||
|  |     unsigned long int i, j; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < height; ++i) { | ||||||
|  |         for (j = 0; j < width; ++j) { | ||||||
|  |             uint8_t *point = out + 4 * ((i * width) + j); | ||||||
|  |             int t_y = y[((i * ystride) + j)]; | ||||||
|  |             int t_u = u[(((i / 2) * ustride) + (j / 2))]; | ||||||
|  |             int t_v = v[(((i / 2) * vstride) + (j / 2))]; | ||||||
|  |             t_y = t_y < 16 ? 16 : t_y; | ||||||
|  |  | ||||||
|  |             int r = (298 * (t_y - 16) + 409 * (t_v - 128) + 128) >> 8; | ||||||
|  |             int g = (298 * (t_y - 16) - 100 * (t_u - 128) - 208 * (t_v - 128) + 128) >> 8; | ||||||
|  |             int b = (298 * (t_y - 16) + 516 * (t_u - 128) + 128) >> 8; | ||||||
|  |  | ||||||
|  |             point[2] = r > 255 ? 255 : r < 0 ? 0 : r; | ||||||
|  |             point[1] = g > 255 ? 255 : g < 0 ? 0 : g; | ||||||
|  |             point[0] = b > 255 ? 255 : b < 0 ? 0 : b; | ||||||
|  |             point[3] = ~0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  | static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, | ||||||
|  |                         uint8_t *input, uint16_t width, uint16_t height) | ||||||
|  | { | ||||||
|  |     uint8_t *end = input + width * height * 2; | ||||||
|  |  | ||||||
|  |     while (input != end) { | ||||||
|  |         uint8_t *line_end = input + width * 2; | ||||||
|  |  | ||||||
|  |         while (input != line_end) { | ||||||
|  |             *plane_y++ = *input++; | ||||||
|  |             *plane_u++ = *input++; | ||||||
|  |             *plane_y++ = *input++; | ||||||
|  |             *plane_v++ = *input++; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         line_end = input + width * 2; | ||||||
|  |  | ||||||
|  |         while (input != line_end) { | ||||||
|  |             *plane_y++ = *input++; | ||||||
|  |             input++;//u | ||||||
|  |             *plane_y++ = *input++; | ||||||
|  |             input++;//v | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int xioctl(int fh, unsigned long request, void *arg) | ||||||
|  | { | ||||||
|  |     int r; | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |         r = ioctl(fh, request, arg); | ||||||
|  |     } while (-1 == r && EINTR == errno); | ||||||
|  |  | ||||||
|  |     return r; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif /* __linux__ */ | ||||||
|  |  | ||||||
|  | /* Meet devices */ | ||||||
|  | #ifdef VIDEO | ||||||
|  | VideoDeviceError init_video_devices(ToxAV *av_) | ||||||
|  | #else | ||||||
|  | VideoDeviceError init_video_devices() | ||||||
|  | #endif /* VIDEO */ | ||||||
|  | { | ||||||
|  |     size[vdt_input] = 0; | ||||||
|  |  | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  |  | ||||||
|  |     for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) { | ||||||
|  |         int fd; | ||||||
|  |         char device_address[] = "/dev/videoXX"; | ||||||
|  |         snprintf(device_address + 10, sizeof(char) * strlen(device_address) - 10, "%i", size[vdt_input]); | ||||||
|  |  | ||||||
|  |         fd = open(device_address, O_RDWR | O_NONBLOCK, 0); | ||||||
|  |  | ||||||
|  |         if ( fd == -1 ) { | ||||||
|  |             break; | ||||||
|  |         } else { | ||||||
|  |             struct v4l2_capability cap; | ||||||
|  |             char *video_input_name; | ||||||
|  |  | ||||||
|  |             /* Query V4L for capture capabilities */ | ||||||
|  |             if ( -1 != ioctl(fd, VIDIOC_QUERYCAP, &cap) ) { | ||||||
|  |                 video_input_name = (char *)malloc(strlen((const char *)cap.card) + strlen(device_address) + 4); | ||||||
|  |                 strcpy(video_input_name, (char *)cap.card); | ||||||
|  |                 strcat(video_input_name, " ("); | ||||||
|  |                 strcat(video_input_name, (char *)device_address); | ||||||
|  |                 strcat(video_input_name, ")"); | ||||||
|  |             } else { | ||||||
|  |                 video_input_name = (char *)malloc(strlen(device_address) + 3); | ||||||
|  |                 strcpy(video_input_name, "("); | ||||||
|  |                 strcat(video_input_name, device_address); | ||||||
|  |                 strcat(video_input_name, ")"); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             video_devices_names[vdt_input][size[vdt_input]] = video_input_name; | ||||||
|  |  | ||||||
|  |             close(fd); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #else /* __OSX__ */ | ||||||
|  |  | ||||||
|  |     if ( osx_video_init((char **)video_devices_names[vdt_input], &size[vdt_input]) != 0 ) | ||||||
|  |         return vde_InternalError; | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     size[vdt_output] = 1; | ||||||
|  |     char *video_output_name = "Toxic Video Receiver"; | ||||||
|  |     video_devices_names[vdt_output][0] = video_output_name; | ||||||
|  |  | ||||||
|  |     // Start poll thread | ||||||
|  |     if ( pthread_mutex_init(&video_mutex, NULL) != 0 ) | ||||||
|  |         return vde_InternalError; | ||||||
|  |  | ||||||
|  |     pthread_t thread_id; | ||||||
|  |  | ||||||
|  |     if ( pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0 ) | ||||||
|  |         return vde_InternalError; | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |     av = av_; | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  |     return (VideoDeviceError) vde_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VideoDeviceError terminate_video_devices() | ||||||
|  | { | ||||||
|  |     /* Cleanup if needed */ | ||||||
|  |     lock; | ||||||
|  |     video_thread_running = false; | ||||||
|  |     unlock; | ||||||
|  |  | ||||||
|  |     usleep(20000); | ||||||
|  |  | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < size[vdt_input]; ++i) { | ||||||
|  |         free((void *)video_devices_names[vdt_input][i]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( pthread_mutex_destroy(&video_mutex) != 0 ) | ||||||
|  |         return (VideoDeviceError) vde_InternalError; | ||||||
|  |  | ||||||
|  | #ifdef __OSX__ | ||||||
|  |     osx_video_release(); | ||||||
|  | #endif /* __OSX__ */ | ||||||
|  |  | ||||||
|  |     return (VideoDeviceError) vde_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx, | ||||||
|  |         VideoDataHandleCallback callback, void *data) | ||||||
|  | { | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  |  | ||||||
|  |     if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] | ||||||
|  |             || !video_devices_running[vdt_input][device_idx]->fd ) | ||||||
|  |         return vde_InvalidSelection; | ||||||
|  |  | ||||||
|  | #else /* __OSX__ */ | ||||||
|  |  | ||||||
|  |     if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] ) | ||||||
|  |         return vde_InvalidSelection; | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     lock; | ||||||
|  |     video_devices_running[vdt_input][device_idx]->cb = callback; | ||||||
|  |     video_devices_running[vdt_input][device_idx]->cb_data = data; | ||||||
|  |     video_devices_running[vdt_input][device_idx]->friend_number = friend_number; | ||||||
|  |     unlock; | ||||||
|  |  | ||||||
|  |     return vde_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection) | ||||||
|  | { | ||||||
|  |     if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection; | ||||||
|  |  | ||||||
|  |     primary_video_device[type] = selection; | ||||||
|  |  | ||||||
|  |     return vde_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx) | ||||||
|  | { | ||||||
|  |     return open_video_device(type, primary_video_device[type], device_idx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void get_primary_video_device_name(VideoDeviceType type, char *buf, int size) | ||||||
|  | { | ||||||
|  |     memcpy(buf, dvideo_device_names[type], size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx) | ||||||
|  | { | ||||||
|  |     if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection; | ||||||
|  |  | ||||||
|  |     lock; | ||||||
|  |  | ||||||
|  |     uint32_t i, temp_idx = -1; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_DEVICES; ++i) { | ||||||
|  |         if ( !video_devices_running[type][i] ) { | ||||||
|  |             temp_idx = i; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (temp_idx == -1) { | ||||||
|  |         unlock; | ||||||
|  |         return vde_AllDevicesBusy; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */ | ||||||
|  |         if ( video_devices_running[type][i] && video_devices_running[type][i]->selection == selection ) { | ||||||
|  |  | ||||||
|  |             video_devices_running[type][temp_idx] = video_devices_running[type][i]; | ||||||
|  |             video_devices_running[type][i]->ref_count++; | ||||||
|  |  | ||||||
|  |             unlock; | ||||||
|  |             return vde_None; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     VideoDevice *device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice)); | ||||||
|  |     device->selection = selection; | ||||||
|  |  | ||||||
|  |     if ( pthread_mutex_init(device->mutex, NULL) != 0 ) { | ||||||
|  |         free(device); | ||||||
|  |         unlock; | ||||||
|  |         return vde_InternalError; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ( type == vdt_input ) { | ||||||
|  |         video_thread_paused = true; | ||||||
|  |  | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  |         /* Open selected device */ | ||||||
|  |         char device_address[] = "/dev/videoXX"; | ||||||
|  |         snprintf(device_address + 10 , sizeof(device_address) - 10, "%i", selection); | ||||||
|  |  | ||||||
|  |         device->fd = open(device_address, O_RDWR); | ||||||
|  |  | ||||||
|  |         if ( device->fd == -1 ) { | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /* Obtain video device capabilities */ | ||||||
|  |         struct v4l2_capability cap; | ||||||
|  |  | ||||||
|  |         if ( -1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap) ) { | ||||||
|  |             close(device->fd); | ||||||
|  |             free(device); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /* Setup video format */ | ||||||
|  |         struct v4l2_format fmt; | ||||||
|  |         memset(&(fmt), 0, sizeof(fmt)); | ||||||
|  |  | ||||||
|  |         fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|  |         fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; | ||||||
|  |  | ||||||
|  |         if ( -1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt) ) { | ||||||
|  |             close(device->fd); | ||||||
|  |             free(device); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         device->video_width = fmt.fmt.pix.width; | ||||||
|  |         device->video_height = fmt.fmt.pix.height; | ||||||
|  |  | ||||||
|  |         /* Request buffers */ | ||||||
|  |         struct v4l2_requestbuffers req; | ||||||
|  |         memset(&(req), 0, sizeof(req)); | ||||||
|  |         req.count = 4; | ||||||
|  |         req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|  |         req.memory = V4L2_MEMORY_MMAP; | ||||||
|  |  | ||||||
|  |         if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) { | ||||||
|  |             close(device->fd); | ||||||
|  |             free(device); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if ( req.count < 2 ) { | ||||||
|  |             close(device->fd); | ||||||
|  |             free(device); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         device->buffers = calloc(req.count, sizeof(struct VideoBuffer)); | ||||||
|  |  | ||||||
|  |         for (i = 0; i < req.count; ++i) { | ||||||
|  |             struct v4l2_buffer buf; | ||||||
|  |             memset(&(buf), 0, sizeof(buf)); | ||||||
|  |  | ||||||
|  |             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|  |             buf.memory = V4L2_MEMORY_MMAP; | ||||||
|  |             buf.index = i; | ||||||
|  |  | ||||||
|  |             if ( -1 == xioctl(device->fd, VIDIOC_QUERYBUF, &buf) ) { | ||||||
|  |                 close(device->fd); | ||||||
|  |                 free(device); | ||||||
|  |                 unlock; | ||||||
|  |                 return vde_FailedStart; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             device->buffers[i].length = buf.length; | ||||||
|  |             device->buffers[i].start = mmap(NULL /* start anywhere */, | ||||||
|  |                                             buf.length, | ||||||
|  |                                             PROT_READ | PROT_WRITE /* required */, | ||||||
|  |                                             MAP_SHARED /* recommended */, | ||||||
|  |                                             device->fd, buf.m.offset); | ||||||
|  |  | ||||||
|  |             if ( MAP_FAILED == device->buffers[i].start ) { | ||||||
|  |                 for (i = 0; i < buf.index; ++i) | ||||||
|  |                     munmap(device->buffers[i].start, device->buffers[i].length); | ||||||
|  |  | ||||||
|  |                 close(device->fd); | ||||||
|  |                 free(device); | ||||||
|  |                 unlock; | ||||||
|  |                 return vde_FailedStart; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         device->n_buffers = i; | ||||||
|  |  | ||||||
|  |         enum v4l2_buf_type type; | ||||||
|  |  | ||||||
|  |         for (i = 0; i < device->n_buffers; ++i) { | ||||||
|  |             struct v4l2_buffer buf; | ||||||
|  |             memset(&(buf), 0, sizeof(buf)); | ||||||
|  |  | ||||||
|  |             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|  |             buf.memory = V4L2_MEMORY_MMAP; | ||||||
|  |             buf.index = i; | ||||||
|  |  | ||||||
|  |             if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) { | ||||||
|  |                 for (i = 0; i < device->n_buffers; ++i) | ||||||
|  |                     munmap(device->buffers[i].start, device->buffers[i].length); | ||||||
|  |  | ||||||
|  |                 close(device->fd); | ||||||
|  |                 free(device); | ||||||
|  |                 unlock; | ||||||
|  |                 return vde_FailedStart; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|  |  | ||||||
|  |         /* Turn on video stream */ | ||||||
|  |         if ( -1 == xioctl(device->fd, VIDIOC_STREAMON, &type) ) { | ||||||
|  |             close_video_device(vdt_input, temp_idx); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | #else /* __OSX__ */ | ||||||
|  |  | ||||||
|  |         if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) { | ||||||
|  |             free(device); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |         /* Create X11 window associated to device */ | ||||||
|  |         if ( (device->x_display = XOpenDisplay(NULL)) == NULL ) { | ||||||
|  |             close_video_device(vdt_input, temp_idx); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         int screen = DefaultScreen(device->x_display); | ||||||
|  |  | ||||||
|  |         if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0, | ||||||
|  |                                   device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen), | ||||||
|  |                                   BlackPixel(device->x_display, screen))) ) { | ||||||
|  |             close_video_device(vdt_input, temp_idx); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         XStoreName(device->x_display, device->x_window, "Video Preview"); | ||||||
|  |         XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask); | ||||||
|  |  | ||||||
|  |         if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) { | ||||||
|  |             close_video_device(vdt_input, temp_idx); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /* Disable user from manually closing the X11 window */ | ||||||
|  |         Atom wm_delete_window = XInternAtom(device->x_display, "WM_DELETE_WINDOW", false); | ||||||
|  |         XSetWMProtocols(device->x_display, device->x_window, &wm_delete_window, 1); | ||||||
|  |  | ||||||
|  |         XMapWindow(device->x_display, device->x_window); | ||||||
|  |         XClearWindow(device->x_display, device->x_window); | ||||||
|  |         XMapRaised(device->x_display, device->x_window); | ||||||
|  |         XFlush(device->x_display); | ||||||
|  |  | ||||||
|  |         vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1); | ||||||
|  |  | ||||||
|  |         video_thread_paused = false; | ||||||
|  |     } else { /* vdt_output */ | ||||||
|  |  | ||||||
|  |         /* Create X11 window associated to device */ | ||||||
|  |         if ( (device->x_display = XOpenDisplay(NULL)) == NULL ) { | ||||||
|  |             close_video_device(vdt_output, temp_idx); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         int screen = DefaultScreen(device->x_display); | ||||||
|  |  | ||||||
|  |         if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0, | ||||||
|  |                                   100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) { | ||||||
|  |             close_video_device(vdt_output, temp_idx); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         XStoreName(device->x_display, device->x_window, "Video Receive"); | ||||||
|  |         XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask); | ||||||
|  |  | ||||||
|  |         if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) { | ||||||
|  |             close_video_device(vdt_output, temp_idx); | ||||||
|  |             unlock; | ||||||
|  |             return vde_FailedStart; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /* Disable user from manually closing the X11 window */ | ||||||
|  |         Atom wm_delete_window = XInternAtom(device->x_display, "WM_DELETE_WINDOW", false); | ||||||
|  |         XSetWMProtocols(device->x_display, device->x_window, &wm_delete_window, 1); | ||||||
|  |  | ||||||
|  |         XMapWindow(device->x_display, device->x_window); | ||||||
|  |         XClearWindow(device->x_display, device->x_window); | ||||||
|  |         XMapRaised(device->x_display, device->x_window); | ||||||
|  |         XFlush(device->x_display); | ||||||
|  |  | ||||||
|  |         vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     *device_idx = temp_idx; | ||||||
|  |     unlock; | ||||||
|  |  | ||||||
|  |     return vde_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height, | ||||||
|  |         uint8_t const *y, uint8_t const *u, uint8_t const *v, | ||||||
|  |         int32_t ystride, int32_t ustride, int32_t vstride, | ||||||
|  |         void *user_data) | ||||||
|  | { | ||||||
|  |     VideoDevice *device = video_devices_running[vdt_output][0]; | ||||||
|  |  | ||||||
|  |     if ( !device ) return vde_DeviceNotActive; | ||||||
|  |  | ||||||
|  |     if ( !device->x_window ) return vde_DeviceNotActive; | ||||||
|  |  | ||||||
|  |     pthread_mutex_lock(device->mutex); | ||||||
|  |  | ||||||
|  |     /* Resize X11 window to correct size */ | ||||||
|  |     if ( device->video_width != width || device->video_height != height ) { | ||||||
|  |         device->video_width = width; | ||||||
|  |         device->video_height = height; | ||||||
|  |         XResizeWindow(device->x_display, device->x_window, width, height); | ||||||
|  |  | ||||||
|  |         vpx_img_free(&device->input); | ||||||
|  |         vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, width, height, 1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* Convert YUV420 data to BGR */ | ||||||
|  |     ystride = abs(ystride); | ||||||
|  |     ustride = abs(ustride); | ||||||
|  |     vstride = abs(vstride); | ||||||
|  |     uint8_t *img_data = malloc(width * height * 4); | ||||||
|  |     yuv420tobgr(width, height, y, u, v, ystride, ustride, vstride, img_data); | ||||||
|  |  | ||||||
|  |     /* Allocate image data in X11 */ | ||||||
|  |     XImage image = { | ||||||
|  |         .width = width, | ||||||
|  |         .height = height, | ||||||
|  |         .depth = 24, | ||||||
|  |         .bits_per_pixel = 32, | ||||||
|  |         .format = ZPixmap, | ||||||
|  |         .byte_order = LSBFirst, | ||||||
|  |         .bitmap_unit = 8, | ||||||
|  |         .bitmap_bit_order = LSBFirst, | ||||||
|  |         .bytes_per_line = width * 4, | ||||||
|  |         .red_mask = 0xFF0000, | ||||||
|  |         .green_mask = 0xFF00, | ||||||
|  |         .blue_mask = 0xFF, | ||||||
|  |         .data = (char *)img_data | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /* Render image data */ | ||||||
|  |     Pixmap pixmap = XCreatePixmap(device->x_display, device->x_window, width, height, 24); | ||||||
|  |     XPutImage(device->x_display, pixmap, device->x_gc, &image, 0, 0, 0, 0, width, height); | ||||||
|  |     XCopyArea(device->x_display, pixmap, device->x_window, device->x_gc, 0, 0, width, height, 0, 0); | ||||||
|  |     XFreePixmap(device->x_display, pixmap); | ||||||
|  |     XFlush(device->x_display); | ||||||
|  |     free(img_data); | ||||||
|  |  | ||||||
|  |     pthread_mutex_unlock(device->mutex); | ||||||
|  |     return vde_None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void *video_thread_poll (void *arg) // TODO: maybe use thread for every input source | ||||||
|  | { | ||||||
|  |     /* | ||||||
|  |      * NOTE: We only need to poll input devices for data. | ||||||
|  |      */ | ||||||
|  |     (void)arg; | ||||||
|  |     uint32_t i; | ||||||
|  |  | ||||||
|  |     while (1) { | ||||||
|  |         lock; | ||||||
|  |  | ||||||
|  |         if (!video_thread_running) { | ||||||
|  |             unlock; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         unlock; | ||||||
|  |  | ||||||
|  |         if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */ | ||||||
|  |         else { | ||||||
|  |             for (i = 0; i < size[vdt_input]; ++i) { | ||||||
|  |                 lock; | ||||||
|  |  | ||||||
|  |                 if ( video_devices_running[vdt_input][i] != NULL ) { | ||||||
|  |                     /* Obtain frame image data from device buffers */ | ||||||
|  |                     VideoDevice *device = video_devices_running[vdt_input][i]; | ||||||
|  |                     uint16_t video_width = device->video_width; | ||||||
|  |                     uint16_t video_height = device->video_height; | ||||||
|  |                     uint8_t *y = device->input.planes[0]; | ||||||
|  |                     uint8_t *u = device->input.planes[1]; | ||||||
|  |                     uint8_t *v = device->input.planes[2]; | ||||||
|  |  | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  |                     struct v4l2_buffer buf; | ||||||
|  |                     memset(&(buf), 0, sizeof(buf)); | ||||||
|  |  | ||||||
|  |                     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|  |                     buf.memory = V4L2_MEMORY_MMAP; | ||||||
|  |  | ||||||
|  |                     if ( -1 == ioctl(device->fd, VIDIOC_DQBUF, &buf) ) { | ||||||
|  |                         unlock; | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     void *data = (void *)device->buffers[buf.index].start; | ||||||
|  |  | ||||||
|  |                     /* Convert frame image data to YUV420 for ToxAV */ | ||||||
|  |                     yuv422to420(y, u, v, data, video_width, video_height); | ||||||
|  |  | ||||||
|  | #else /* __OSX__*/ | ||||||
|  |  | ||||||
|  |                     if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) { | ||||||
|  |                         unlock; | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |                     /* Send frame data to friend through ToxAV */ | ||||||
|  |                     if ( device->cb ) | ||||||
|  |                         device->cb(video_width, video_height, y, u, v, device->cb_data); | ||||||
|  |  | ||||||
|  |                     /* Convert YUV420 data to BGR */ | ||||||
|  |                     uint8_t *img_data = malloc(video_width * video_height * 4); | ||||||
|  |                     yuv420tobgr(video_width, video_height, y, u, v, | ||||||
|  |                                 video_width, video_width / 2, video_width / 2, img_data); | ||||||
|  |  | ||||||
|  |                     /* Allocate image data in X11 */ | ||||||
|  |                     XImage image = { | ||||||
|  |                         .width = video_width, | ||||||
|  |                         .height = video_height, | ||||||
|  |                         .depth = 24, | ||||||
|  |                         .bits_per_pixel = 32, | ||||||
|  |                         .format = ZPixmap, | ||||||
|  |                         .byte_order = LSBFirst, | ||||||
|  |                         .bitmap_unit = 8, | ||||||
|  |                         .bitmap_bit_order = LSBFirst, | ||||||
|  |                         .bytes_per_line = video_width * 4, | ||||||
|  |                         .red_mask = 0xFF0000, | ||||||
|  |                         .green_mask = 0xFF00, | ||||||
|  |                         .blue_mask = 0xFF, | ||||||
|  |                         .data = (char *)img_data | ||||||
|  |                     }; | ||||||
|  |  | ||||||
|  |                     /* Render image data */ | ||||||
|  |                     Pixmap pixmap = XCreatePixmap(device->x_display, device->x_window, video_width, video_height, 24); | ||||||
|  |                     XPutImage(device->x_display, pixmap, device->x_gc, &image, 0, 0, 0, 0, video_width, video_height); | ||||||
|  |                     XCopyArea(device->x_display, pixmap, device->x_window, device->x_gc, 0, 0, video_width, video_height, 0, 0); | ||||||
|  |                     XFreePixmap(device->x_display, pixmap); | ||||||
|  |                     XFlush(device->x_display); | ||||||
|  |                     free(img_data); | ||||||
|  |  | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  |  | ||||||
|  |                     if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) { | ||||||
|  |                         unlock; | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  | #endif /* __linux__ */ | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 unlock; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             usleep(1000 * 1000 / 24); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pthread_exit(NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx) | ||||||
|  | { | ||||||
|  |     if ( device_idx >= MAX_DEVICES ) return vde_InvalidSelection; | ||||||
|  |  | ||||||
|  |     lock; | ||||||
|  |     VideoDevice *device = video_devices_running[type][device_idx]; | ||||||
|  |     VideoDeviceError rc = vde_None; | ||||||
|  |  | ||||||
|  |     if ( !device ) { | ||||||
|  |         unlock; | ||||||
|  |         return vde_DeviceNotActive; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     video_devices_running[type][device_idx] = NULL; | ||||||
|  |  | ||||||
|  |     if ( !device->ref_count ) { | ||||||
|  |  | ||||||
|  |         if ( type == vdt_input ) { | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  |             enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||||||
|  |  | ||||||
|  |             if ( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {} | ||||||
|  |  | ||||||
|  |             int i; | ||||||
|  |  | ||||||
|  |             for (i = 0; i < device->n_buffers; ++i) { | ||||||
|  |                 if ( -1 == munmap(device->buffers[i].start, device->buffers[i].length) ) { | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             close(device->fd); | ||||||
|  |  | ||||||
|  | #else /* __OSX__ */ | ||||||
|  |             osx_video_close_device(device_idx); | ||||||
|  | #endif | ||||||
|  |             vpx_img_free(&device->input); | ||||||
|  |             XDestroyWindow(device->x_display, device->x_window); | ||||||
|  |             XFlush(device->x_display); | ||||||
|  |             XCloseDisplay(device->x_display); | ||||||
|  |             pthread_mutex_destroy(device->mutex); | ||||||
|  |  | ||||||
|  | #if defined(__linux__) || defined(__FreeBSD__) | ||||||
|  |             free(device->buffers); | ||||||
|  | #endif /* __linux__ */ | ||||||
|  |  | ||||||
|  |             free(device); | ||||||
|  |         } else { | ||||||
|  |             vpx_img_free(&device->input); | ||||||
|  |             XDestroyWindow(device->x_display, device->x_window); | ||||||
|  |             XFlush(device->x_display); | ||||||
|  |             XCloseDisplay(device->x_display); | ||||||
|  |             pthread_mutex_destroy(device->mutex); | ||||||
|  |             free(device); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } else device->ref_count--; | ||||||
|  |  | ||||||
|  |     unlock; | ||||||
|  |     return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void print_video_devices(ToxWindow *self, VideoDeviceType type) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < size[type]; ++i) | ||||||
|  |         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection) | ||||||
|  | { | ||||||
|  |     return (size[type] <= selection || selection < 0) ? vde_InvalidSelection : vde_None; | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								src/video_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/video_device.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | /*  video_device.h | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  This file is part of Toxic. | ||||||
|  |  * | ||||||
|  |  *  Toxic is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  Toxic is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef VIDEO_DEVICE_H | ||||||
|  | #define VIDEO_DEVICE_H | ||||||
|  |  | ||||||
|  | #define MAX_DEVICES 32 | ||||||
|  | #include <inttypes.h> | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
|  | typedef enum VideoDeviceType { | ||||||
|  |     vdt_input, | ||||||
|  |     vdt_output, | ||||||
|  | } VideoDeviceType; | ||||||
|  |  | ||||||
|  | typedef enum VideoDeviceError { | ||||||
|  |     vde_None, | ||||||
|  |     vde_InternalError = -1, | ||||||
|  |     vde_InvalidSelection = -2, | ||||||
|  |     vde_FailedStart = -3, | ||||||
|  |     vde_Busy = -4, | ||||||
|  |     vde_AllDevicesBusy = -5, | ||||||
|  |     vde_DeviceNotActive = -6, | ||||||
|  |     vde_BufferError = -7, | ||||||
|  |     vde_UnsupportedMode = -8, | ||||||
|  |     vde_CaptureError = -9, | ||||||
|  | } VideoDeviceError; | ||||||
|  |  | ||||||
|  | typedef void (*VideoDataHandleCallback) (int16_t width, int16_t height, const uint8_t *y, const uint8_t *u, | ||||||
|  |         const uint8_t *v, void *data); | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  | VideoDeviceError init_video_devices(ToxAV *av); | ||||||
|  | #else | ||||||
|  | VideoDeviceError init_video_devices(); | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  | VideoDeviceError terminate_video_devices(); | ||||||
|  |  | ||||||
|  | /* Callback handles ready data from INPUT device */ | ||||||
|  | VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback, | ||||||
|  |         void *data); | ||||||
|  | void *get_video_device_callback_data(uint32_t device_idx); | ||||||
|  |  | ||||||
|  | VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection); | ||||||
|  | VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx); | ||||||
|  | /* Start device */ | ||||||
|  | VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx); | ||||||
|  | /* Stop device */ | ||||||
|  | VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx); | ||||||
|  |  | ||||||
|  | /* Write data to device */ | ||||||
|  | VideoDeviceError write_video_out(uint16_t width, uint16_t height, uint8_t const *y, uint8_t const *u, uint8_t const *v, | ||||||
|  |                                  int32_t ystride, int32_t ustride, int32_t vstride, void *user_data); | ||||||
|  |  | ||||||
|  | void print_video_devices(ToxWindow *self, VideoDeviceType type); | ||||||
|  | void get_primary_video_device_name(VideoDeviceType type, char *buf, int size); | ||||||
|  |  | ||||||
|  | VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection); | ||||||
|  | #endif /* VIDEO_DEVICE_H */ | ||||||
							
								
								
									
										166
									
								
								src/windows.c
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								src/windows.c
									
									
									
									
									
								
							| @@ -151,8 +151,18 @@ void on_friendadded(Tox *m, uint32_t friendnumber, bool sort) | |||||||
|     store_data(m, DATA_FILE); |     store_data(m, DATA_FILE); | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, | void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length, void *userdata) | ||||||
|                      void *userdata) | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupInvite != NULL) | ||||||
|  |             windows[i].onGroupInvite(&windows[i], m, friendnumber, (char *) invite_data, length); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type, | ||||||
|  |                       const uint8_t *message, size_t length, void *userdata) | ||||||
| { | { | ||||||
|     char msg[MAX_STR_SIZE + 1]; |     char msg[MAX_STR_SIZE + 1]; | ||||||
|     length = copy_tox_str(msg, sizeof(msg), (const char *) message, length); |     length = copy_tox_str(msg, sizeof(msg), (const char *) message, length); | ||||||
| @@ -161,56 +171,151 @@ void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *mes | |||||||
|  |  | ||||||
|     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i].onGroupMessage != NULL) |         if (windows[i].onGroupMessage != NULL) | ||||||
|             windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, msg, length); |             windows[i].onGroupMessage(&windows[i], m, groupnumber, peer_id, type, msg, length); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, | void on_group_private_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *message, | ||||||
|                     void *userdata) |                               size_t length, void *userdata) | ||||||
| { | { | ||||||
|     char msg[MAX_STR_SIZE + 1]; |     char msg[MAX_STR_SIZE + 1]; | ||||||
|     length = copy_tox_str(msg, sizeof(msg), (const char *) action, length); |     length = copy_tox_str(msg, sizeof(msg), (const char *) message, length); | ||||||
|  |  | ||||||
|     size_t i; |     size_t i; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i].onGroupAction != NULL) |         if (windows[i].onGroupPrivateMessage != NULL) | ||||||
|             windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, msg, length); |             windows[i].onGroupPrivateMessage(&windows[i], m, groupnumber, peer_id, msg, length); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length, | void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_USER_STATUS status, void *userdata) | ||||||
|                     void *userdata) |  | ||||||
| { | { | ||||||
|     size_t i; |     size_t i; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i].onGroupInvite != NULL) |         if (windows[i].onGroupStatusChange != NULL) | ||||||
|             windows[i].onGroupInvite(&windows[i], m, friendnumber, type, (char *) group_pub_key, length); |             windows[i].onGroupStatusChange(&windows[i], m, groupnumber, peer_id, status); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata) | void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peer_id, void *userdata) | ||||||
| { | { | ||||||
|     size_t i; |     size_t i; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i].onGroupNamelistChange != NULL) |         if (windows[i].onGroupPeerJoin != NULL) | ||||||
|             windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change); |             windows[i].onGroupPeerJoin(&windows[i], m, groupnumber, peer_id); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, | void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *partmsg, size_t length, | ||||||
|                           void *userdata) |                         void *userdata) | ||||||
|  | { | ||||||
|  |     char msg[MAX_STR_SIZE + 1]; | ||||||
|  |  | ||||||
|  |     if (length == 0 || !partmsg) { | ||||||
|  |         strcpy(msg, "Quit"); | ||||||
|  |         length = strlen(msg); | ||||||
|  |     } else { | ||||||
|  |         length = copy_tox_str(msg, sizeof(msg), (const char *) partmsg, length); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupPeerExit != NULL) | ||||||
|  |             windows[i].onGroupPeerExit(&windows[i], m, groupnumber, peer_id, msg, length); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_topic_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *topic, size_t length, | ||||||
|  |                            void *userdata) | ||||||
| { | { | ||||||
|     char data[MAX_STR_SIZE + 1]; |     char data[MAX_STR_SIZE + 1]; | ||||||
|     length = copy_tox_str(data, sizeof(data), (const char *) title, length); |     length = copy_tox_str(data, sizeof(data), (const char *) topic, length); | ||||||
|  |  | ||||||
|     size_t i; |     size_t i; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|         if (windows[i].onGroupTitleChange != NULL) |         if (windows[i].onGroupTopicChange != NULL) | ||||||
|             windows[i].onGroupTitleChange(&windows[i], m, groupnumber, peernumber, data, length); |             windows[i].onGroupTopicChange(&windows[i], m, groupnumber, peer_id, data, length); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_peer_limit(Tox *m, uint32_t groupnumber, uint32_t peer_limit, void *userdata) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupPeerLimit != NULL) | ||||||
|  |             windows[i].onGroupPeerLimit(&windows[i], m, groupnumber, peer_limit); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_privacy_state(Tox *m, uint32_t groupnumber, TOX_GROUP_PRIVACY_STATE privacy_state, void *userdata) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupPrivacyState != NULL) | ||||||
|  |             windows[i].onGroupPrivacyState(&windows[i], m, groupnumber, privacy_state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_password(Tox *m, uint32_t groupnumber, const uint8_t *password, size_t length, void *userdata) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupPassword != NULL) | ||||||
|  |             windows[i].onGroupPassword(&windows[i], m, groupnumber, (char *) password, length); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_nick_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *newname, size_t length, | ||||||
|  |                           void *userdata) | ||||||
|  | { | ||||||
|  |     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|  |     length = copy_tox_str(name, sizeof(name), (const char *) newname, length); | ||||||
|  |     filter_str(name, length); | ||||||
|  |  | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupNickChange != NULL) | ||||||
|  |             windows[i].onGroupNickChange(&windows[i], m, groupnumber, peer_id, name, length); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_self_join(Tox *m, uint32_t groupnumber, void *userdata) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupSelfJoin != NULL) | ||||||
|  |             windows[i].onGroupSelfJoin(&windows[i], m, groupnumber); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_rejected(Tox *m, uint32_t groupnumber, TOX_GROUP_JOIN_FAIL type, void *userdata) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupRejected != NULL) | ||||||
|  |             windows[i].onGroupRejected(&windows[i], m, groupnumber, type); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void on_group_moderation(Tox *m, uint32_t groupnumber, uint32_t source_peer_id, uint32_t target_peer_id, | ||||||
|  |                          TOX_GROUP_MOD_EVENT type, void *userdata) | ||||||
|  | { | ||||||
|  |     size_t i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
|  |         if (windows[i].onGroupModeration != NULL) | ||||||
|  |             windows[i].onGroupModeration(&windows[i], m, groupnumber, source_peer_id, target_peer_id, type); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -400,6 +505,9 @@ void on_window_resize(void) | |||||||
|     getmaxyx(stdscr, y2, x2); |     getmaxyx(stdscr, y2, x2); | ||||||
|     y2 -= 2; |     y2 -= 2; | ||||||
|  |  | ||||||
|  |     if (y2 <= 0 || x2 <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     size_t i; |     size_t i; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { |     for (i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||||
| @@ -442,10 +550,12 @@ void on_window_resize(void) | |||||||
|         } |         } | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|         if (w->chatwin->infobox.active) { |         if (w->chatwin->infobox.active) { | ||||||
|             delwin(w->chatwin->infobox.win); |             delwin(w->chatwin->infobox.win); | ||||||
|             w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH); |             w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| #endif   /* AUDIO */ | #endif   /* AUDIO */ | ||||||
|  |  | ||||||
|         scrollok(w->chatwin->history, 0); |         scrollok(w->chatwin->history, 0); | ||||||
| @@ -455,19 +565,28 @@ void on_window_resize(void) | |||||||
| static void draw_window_tab(ToxWindow *toxwin) | static void draw_window_tab(ToxWindow *toxwin) | ||||||
| { | { | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |  | ||||||
|     if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert)); |     if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert)); | ||||||
|  |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     clrtoeol(); |     clrtoeol(); | ||||||
|     printw(" [%s]", toxwin->name); |     printw(" [%s]", toxwin->name); | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |  | ||||||
|     if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert)); |     if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert)); | ||||||
|  |  | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void draw_bar(void) | static void draw_bar(void) | ||||||
| { | { | ||||||
|  |     int y, x; | ||||||
|  |  | ||||||
|  |     // save current cursor position | ||||||
|  |     getyx(active_window->window, y, x); | ||||||
|  |  | ||||||
|     attron(COLOR_PAIR(BLUE)); |     attron(COLOR_PAIR(BLUE)); | ||||||
|     mvhline(LINES - 2, 0, '_', COLS); |     mvhline(LINES - 2, 0, '_', COLS); | ||||||
|     attroff(COLOR_PAIR(BLUE)); |     attroff(COLOR_PAIR(BLUE)); | ||||||
| @@ -505,14 +624,16 @@ static void draw_bar(void) | |||||||
|             attroff(A_BOLD); |             attroff(A_BOLD); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // restore cursor position after drawing | ||||||
|  |     move(y, x); | ||||||
|  |  | ||||||
|     refresh(); |     refresh(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void draw_active_window(Tox *m) | void draw_active_window(Tox *m) | ||||||
| { | { | ||||||
|     ToxWindow *a = active_window; |  | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     pthread_mutex_lock(&Winthread.lock); | ||||||
|  |     ToxWindow *a = active_window; | ||||||
|     a->alert = WINDOW_ALERT_NONE; |     a->alert = WINDOW_ALERT_NONE; | ||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
| @@ -522,6 +643,7 @@ void draw_active_window(Tox *m) | |||||||
|  |  | ||||||
|     touchwin(a->window); |     touchwin(a->window); | ||||||
|     a->onDraw(a, m); |     a->onDraw(a, m); | ||||||
|  |     wrefresh(a->window); | ||||||
|  |  | ||||||
|     /* Handle input */ |     /* Handle input */ | ||||||
|     bool ltr; |     bool ltr; | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ struct cqueue_thread { | |||||||
|     pthread_t tid; |     pthread_t tid; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct audio_thread { | struct av_thread { | ||||||
|     pthread_t tid; |     pthread_t tid; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -90,7 +90,7 @@ struct arg_opts { | |||||||
|     bool encrypt_data; |     bool encrypt_data; | ||||||
|     bool unencrypt_data; |     bool unencrypt_data; | ||||||
|  |  | ||||||
|     char dns_path[MAX_STR_SIZE]; |     char nameserver_path[MAX_STR_SIZE]; | ||||||
|     char config_path[MAX_STR_SIZE]; |     char config_path[MAX_STR_SIZE]; | ||||||
|     char nodes_path[MAX_STR_SIZE]; |     char nodes_path[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
| @@ -121,11 +121,6 @@ struct ToxWindow { | |||||||
|     void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t); |     void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t); | ||||||
|     void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS); |     void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS); | ||||||
|     void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t); |     void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t); | ||||||
|     void(*onGroupMessage)(ToxWindow *, Tox *, int, int, const char *, uint16_t); |  | ||||||
|     void(*onGroupAction)(ToxWindow *, Tox *, int, int, const char *, uint16_t); |  | ||||||
|     void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t); |  | ||||||
|     void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t); |  | ||||||
|     void(*onGroupTitleChange)(ToxWindow *, Tox *, int, int, const char *, uint8_t); |  | ||||||
|     void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t); |     void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t); | ||||||
|     void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); |     void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); | ||||||
|     void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL); |     void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL); | ||||||
| @@ -133,26 +128,44 @@ struct ToxWindow { | |||||||
|     void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool); |     void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool); | ||||||
|     void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t); |     void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t); | ||||||
|  |  | ||||||
|  |     void(*onGroupInvite)(ToxWindow *, Tox *, uint32_t, const char *, size_t); | ||||||
|  |     void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t); | ||||||
|  |     void(*onGroupPrivateMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); | ||||||
|  |     void(*onGroupPeerJoin)(ToxWindow *, Tox *, uint32_t, uint32_t); | ||||||
|  |     void(*onGroupPeerExit)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); | ||||||
|  |     void(*onGroupNickChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); | ||||||
|  |     void(*onGroupStatusChange)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_USER_STATUS); | ||||||
|  |     void(*onGroupTopicChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); | ||||||
|  |     void(*onGroupPeerLimit)(ToxWindow *, Tox *, uint32_t, uint32_t); | ||||||
|  |     void(*onGroupPrivacyState)(ToxWindow *, Tox *, uint32_t, TOX_GROUP_PRIVACY_STATE); | ||||||
|  |     void(*onGroupPassword)(ToxWindow *, Tox *, uint32_t, const char *, size_t); | ||||||
|  |     void(*onGroupSelfJoin)(ToxWindow *, Tox *, uint32_t); | ||||||
|  |     void(*onGroupRejected)(ToxWindow *, Tox *, uint32_t, TOX_GROUP_JOIN_FAIL); | ||||||
|  |     void(*onGroupModeration)(ToxWindow *, Tox *, uint32_t, uint32_t, uint32_t, TOX_GROUP_MOD_EVENT); | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|     void(*onInvite)(ToxWindow *, ToxAv *, int); |     void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onRinging)(ToxWindow *, ToxAv *, int); |     void(*onRinging)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onStarting)(ToxWindow *, ToxAv *, int); |     void(*onStarting)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onEnding)(ToxWindow *, ToxAv *, int); |     void(*onEnding)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onError)(ToxWindow *, ToxAv *, int); |     void(*onError)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onStart)(ToxWindow *, ToxAv *, int); |     void(*onStart)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onCancel)(ToxWindow *, ToxAv *, int); |     void(*onCancel)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onReject)(ToxWindow *, ToxAv *, int); |     void(*onReject)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onEnd)(ToxWindow *, ToxAv *, int); |     void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int); | ||||||
|     void(*onRequestTimeout)(ToxWindow *, ToxAv *, int); |     void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, int, const int16_t *, unsigned int, uint8_t, unsigned int); | ||||||
|     void(*onPeerTimeout)(ToxWindow *, ToxAv *, int); |  | ||||||
|     void(*onWriteDevice)(ToxWindow *, Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int); |  | ||||||
|  |  | ||||||
|     int call_idx; /* If in a call will have this index set, otherwise it's -1. |  | ||||||
|                    * Don't modify outside av callbacks. */ |  | ||||||
|     int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */ |     int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */ | ||||||
|  |     bool is_call; | ||||||
|     int ringing_sound; |     int ringing_sound; | ||||||
|  |  | ||||||
|  | #ifdef VIDEO | ||||||
|  |  | ||||||
|  |     int video_device_selection[2]; /* -1 if not set, if set uses these selections instead of primary video device */ | ||||||
|  |  | ||||||
|  | #endif /* VIDEO */ | ||||||
|  |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
|     int active_box; /* For box notify */ |     int active_box; /* For box notify */ | ||||||
| @@ -201,8 +214,8 @@ struct infobox { | |||||||
|     bool hide; |     bool hide; | ||||||
|     bool active; |     bool active; | ||||||
|  |  | ||||||
|     uint64_t lastupdate; |     time_t lastupdate; | ||||||
|     uint64_t starttime; |     time_t starttime; | ||||||
|     char timestr[TIME_STR_SIZE]; |     char timestr[TIME_STR_SIZE]; | ||||||
|  |  | ||||||
|     WINDOW *win; |     WINDOW *win; | ||||||
| @@ -234,6 +247,7 @@ struct ChatContext { | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     uint8_t self_is_typing; |     uint8_t self_is_typing; | ||||||
|  |     uint8_t pastemode; /* whether to translate \r to \n */ | ||||||
|  |  | ||||||
|     WINDOW *history; |     WINDOW *history; | ||||||
|     WINDOW *linewin; |     WINDOW *linewin; | ||||||
|   | |||||||
							
								
								
									
										66
									
								
								src/xtra.c
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								src/xtra.c
									
									
									
									
									
								
							| @@ -57,8 +57,7 @@ struct _Xtra { | |||||||
|     Atom expecting_type; |     Atom expecting_type; | ||||||
| } Xtra; | } Xtra; | ||||||
|  |  | ||||||
| typedef struct _Property | typedef struct _Property { | ||||||
| { |  | ||||||
|     unsigned char *data; |     unsigned char *data; | ||||||
|     int            read_format; |     int            read_format; | ||||||
|     unsigned long  read_num; |     unsigned long  read_num; | ||||||
| @@ -97,21 +96,23 @@ Property read_property(Window s, Atom p) | |||||||
| Atom get_dnd_type(long *a, int l) | Atom get_dnd_type(long *a, int l) | ||||||
| { | { | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |  | ||||||
|     for (; i < l; i ++) { |     for (; i < l; i ++) { | ||||||
|         if (a[i] != XtraNil) return a[i]; /* Get first valid */ |         if (a[i] != XtraNil) return a[i]; /* Get first valid */ | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return XtraNil; |     return XtraNil; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* TODO maybe support only certain types in the future */ | /* TODO maybe support only certain types in the future */ | ||||||
| static void handle_xdnd_enter(XClientMessageEvent* e) | static void handle_xdnd_enter(XClientMessageEvent *e) | ||||||
| { | { | ||||||
|     Xtra.handling_version = (e->data.l[1] >> 24); |     Xtra.handling_version = (e->data.l[1] >> 24); | ||||||
|  |  | ||||||
|     if ((e->data.l[1] & 1)) { |     if ((e->data.l[1] & 1)) { | ||||||
|         // Fetch the list of possible conversions |         // Fetch the list of possible conversions | ||||||
|         Property p = read_property(e->data.l[0], XdndTypeList); |         Property p = read_property(e->data.l[0], XdndTypeList); | ||||||
|         Xtra.expecting_type = get_dnd_type((long*)p.data, p.read_num); |         Xtra.expecting_type = get_dnd_type((long *)p.data, p.read_num); | ||||||
|         XFree(p.data); |         XFree(p.data); | ||||||
|     } else { |     } else { | ||||||
|         // Use the available list |         // Use the available list | ||||||
| @@ -119,7 +120,7 @@ static void handle_xdnd_enter(XClientMessageEvent* e) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void handle_xdnd_position(XClientMessageEvent* e) | static void handle_xdnd_position(XClientMessageEvent *e) | ||||||
| { | { | ||||||
|     XEvent ev = { |     XEvent ev = { | ||||||
|         .xclient = { |         .xclient = { | ||||||
| @@ -143,7 +144,7 @@ static void handle_xdnd_position(XClientMessageEvent* e) | |||||||
|     XFlush(Xtra.display); |     XFlush(Xtra.display); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void handle_xdnd_drop(XClientMessageEvent* e) | static void handle_xdnd_drop(XClientMessageEvent *e) | ||||||
| { | { | ||||||
|     /* Not expecting any type */ |     /* Not expecting any type */ | ||||||
|     if (Xtra.expecting_type == XtraNil) { |     if (Xtra.expecting_type == XtraNil) { | ||||||
| @@ -172,7 +173,7 @@ static void handle_xdnd_drop(XClientMessageEvent* e) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void handle_xdnd_selection(XSelectionEvent* e) | static void handle_xdnd_selection(XSelectionEvent *e) | ||||||
| { | { | ||||||
|     /* DnD succesfully finished, send finished and call callback */ |     /* DnD succesfully finished, send finished and call callback */ | ||||||
|     XEvent ev = { |     XEvent ev = { | ||||||
| @@ -199,12 +200,12 @@ static void handle_xdnd_selection(XSelectionEvent* e) | |||||||
|  |  | ||||||
|  |  | ||||||
|     /* Call callback for every entry */ |     /* Call callback for every entry */ | ||||||
|     if (Xtra.on_drop && p.read_num) |     if (Xtra.on_drop && p.read_num) { | ||||||
|     { |  | ||||||
|         char *sptr; |         char *sptr; | ||||||
|         char *str = strtok_r((char *) p.data, "\n\r", &sptr); |         char *str = strtok_r((char *) p.data, "\n\r", &sptr); | ||||||
|  |  | ||||||
|         if (str) Xtra.on_drop(str, dt); |         if (str) Xtra.on_drop(str, dt); | ||||||
|  |  | ||||||
|         while ((str = strtok_r(NULL, "\n\r", &sptr))) |         while ((str = strtok_r(NULL, "\n\r", &sptr))) | ||||||
|             Xtra.on_drop(str, dt); |             Xtra.on_drop(str, dt); | ||||||
|     } |     } | ||||||
| @@ -212,7 +213,7 @@ static void handle_xdnd_selection(XSelectionEvent* e) | |||||||
|     if (p.data) XFree(p.data); |     if (p.data) XFree(p.data); | ||||||
| } | } | ||||||
|  |  | ||||||
| void *event_loop(void* p) | void *event_loop(void *p) | ||||||
| { | { | ||||||
|     /* Handle events like a real nigga */ |     /* Handle events like a real nigga */ | ||||||
|  |  | ||||||
| @@ -221,30 +222,27 @@ void *event_loop(void* p) | |||||||
|     XEvent event; |     XEvent event; | ||||||
|     int pending; |     int pending; | ||||||
|  |  | ||||||
|     while (Xtra.display) |     while (Xtra.display) { | ||||||
|     { |  | ||||||
|         /* NEEDMOEVENTSFODEMPROGRAMS */ |         /* NEEDMOEVENTSFODEMPROGRAMS */ | ||||||
|  |  | ||||||
|         XLockDisplay(Xtra.display); |         XLockDisplay(Xtra.display); | ||||||
|         if((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event); |  | ||||||
|  |  | ||||||
|         if (!pending) |         if ((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event); | ||||||
|         { |  | ||||||
|  |         if (!pending) { | ||||||
|             XUnlockDisplay(Xtra.display); |             XUnlockDisplay(Xtra.display); | ||||||
|             usleep(10000); |             usleep(10000); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (event.type == ClientMessage) |         if (event.type == ClientMessage) { | ||||||
|         { |  | ||||||
|             Atom type = event.xclient.message_type; |             Atom type = event.xclient.message_type; | ||||||
|  |  | ||||||
|             if      (type == XdndEnter)         handle_xdnd_enter(&event.xclient); |             if      (type == XdndEnter)         handle_xdnd_enter(&event.xclient); | ||||||
|             else if (type == XdndPosition)      handle_xdnd_position(&event.xclient); |             else if (type == XdndPosition)      handle_xdnd_position(&event.xclient); | ||||||
|             else if (type == XdndDrop)          handle_xdnd_drop(&event.xclient); |             else if (type == XdndDrop)          handle_xdnd_drop(&event.xclient); | ||||||
|             else if (type == XtraTerminate)     break; |             else if (type == XtraTerminate)     break; | ||||||
|         } |         } else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection); | ||||||
|         else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection); |  | ||||||
|         /* AINNOBODYCANHANDLEDEMEVENTS*/ |         /* AINNOBODYCANHANDLEDEMEVENTS*/ | ||||||
|         else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event); |         else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event); | ||||||
|  |  | ||||||
| @@ -256,6 +254,7 @@ void *event_loop(void* p) | |||||||
|      * otherwise HEWUSAGUDBOI happens |      * otherwise HEWUSAGUDBOI happens | ||||||
|      */ |      */ | ||||||
|     if (Xtra.display) XCloseDisplay(Xtra.display); |     if (Xtra.display) XCloseDisplay(Xtra.display); | ||||||
|  |  | ||||||
|     return (Xtra.display = NULL); |     return (Xtra.display = NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -267,10 +266,16 @@ int init_xtra(drop_callback d) | |||||||
|     else Xtra.on_drop = d; |     else Xtra.on_drop = d; | ||||||
|  |  | ||||||
|     XInitThreads(); |     XInitThreads(); | ||||||
|  |  | ||||||
|     if ( !(Xtra.display = XOpenDisplay(NULL))) return -1; |     if ( !(Xtra.display = XOpenDisplay(NULL))) return -1; | ||||||
|  |  | ||||||
|     Xtra.terminal_window = focused_window_id(); |     Xtra.terminal_window = focused_window_id(); | ||||||
|  |  | ||||||
|  |     /* OSX: if focused window is 0, it means toxic is ran from | ||||||
|  |      * native terminal and not X11 terminal window, silently exit */ | ||||||
|  |     if (!Xtra.terminal_window) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|     { |     { | ||||||
|         /* Create an invisible window which will act as proxy for the DnD operation. */ |         /* Create an invisible window which will act as proxy for the DnD operation. */ | ||||||
|         XSetWindowAttributes attr  = {0}; |         XSetWindowAttributes attr  = {0}; | ||||||
| @@ -295,15 +300,15 @@ int init_xtra(drop_callback d) | |||||||
|                      &root, &x, &y, &wht, &hht, &b, &d); |                      &root, &x, &y, &wht, &hht, &b, &d); | ||||||
|  |  | ||||||
|         if (! (Xtra.proxy_window = XCreateWindow |         if (! (Xtra.proxy_window = XCreateWindow | ||||||
|                         (Xtra.display, Xtra.terminal_window,       /* Parent */ |                                    (Xtra.display, Xtra.terminal_window,       /* Parent */ | ||||||
|                          0, 0,                                     /* Position */ |                                     0, 0,                                     /* Position */ | ||||||
|                          wht, hht,                                 /* Width + height */ |                                     wht, hht,                                 /* Width + height */ | ||||||
|                          0,                                        /* Border width */ |                                     0,                                        /* Border width */ | ||||||
|                          CopyFromParent,                           /* Depth */ |                                     CopyFromParent,                           /* Depth */ | ||||||
|                          InputOnly,                                /* Class */ |                                     InputOnly,                                /* Class */ | ||||||
|                          CopyFromParent,                           /* Visual */ |                                     CopyFromParent,                           /* Visual */ | ||||||
|                          CWEventMask | CWCursor,                   /* Value mask */ |                                     CWEventMask | CWCursor,                   /* Value mask */ | ||||||
|                          &attr)) )                                 /* Attributes for value mask */ |                                     &attr)) )                                 /* Attributes for value mask */ | ||||||
|             return -1; |             return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -330,9 +335,10 @@ int init_xtra(drop_callback d) | |||||||
|                     XA_ATOM, |                     XA_ATOM, | ||||||
|                     32, |                     32, | ||||||
|                     PropModeReplace, |                     PropModeReplace, | ||||||
|                     (unsigned char*)&XdndVersion, 1); |                     (unsigned char *)&XdndVersion, 1); | ||||||
|  |  | ||||||
|     pthread_t id; |     pthread_t id; | ||||||
|  |  | ||||||
|     if (pthread_create(&id, NULL, event_loop, NULL) != 0) |     if (pthread_create(&id, NULL, event_loop, NULL) != 0) | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
| @@ -343,7 +349,7 @@ int init_xtra(drop_callback d) | |||||||
|  |  | ||||||
| void terminate_xtra() | void terminate_xtra() | ||||||
| { | { | ||||||
|     if (!Xtra.display) return; |     if (!Xtra.display || !Xtra.terminal_window) return; | ||||||
|  |  | ||||||
|     XEvent terminate = { |     XEvent terminate = { | ||||||
|         .xclient = { |         .xclient = { | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ typedef enum { | |||||||
| } | } | ||||||
| DropType; | DropType; | ||||||
|  |  | ||||||
| typedef void (*drop_callback) (const char*, DropType); | typedef void (*drop_callback) (const char *, DropType); | ||||||
|  |  | ||||||
| int               init_xtra(drop_callback d); | int               init_xtra(drop_callback d); | ||||||
| void              terminate_xtra(); | void              terminate_xtra(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user