mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-11-04 00:36: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)*
 | 
			
		||||
@@ -10,11 +10,13 @@
 | 
			
		||||
<a name="deps" />
 | 
			
		||||
## Dependencies
 | 
			
		||||
| Name                                                 | Needed by                  | Debian package      |
 | 
			
		||||
|------------------------------------------------------|----------------------------|------------------|
 | 
			
		||||
|------------------------------------------------------|----------------------------|---------------------|
 | 
			
		||||
| [Tox Core](https://github.com/irungentoo/toxcore)    | BASE                       | *None*              |
 | 
			
		||||
| [NCurses](https://www.gnu.org/software/ncurses)      | BASE                       | libncursesw5-dev    |
 | 
			
		||||
| [LibConfig](http://www.hyperrealm.com/libconfig)     | BASE                       | libconfig-dev       |
 | 
			
		||||
| [GNUmake](https://www.gnu.org/software/make)         | BASE                       | make                |
 | 
			
		||||
| [libcurl](http://curl.haxx.se/)                      | BASE                       | libcurl4-openssl-dev|
 | 
			
		||||
| [libqrencode](https://fukuchi.org/works/qrencode/)   | BASE                       | libqrencode-dev     |
 | 
			
		||||
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO                      | *None*              |
 | 
			
		||||
| [OpenAL](http://openal.org)                          | AUDIO, SOUND NOTIFICATIONS | libopenal-dev       |
 | 
			
		||||
| [OpenALUT](http://openal.org)                        | SOUND NOTIFICATIONS        | libalut-dev         |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								Makefile
									
									
									
									
									
								
							@@ -3,18 +3,18 @@ CFG_DIR = $(BASE_DIR)/cfg
 | 
			
		||||
 | 
			
		||||
-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 += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
 | 
			
		||||
CFLAGS += $(USER_CFLAGS)
 | 
			
		||||
LDFLAGS = $(USER_LDFLAGS)
 | 
			
		||||
 | 
			
		||||
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_transfers.o notify.o
 | 
			
		||||
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
 | 
			
		||||
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
 | 
			
		||||
OBJ += group_commands.o term_mplex.o avatars.o
 | 
			
		||||
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o
 | 
			
		||||
OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o
 | 
			
		||||
OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
 | 
			
		||||
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
 | 
			
		||||
 | 
			
		||||
# Check on wich system we are running
 | 
			
		||||
UNAME_S = $(shell uname -s)
 | 
			
		||||
@@ -24,6 +24,9 @@ endif
 | 
			
		||||
ifeq ($(UNAME_S), FreeBSD)
 | 
			
		||||
    -include $(CFG_DIR)/systems/FreeBSD.mk
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(UNAME_S), DragonFly)
 | 
			
		||||
    -include $(CFG_DIR)/systems/FreeBSD.mk
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(UNAME_S), OpenBSD)
 | 
			
		||||
    -include $(CFG_DIR)/systems/FreeBSD.mk
 | 
			
		||||
endif
 | 
			
		||||
@@ -59,6 +62,10 @@ $(BUILD_DIR)/toxic: $(OBJ)
 | 
			
		||||
	@echo "  LD    $(@:$(BUILD_DIR)/%=%)"
 | 
			
		||||
	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
 | 
			
		||||
 | 
			
		||||
$(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO)
 | 
			
		||||
	@echo "  CC    $(@:$(BUILD_DIR)/)osx_video.o"
 | 
			
		||||
	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO)
 | 
			
		||||
 | 
			
		||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
 | 
			
		||||
	@if [ ! -e $(BUILD_DIR) ]; then \
 | 
			
		||||
		mkdir -p $(BUILD_DIR) ;\
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
# Toxic [](https://travis-ci.org/Tox/toxic)
 | 
			
		||||
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.
 | 
			
		||||
<a href="https://scan.coverity.com/projects/toxic-tox">
 | 
			
		||||
  <img alt="Coverity Scan Build Status"
 | 
			
		||||
       src="https://scan.coverity.com/projects/4975/badge.svg"/>
 | 
			
		||||
</a>
 | 
			
		||||
 | 
			
		||||
Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
 | 
			
		||||
 | 
			
		||||
[](https://i.imgur.com/san99Z2.png)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
AUDIO_LIBS = libtoxav openal
 | 
			
		||||
AUDIO_CFLAGS = -DAUDIO
 | 
			
		||||
ifneq (, $(findstring device.o, $(OBJ)))
 | 
			
		||||
ifneq (, $(findstring audio_device.o, $(OBJ)))
 | 
			
		||||
    AUDIO_OBJ = audio_call.o
 | 
			
		||||
else
 | 
			
		||||
    AUDIO_OBJ = audio_call.o device.o
 | 
			
		||||
    AUDIO_OBJ = audio_call.o audio_device.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we can build audio support
 | 
			
		||||
@@ -9,7 +9,17 @@ endif
 | 
			
		||||
# Check if we want build audio support
 | 
			
		||||
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(AUDIO), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/av.mk
 | 
			
		||||
    -include $(CHECKS_DIR)/audio.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build video support
 | 
			
		||||
VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(X11), disabled)
 | 
			
		||||
ifneq ($(AUDIO), disabled)
 | 
			
		||||
ifneq ($(VIDEO), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/video.mk
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build sound notifications support
 | 
			
		||||
@@ -24,6 +34,12 @@ ifneq ($(DESK_NOTIFY), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/desktop_notifications.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build QR exported as PNG support
 | 
			
		||||
QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(QR_PNG), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/qr_png.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we can build Toxic
 | 
			
		||||
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
 | 
			
		||||
ifneq ($(CHECK_LIBS), error)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
SND_NOTIFY_LIBS = openal freealut
 | 
			
		||||
SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY
 | 
			
		||||
ifneq (, $(findstring device.o, $(OBJ)))
 | 
			
		||||
ifneq (, $(findstring audio_device.o, $(OBJ)))
 | 
			
		||||
    SND_NOTIFY_OBJ =
 | 
			
		||||
else
 | 
			
		||||
    SND_NOTIFY_OBJ = device.o
 | 
			
		||||
    SND_NOTIFY_OBJ = audio_device.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we can build sound notifications support
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
TOXIC_VERSION = 0.6.0
 | 
			
		||||
TOXIC_VERSION = 0.7.1
 | 
			
		||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
 | 
			
		||||
ifneq (, $(findstring error, $(REV)))
 | 
			
		||||
    VERSION = $(TOXIC_VERSION)
 | 
			
		||||
@@ -16,7 +16,7 @@ MISC_DIR = $(BASE_DIR)/misc
 | 
			
		||||
 | 
			
		||||
# Project files
 | 
			
		||||
MANFILES = toxic.1 toxic.conf.5
 | 
			
		||||
DATAFILES = DHTnodes DNSservers toxic.conf.example
 | 
			
		||||
DATAFILES = nameservers toxic.conf.example
 | 
			
		||||
DESKFILE = toxic.desktop
 | 
			
		||||
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
 | 
			
		||||
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
 | 
			
		||||
@@ -28,6 +28,5 @@ BINDIR = $(PREFIX)/bin
 | 
			
		||||
DATADIR = $(PREFIX)/share/toxic
 | 
			
		||||
MANDIR = $(PREFIX)/share/man
 | 
			
		||||
APPDIR = $(PREFIX)/share/applications
 | 
			
		||||
 | 
			
		||||
# Platform tools
 | 
			
		||||
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))
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
 | 
			
		||||
OSX_LIBRARIES = -lobjc -lresolv
 | 
			
		||||
OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \
 | 
			
		||||
	-framework QuartzCore -framework CoreMedia
 | 
			
		||||
OSX_VIDEO = osx_video.m
 | 
			
		||||
 | 
			
		||||
LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
 | 
			
		||||
OBJ += osx_video.o
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
# Specials options for freebsd systems
 | 
			
		||||
LIBS := $(filter-out ncursesw, $(LIBS))
 | 
			
		||||
LDFLAGS += -lncursesw
 | 
			
		||||
LDFLAGS += -lncursesw -lcurl
 | 
			
		||||
MANDIR = $(PREFIX)/man
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
# Specials options for linux systems
 | 
			
		||||
CFLAGS +=
 | 
			
		||||
LDFLAGS += -ldl -lresolv -lrt
 | 
			
		||||
LDFLAGS += -ldl -lrt -lcurl
 | 
			
		||||
MANDIR = $(PREFIX)/share/man
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ help:
 | 
			
		||||
	@echo "  DISABLE_AV:             Set to \"1\" to force building without audio call support"
 | 
			
		||||
	@echo "  DISABLE_SOUND_NOTIFY:   Set to \"1\" to force building without sound notification support"
 | 
			
		||||
	@echo "  DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
 | 
			
		||||
	@echo "  DISABLE_QRPNG:          Set to \"1\" to force building without QR exported as PNG support"
 | 
			
		||||
	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS"
 | 
			
		||||
	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS"
 | 
			
		||||
	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								doc/toxic.1
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								doc/toxic.1
									
									
									
									
									
								
							@@ -2,12 +2,12 @@
 | 
			
		||||
.\"     Title: toxic
 | 
			
		||||
.\"    Author: [see the "AUTHORS" section]
 | 
			
		||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
 | 
			
		||||
.\"      Date: 2015-03-28
 | 
			
		||||
.\"      Date: 2016-09-20
 | 
			
		||||
.\"    Manual: Toxic Manual
 | 
			
		||||
.\"    Source: toxic __VERSION__
 | 
			
		||||
.\"  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
 | 
			
		||||
.\" -----------------------------------------------------------------
 | 
			
		||||
@@ -70,7 +70,7 @@ Encrypt an unencrypted data file\&. An error will occur if this option is used w
 | 
			
		||||
Use specified
 | 
			
		||||
\fIdata\-file\fR
 | 
			
		||||
instead of
 | 
			
		||||
\fI~/\&.config/tox/data\fR
 | 
			
		||||
\fI~/\&.config/tox/toxic_profile\&.tox\fR
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\-h, \-\-help
 | 
			
		||||
@@ -82,8 +82,8 @@ Show help message
 | 
			
		||||
.RS 4
 | 
			
		||||
Use specified
 | 
			
		||||
\fInodes\-file\fR
 | 
			
		||||
for DHT bootstrap nodes, instead of
 | 
			
		||||
\fI__DATADIR__/DHTnodes\fR
 | 
			
		||||
for DHT bootstrap nodes instead of
 | 
			
		||||
\fI~/\&.config/tox/DHTnodes\&.json\fR
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\-o, \-\-noconnect
 | 
			
		||||
@@ -101,9 +101,9 @@ Use a SOCKS5 proxy: Requires [IP] [port]
 | 
			
		||||
Use a HTTP proxy: Requires [IP] [port]
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\-r, \-\-dnslist
 | 
			
		||||
\-r, \-\-namelist
 | 
			
		||||
.RS 4
 | 
			
		||||
Use specified DNSservers file
 | 
			
		||||
Use specified nameservers list
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\-t, \-\-force\-tcp
 | 
			
		||||
@@ -122,12 +122,14 @@ Unencrypt a data file\&. A warning will appear if this option is used with a dat
 | 
			
		||||
.RE
 | 
			
		||||
.SH "FILES"
 | 
			
		||||
.PP
 | 
			
		||||
__DATADIR__/DHTnodes
 | 
			
		||||
~/\&.config/tox/DHTnodes\&.json
 | 
			
		||||
.RS 4
 | 
			
		||||
Default list of DHT bootstrap nodes\&.
 | 
			
		||||
Default location for list of DHT bootstrap nodes (list obtained from
 | 
			
		||||
https://nodes\&.tox\&.chat)\&. This list is automatically updated\&. See
 | 
			
		||||
\fBtoxic\&.conf\fR(5) for details on controlling the update frequency\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
~/\&.config/tox/data
 | 
			
		||||
~/\&.config/tox/toxic_profile\&.tox
 | 
			
		||||
.RS 4
 | 
			
		||||
Savestate which contains your personal info (nickname, Tox ID, contacts, etc)
 | 
			
		||||
.RE
 | 
			
		||||
@@ -144,7 +146,11 @@ Configuration example\&.
 | 
			
		||||
.RE
 | 
			
		||||
.SH "BUGS"
 | 
			
		||||
.sp
 | 
			
		||||
Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&.
 | 
			
		||||
\-Unicode characters with a width larger than 1 column may cause strange behaviour\&.
 | 
			
		||||
.sp
 | 
			
		||||
\-Text occasionally fails to auto\-scroll to the bottom\&.
 | 
			
		||||
.sp
 | 
			
		||||
\-Screen flickering sometimes occurs on certain terminals\&.
 | 
			
		||||
.SH "AUTHORS"
 | 
			
		||||
.sp
 | 
			
		||||
JFreegman <JFreegman@gmail\&.com>
 | 
			
		||||
@@ -153,6 +159,6 @@ JFreegman <JFreegman@gmail\&.com>
 | 
			
		||||
\fBtoxic\&.conf\fR(5)
 | 
			
		||||
.SH "LINKS"
 | 
			
		||||
.sp
 | 
			
		||||
Project page: https://github\&.com/Tox/toxic
 | 
			
		||||
Project page: https://github\&.com/JFreegman/toxic
 | 
			
		||||
.sp
 | 
			
		||||
IRC channel: chat\&.freenode\&.net#tox
 | 
			
		||||
 
 | 
			
		||||
@@ -35,14 +35,13 @@ OPTIONS
 | 
			
		||||
    is used with an encrypted data file.
 | 
			
		||||
 | 
			
		||||
-f, --file data-file::
 | 
			
		||||
    Use specified 'data-file' instead of '~/.config/tox/data'
 | 
			
		||||
    Use specified 'data-file' instead of '~/.config/tox/toxic_profile.tox'
 | 
			
		||||
 | 
			
		||||
-h, --help::
 | 
			
		||||
    Show help message
 | 
			
		||||
 | 
			
		||||
-n, --nodes nodes-file::
 | 
			
		||||
    Use specified 'nodes-file' for DHT bootstrap nodes, instead of
 | 
			
		||||
    '{datadir}/DHTnodes'
 | 
			
		||||
    Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json'
 | 
			
		||||
 | 
			
		||||
-o, --noconnect::
 | 
			
		||||
    Do not connect to the DHT network
 | 
			
		||||
@@ -53,8 +52,8 @@ OPTIONS
 | 
			
		||||
-P, --HTTP-proxy::
 | 
			
		||||
    Use a HTTP proxy: Requires [IP] [port]
 | 
			
		||||
 | 
			
		||||
-r, --dnslist::
 | 
			
		||||
    Use specified DNSservers file
 | 
			
		||||
-r, --namelist::
 | 
			
		||||
    Use specified nameservers list
 | 
			
		||||
 | 
			
		||||
-t, --force-tcp::
 | 
			
		||||
    Force TCP connection (use this with proxies)
 | 
			
		||||
@@ -68,10 +67,11 @@ OPTIONS
 | 
			
		||||
 | 
			
		||||
FILES
 | 
			
		||||
-----
 | 
			
		||||
{datadir}/DHTnodes::
 | 
			
		||||
    Default list of DHT bootstrap nodes.
 | 
			
		||||
~/.config/tox/DHTnodes.json::
 | 
			
		||||
    Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat).
 | 
			
		||||
    This list is automatically updated. See *toxic.conf*(5) for details on controlling the update frequency.
 | 
			
		||||
 | 
			
		||||
~/.config/tox/data::
 | 
			
		||||
~/.config/tox/toxic_profile.tox::
 | 
			
		||||
    Savestate which contains your personal info (nickname, Tox ID, contacts,
 | 
			
		||||
    etc)
 | 
			
		||||
 | 
			
		||||
@@ -83,9 +83,12 @@ FILES
 | 
			
		||||
 | 
			
		||||
BUGS
 | 
			
		||||
----
 | 
			
		||||
Unicode characters with a width larger than 1 column may cause strange
 | 
			
		||||
behaviour. Expect more bugs and bad behaviour: this software is in a
 | 
			
		||||
pre-alpha stage.
 | 
			
		||||
-Unicode characters with a width larger than 1 column may cause strange
 | 
			
		||||
behaviour.
 | 
			
		||||
 | 
			
		||||
-Text occasionally fails to auto-scroll to the bottom.
 | 
			
		||||
 | 
			
		||||
-Screen flickering sometimes occurs on certain terminals.
 | 
			
		||||
 | 
			
		||||
AUTHORS
 | 
			
		||||
-------
 | 
			
		||||
@@ -97,6 +100,6 @@ SEE ALSO
 | 
			
		||||
 | 
			
		||||
LINKS
 | 
			
		||||
-----
 | 
			
		||||
Project page: <https://github.com/Tox/toxic>
 | 
			
		||||
Project page: <https://github.com/JFreegman/toxic>
 | 
			
		||||
 | 
			
		||||
IRC channel: chat.freenode.net#tox
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,12 @@
 | 
			
		||||
.\"     Title: toxic.conf
 | 
			
		||||
.\"    Author: [see the "AUTHORS" section]
 | 
			
		||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
 | 
			
		||||
.\"      Date: 2015-03-28
 | 
			
		||||
.\"      Date: 2016-07-21
 | 
			
		||||
.\"    Manual: Toxic Manual
 | 
			
		||||
.\"    Source: toxic __VERSION__
 | 
			
		||||
.\"  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
 | 
			
		||||
.\" -----------------------------------------------------------------
 | 
			
		||||
@@ -85,7 +85,7 @@ Time format string for logging enclosed by double quotes\&. See
 | 
			
		||||
.PP
 | 
			
		||||
\fBalerts\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable or disable terminal alerts on events\&. true or false
 | 
			
		||||
Enable or disable acoustic alerts on events\&. true or false
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBnative_colors\fR
 | 
			
		||||
@@ -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
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBshow_connection_msg\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable friend connection change notifications\&. true or false
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBnodelist_update_freq\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
How often in days to update the DHT nodes list\&. (0 to disable updates)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBhistory_size\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
 | 
			
		||||
@@ -138,6 +148,11 @@ Indicator for alert messages\&.
 | 
			
		||||
Indicator for normal messages\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBline_special\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Indicator for special messages\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBmplex_away\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Set user status when attaching and detaching from GNU screen or tmux\&. true or false
 | 
			
		||||
@@ -146,6 +161,38 @@ Set user status when attaching and detaching from GNU screen or tmux\&. true or
 | 
			
		||||
\fBmplex_away_note\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&.
 | 
			
		||||
.sp
 | 
			
		||||
.if n \{\
 | 
			
		||||
.RS 4
 | 
			
		||||
.\}
 | 
			
		||||
.nf
 | 
			
		||||
The following options control whether to output a terminal bell on certain events\&.
 | 
			
		||||
Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&.
 | 
			
		||||
These options don\*(Aqt affect the "alerts" option\&.
 | 
			
		||||
.fi
 | 
			
		||||
.if n \{\
 | 
			
		||||
.RE
 | 
			
		||||
.\}
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBbell_on_message\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable/Disable the terminal bell when receiving a message\&. true or false
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBbell_on_filetrans\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable/Disable the terminal bell when receiving a filetransfer\&. true or false
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBbell_on_filetrans_accept\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable/Disable the terminal bell when a filetransfer was accepted\&. true or false
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBbell_on_invite\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable/Disable the terminal bell when receiving a group/call invite\&. true or false
 | 
			
		||||
.RE
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
@@ -189,6 +236,11 @@ Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
 | 
			
		||||
.RS 4
 | 
			
		||||
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBpassword_eval\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Replace password prompt by running this command and using its output as the password\&.
 | 
			
		||||
.RE
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBsounds\fR
 | 
			
		||||
@@ -303,6 +355,11 @@ Key combination to scroll contacts list down\&.
 | 
			
		||||
.RS 4
 | 
			
		||||
Toggle the peer list on and off\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBtoggle_paste_mode\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Toggle treating linebreaks as enter key press\&.
 | 
			
		||||
.RE
 | 
			
		||||
.RE
 | 
			
		||||
.SH "FILES"
 | 
			
		||||
.PP
 | 
			
		||||
@@ -320,7 +377,7 @@ Configuration example\&.
 | 
			
		||||
\fBtoxic\fR(1)
 | 
			
		||||
.SH "RESOURCES"
 | 
			
		||||
.sp
 | 
			
		||||
Project page: https://github\&.com/Tox/toxic
 | 
			
		||||
Project page: https://github\&.com/JFreegman/toxic
 | 
			
		||||
.sp
 | 
			
		||||
IRC channel: chat\&.freenode\&.net#tox
 | 
			
		||||
.SH "AUTHORS"
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ OPTIONS
 | 
			
		||||
        See *date*(1)
 | 
			
		||||
 | 
			
		||||
    *alerts*;;
 | 
			
		||||
        Enable or disable terminal alerts on events. true or false
 | 
			
		||||
        Enable or disable acoustic alerts on events. true or false
 | 
			
		||||
 | 
			
		||||
    *native_colors*;;
 | 
			
		||||
        Select between native terminal colors and toxic color theme. true or false
 | 
			
		||||
@@ -72,6 +72,12 @@ OPTIONS
 | 
			
		||||
    *show_welcome_msg*;;
 | 
			
		||||
        Show welcome message on startup. true or false
 | 
			
		||||
 | 
			
		||||
    *show_connection_msg*;;
 | 
			
		||||
        Enable friend connection change notifications. true or false
 | 
			
		||||
 | 
			
		||||
    *nodelist_update_freq*;;
 | 
			
		||||
        How often in days to update the DHT nodes list. (0 to disable updates)
 | 
			
		||||
 | 
			
		||||
    *history_size*;;
 | 
			
		||||
        Maximum lines for chat window history. Integer value. (for example: 700)
 | 
			
		||||
 | 
			
		||||
@@ -88,6 +94,9 @@ OPTIONS
 | 
			
		||||
    *line_normal*;;
 | 
			
		||||
        Indicator for normal messages.
 | 
			
		||||
 | 
			
		||||
    *line_special*;;
 | 
			
		||||
        Indicator for special messages.
 | 
			
		||||
 | 
			
		||||
    *mplex_away*;;
 | 
			
		||||
        Set user status when attaching and detaching from GNU screen or tmux.
 | 
			
		||||
        true or false
 | 
			
		||||
@@ -97,6 +106,23 @@ OPTIONS
 | 
			
		||||
        detach. When attaching, the status message is set back to the original
 | 
			
		||||
        value.
 | 
			
		||||
 | 
			
		||||
    The following options control whether to output a terminal bell on certain events.
 | 
			
		||||
    Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window.
 | 
			
		||||
    These options don't affect the "alerts" option.
 | 
			
		||||
 | 
			
		||||
    *bell_on_message*;;
 | 
			
		||||
        Enable/Disable the terminal bell when receiving a message. true or false
 | 
			
		||||
 | 
			
		||||
    *bell_on_filetrans*;;
 | 
			
		||||
        Enable/Disable the terminal bell when receiving a filetransfer. true or false
 | 
			
		||||
 | 
			
		||||
    *bell_on_filetrans_accept*;;
 | 
			
		||||
        Enable/Disable the terminal bell when a filetransfer was accepted. true or false
 | 
			
		||||
 | 
			
		||||
    *bell_on_invite*;;
 | 
			
		||||
        Enable/Disable the terminal bell when receiving a group/call invite. true or false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
*audio*::
 | 
			
		||||
    Configuration related to audio devices.
 | 
			
		||||
 | 
			
		||||
@@ -123,6 +149,10 @@ OPTIONS
 | 
			
		||||
    *chatlogs_path*;;
 | 
			
		||||
        Default path for chatlogs. String value. Absolute path for chatlog files.
 | 
			
		||||
 | 
			
		||||
    *password_eval*;;
 | 
			
		||||
        Replace password prompt by running this command and using its output as
 | 
			
		||||
	the password.
 | 
			
		||||
 | 
			
		||||
*sounds*::
 | 
			
		||||
    Configuration related to notification sounds.
 | 
			
		||||
    Special value "silent" can be used to disable a specific notification. +
 | 
			
		||||
@@ -194,6 +224,9 @@ OPTIONS
 | 
			
		||||
    *toggle_peerlist*;;
 | 
			
		||||
        Toggle the peer list on and off.
 | 
			
		||||
 | 
			
		||||
    *toggle_paste_mode*;;
 | 
			
		||||
        Toggle treating linebreaks as enter key press.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FILES
 | 
			
		||||
-----
 | 
			
		||||
@@ -211,7 +244,7 @@ SEE ALSO
 | 
			
		||||
 | 
			
		||||
RESOURCES
 | 
			
		||||
---------
 | 
			
		||||
Project page: <https://github.com/Tox/toxic>
 | 
			
		||||
Project page: <https://github.com/JFreegman/toxic>
 | 
			
		||||
 | 
			
		||||
IRC channel:  chat.freenode.net#tox
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,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
 | 
			
		||||
  timestamps=true;
 | 
			
		||||
 | 
			
		||||
  // true to enable terminal alerts on messages, false to disable
 | 
			
		||||
  // true to enable acoustic alerts on messages, false to disable
 | 
			
		||||
  alerts=true;
 | 
			
		||||
 | 
			
		||||
  // Output a bell when receiving a message (see manpage)
 | 
			
		||||
  bell_on_message=true
 | 
			
		||||
 | 
			
		||||
  // Output a bell when receiving a filetransfer (see manpage)
 | 
			
		||||
  bell_on_filetrans=true
 | 
			
		||||
 | 
			
		||||
  // Don't output a bell when a filetransfer was accepted (see manpage)
 | 
			
		||||
  bell_on_filetrans_accept=false
 | 
			
		||||
 | 
			
		||||
  // Output a bell when receiving a group/call invite (see manpage)
 | 
			
		||||
  bell_on_invite=true
 | 
			
		||||
 | 
			
		||||
  // true to use native terminal colours, false to use toxic default colour theme
 | 
			
		||||
  native_colors=false;
 | 
			
		||||
 | 
			
		||||
@@ -17,7 +29,7 @@ ui = {
 | 
			
		||||
  // 24 or 12 hour time
 | 
			
		||||
  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";
 | 
			
		||||
 | 
			
		||||
  // 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
 | 
			
		||||
  show_welcome_msg=true;
 | 
			
		||||
 | 
			
		||||
  // true to show friend connection change messages on the home screen
 | 
			
		||||
  show_connection_msg=true;
 | 
			
		||||
 | 
			
		||||
  // How often in days to update the DHT nodes list. (0 to disable updates)
 | 
			
		||||
  nodeslist_update_freq=7;
 | 
			
		||||
 | 
			
		||||
  // maximum lines for chat window history
 | 
			
		||||
  history_size=700;
 | 
			
		||||
 | 
			
		||||
@@ -44,6 +62,9 @@ ui = {
 | 
			
		||||
  // Indicator for normal messages.
 | 
			
		||||
  line_normal="---";
 | 
			
		||||
 | 
			
		||||
  // Indicator for special messages (currently only used for private group messages)
 | 
			
		||||
  line_special=">>>";
 | 
			
		||||
 | 
			
		||||
  // true to change status based on screen/tmux attach/detach, false to disable
 | 
			
		||||
  mplex_away=true;
 | 
			
		||||
 | 
			
		||||
@@ -66,7 +87,7 @@ tox = {
 | 
			
		||||
  // Path for downloaded files
 | 
			
		||||
  // download_path="/home/USERNAME/Downloads/";
 | 
			
		||||
 | 
			
		||||
  // Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
 | 
			
		||||
  // Path for your avatar (file must be a .png and cannot exceed 64 KiB)
 | 
			
		||||
  // avatar_path="/home/USERNAME/Pictures/youravatar.png";
 | 
			
		||||
 | 
			
		||||
  // Path for chatlogs
 | 
			
		||||
@@ -86,7 +107,7 @@ sounds = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
 | 
			
		||||
// Note: All printable keys register as input
 | 
			
		||||
// Note: Ctrl+M does not work
 | 
			
		||||
keys = {
 | 
			
		||||
  next_tab="Ctrl+P";
 | 
			
		||||
  prev_tab="Ctrl+O";
 | 
			
		||||
@@ -98,5 +119,6 @@ keys = {
 | 
			
		||||
  peer_list_up="Ctrl+[";
 | 
			
		||||
  peer_list_down="Ctrl+]";
 | 
			
		||||
  toggle_peerlist="Ctrl+b";
 | 
			
		||||
  toggle_paste_mode="Ctrl+T";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										606
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										606
									
								
								src/audio_call.c
									
									
									
									
									
								
							@@ -23,11 +23,17 @@
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "audio_call.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
#include "chat_commands.h"
 | 
			
		||||
#include "global_commands.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "chat.h"
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
#include "video_call.h"
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <curses.h>
 | 
			
		||||
@@ -49,174 +55,169 @@
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern FriendsList Friends;
 | 
			
		||||
 | 
			
		||||
#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->out_idx = -1;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    call->vin_idx = -1;
 | 
			
		||||
    call->vout_idx = -1;
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    if ( start ) {
 | 
			
		||||
        call->ttas = true;
 | 
			
		||||
 | 
			
		||||
        if (pthread_mutex_init(&call->mutex, NULL) != 0)
 | 
			
		||||
        if ( pthread_mutex_init(&call->mutex, NULL) != 0 )
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
    } else {
 | 
			
		||||
        call->ttid = 0;
 | 
			
		||||
        if (pthread_mutex_destroy(&call->mutex) != 0)
 | 
			
		||||
 | 
			
		||||
        if ( pthread_mutex_destroy(&call->mutex) != 0 )
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ASettings {
 | 
			
		||||
    AudioError errors;
 | 
			
		||||
void call_cb                 ( ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled,
 | 
			
		||||
                               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;
 | 
			
		||||
 | 
			
		||||
    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 );
 | 
			
		||||
void write_device_callback( uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels,
 | 
			
		||||
                            uint32_t sample_rate );
 | 
			
		||||
 | 
			
		||||
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_audio(ToxWindow *self, Tox *tox)
 | 
			
		||||
ToxAV *init_audio(ToxWindow *self, Tox *tox)
 | 
			
		||||
{
 | 
			
		||||
    ASettins.cs = av_DefaultSettings;
 | 
			
		||||
    ASettins.cs.max_video_height = ASettins.cs.max_video_width = 0;
 | 
			
		||||
    TOXAV_ERR_NEW error;
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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");
 | 
			
		||||
        toxav_kill(ASettins.av);
 | 
			
		||||
        return ASettins.av = NULL;
 | 
			
		||||
        toxav_kill(CallControl.av);
 | 
			
		||||
 | 
			
		||||
        return CallControl.av = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_call_started, av_OnStart, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_call_canceled, av_OnCancel, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_call_rejected, av_OnReject, self);
 | 
			
		||||
    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_callback_call(CallControl.av, call_cb, tox);
 | 
			
		||||
    toxav_callback_call_state(CallControl.av, callstate_cb, NULL);
 | 
			
		||||
    toxav_callback_audio_receive_frame(CallControl.av, receive_audio_frame_cb, NULL);
 | 
			
		||||
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_recv_ringing, av_OnRinging, self);
 | 
			
		||||
    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;
 | 
			
		||||
    return CallControl.av;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void terminate_audio()
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < MAX_CALLS; ++i)
 | 
			
		||||
        stop_transmission(&ASettins.calls[i], i);
 | 
			
		||||
 | 
			
		||||
    if ( ASettins.av )
 | 
			
		||||
        toxav_kill(ASettins.av);
 | 
			
		||||
    for (i = 0; i < MAX_CALLS; ++i)
 | 
			
		||||
        stop_transmission(&CallControl.calls[i], i);
 | 
			
		||||
 | 
			
		||||
    if ( CallControl.av )
 | 
			
		||||
        toxav_kill(CallControl.av);
 | 
			
		||||
 | 
			
		||||
    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];
 | 
			
		||||
    int32_t payload_size = toxav_prepare_audio_frame(ASettins.av, call_index, encoded_payload, RTP_PAYLOAD_SIZE, captured, size);
 | 
			
		||||
    if ( payload_size <= 0 || toxav_send_audio(ASettins.av, call_index, encoded_payload, payload_size) < 0 ) {
 | 
			
		||||
        /*fprintf(stderr, "Could not encode audio packet\n");*/
 | 
			
		||||
    if ( sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number,
 | 
			
		||||
            captured, sample_count,
 | 
			
		||||
            CallControl.audio_channels,
 | 
			
		||||
            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;
 | 
			
		||||
    (void)agent;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
    if ( CallControl.calls[friend_number].ttas )
 | 
			
		||||
        write_out(CallControl.calls[friend_number].out_idx, PCM, sample_count, channels, sample_rate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int start_transmission(ToxWindow *self, Call *call)
 | 
			
		||||
{
 | 
			
		||||
    if ( !self || !ASettins.av || self->call_idx == -1 ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission");
 | 
			
		||||
    if ( !self || !CallControl.av ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission");
 | 
			
		||||
        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)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    ToxAvCSettings csettings;
 | 
			
		||||
    toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings);
 | 
			
		||||
    DeviceError error = open_primary_device(input, &call->in_idx,
 | 
			
		||||
                                            CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
 | 
			
		||||
 | 
			
		||||
    if ( open_primary_device(input, &call->in_idx,
 | 
			
		||||
            csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None )
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input device!");
 | 
			
		||||
    if ( error != de_None ) {
 | 
			
		||||
        if ( error == de_FailedStart)
 | 
			
		||||
            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,
 | 
			
		||||
         read_device_callback, &self->call_idx, true) != de_None)
 | 
			
		||||
        if ( error == de_InternalError )
 | 
			
		||||
            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 */
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
 | 
			
		||||
 | 
			
		||||
    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!");
 | 
			
		||||
        call->has_output = 0;
 | 
			
		||||
    }
 | 
			
		||||
@@ -224,10 +225,15 @@ int start_transmission(ToxWindow *self, Call *call)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int stop_transmission(Call *call, int32_t call_index)
 | 
			
		||||
int stop_transmission(Call *call, uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    if ( call->ttas ) {
 | 
			
		||||
        toxav_kill_transmission(ASettins.av, call_index);
 | 
			
		||||
        TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK;
 | 
			
		||||
 | 
			
		||||
        if ( CallControl.call_state > TOXAV_FRIEND_CALL_STATE_FINISHED )
 | 
			
		||||
            toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_CANCEL, &error);
 | 
			
		||||
 | 
			
		||||
        if ( error == TOXAV_ERR_CALL_CONTROL_OK ) {
 | 
			
		||||
            call->ttas = false;
 | 
			
		||||
 | 
			
		||||
            if ( call->in_idx != -1 )
 | 
			
		||||
@@ -236,10 +242,14 @@ int stop_transmission(Call *call, int32_t call_index)
 | 
			
		||||
            if ( call->out_idx != -1 )
 | 
			
		||||
                close_device(output, call->out_idx);
 | 
			
		||||
 | 
			
		||||
        if (set_call(call, false) == -1)
 | 
			
		||||
            if ( set_call(call, false) == -1 )
 | 
			
		||||
                return -1;
 | 
			
		||||
 | 
			
		||||
            return 0;
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
@@ -255,85 +265,194 @@ int stop_transmission(Call *call, int32_t call_index)
 | 
			
		||||
/*
 | 
			
		||||
 * Callbacks
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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 )
 | 
			
		||||
void call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
void callback_recv_starting ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow* windows = arg;
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i)
 | 
			
		||||
        if (windows[i].onStarting != NULL && windows[i].call_idx == call_index) {
 | 
			
		||||
            windows[i].onStarting(&windows[i], ASettins.av, call_index);
 | 
			
		||||
            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!");
 | 
			
		||||
    CallControl.call_state = state;
 | 
			
		||||
 | 
			
		||||
    switch ( state ) {
 | 
			
		||||
        case ( TOXAV_FRIEND_CALL_STATE_ERROR ):
 | 
			
		||||
            line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!");
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            callback_video_end(friend_number);
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
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);
 | 
			
		||||
    stop_transmission(&ASettins.calls[call_index], call_index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void callback_call_started ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow* windows = arg;
 | 
			
		||||
    ToxWindow *windows = CallControl.prompt;
 | 
			
		||||
    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)
 | 
			
		||||
        if (windows[i].onStart != NULL && windows[i].call_idx == call_index) {
 | 
			
		||||
            windows[i].onStart(&windows[i], ASettins.av, call_index);
 | 
			
		||||
            if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index]) ) {/* YEAH! */
 | 
			
		||||
        if ( windows[i].onStart != NULL && windows[i].num == friend_number ) {
 | 
			
		||||
            windows[i].onStart(&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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
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 */
 | 
			
		||||
    stop_transmission(&ASettins.calls[call_index], call_index);
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        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);
 | 
			
		||||
}
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
    ToxWindow *windows = CallControl.prompt;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
void callback_requ_timeout ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onRequestTimeout);
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if (windows[i].onReject != NULL && windows[i].num == friend_number) {
 | 
			
		||||
            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);
 | 
			
		||||
    stop_transmission(&ASettins.calls[call_index], call_index);
 | 
			
		||||
    /* 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);
 | 
			
		||||
}
 | 
			
		||||
    ToxWindow *windows = CallControl.prompt;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
// void callback_media_change(void* av, int32_t call_index, void* arg)
 | 
			
		||||
// {
 | 
			
		||||
  /*... TODO cancel all media change requests */
 | 
			
		||||
// }
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if (windows[i].onEnd != NULL && windows[i].num == friend_number) {
 | 
			
		||||
            windows[i].onEnd(&windows[i], CallControl.av, friend_number, CallControl.call_state);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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])
 | 
			
		||||
{
 | 
			
		||||
    TOXAV_ERR_CALL error;
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if (argc != 0) {
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
        error_str = "Audio not supported!";
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "ToxAV not supported!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!self->stb->connection) {
 | 
			
		||||
    if ( !self->stb->connection ) {
 | 
			
		||||
        error_str = "Friend is offline.";
 | 
			
		||||
        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 ) {
 | 
			
		||||
        if ( error == av_ErrorAlreadyInCallWithPeer ) error_str = "Already in a call!";
 | 
			
		||||
    toxav_call(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
 | 
			
		||||
 | 
			
		||||
    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!";
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
on_error:
 | 
			
		||||
@@ -380,29 +509,39 @@ on_error:
 | 
			
		||||
 | 
			
		||||
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    TOXAV_ERR_ANSWER error;
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if (argc != 0) {
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "Audio not supported!";
 | 
			
		||||
        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 ) {
 | 
			
		||||
        if ( error == av_ErrorInvalidState ) error_str = "Cannot answer in invalid state!";
 | 
			
		||||
        else if ( error == av_ErrorNoCall ) error_str = "No incoming call!";
 | 
			
		||||
    toxav_answer(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
 | 
			
		||||
 | 
			
		||||
    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!";
 | 
			
		||||
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Callback will print status... */
 | 
			
		||||
    callback_recv_starting(self->num);
 | 
			
		||||
    CallControl.pending_call = false;
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
@@ -413,27 +552,27 @@ void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
 | 
			
		||||
{
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if (argc != 0) {
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "Audio not supported!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ToxAvError error = toxav_reject(ASettins.av, self->call_idx, "Why not?");
 | 
			
		||||
 | 
			
		||||
    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!";
 | 
			
		||||
 | 
			
		||||
    if ( !CallControl.pending_call ) {
 | 
			
		||||
        error_str = "No incoming call!";
 | 
			
		||||
        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_call_rejected(self->num);
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
@@ -442,39 +581,28 @@ on_error:
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "Audio not supported!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ToxAvError error;
 | 
			
		||||
 | 
			
		||||
    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!";
 | 
			
		||||
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        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;
 | 
			
		||||
on_error:
 | 
			
		||||
    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 ) {
 | 
			
		||||
        error_str="Invalid selection!";
 | 
			
		||||
        error_str = "Invalid selection!";
 | 
			
		||||
        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 ) {
 | 
			
		||||
        error_str="Invalid selection!";
 | 
			
		||||
        error_str = "Invalid selection!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If call is active, change device */
 | 
			
		||||
    if ( self->call_idx > -1) {
 | 
			
		||||
        Call* this_call = &ASettins.calls[self->call_idx];
 | 
			
		||||
        if (this_call->ttas) {
 | 
			
		||||
    if ( self->is_call ) {
 | 
			
		||||
        Call *this_call = &CallControl.calls[self->num];
 | 
			
		||||
 | 
			
		||||
            ToxAvCSettings csettings;
 | 
			
		||||
            toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings);
 | 
			
		||||
        if ( this_call->ttas ) {
 | 
			
		||||
 | 
			
		||||
            if (type == output) {
 | 
			
		||||
 | 
			
		||||
            if ( type == output ) {
 | 
			
		||||
                pthread_mutex_lock(&this_call->mutex);
 | 
			
		||||
                close_device(output, 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;
 | 
			
		||||
                pthread_mutex_unlock(&this_call->mutex);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
            } else {
 | 
			
		||||
                /* TODO: check for failure */
 | 
			
		||||
                close_device(input, this_call->in_idx);
 | 
			
		||||
                open_device(input, selection, &this_call->in_idx, csettings.audio_sample_rate,
 | 
			
		||||
                    csettings.audio_frame_duration, csettings.audio_channels);
 | 
			
		||||
                open_device(input, selection, &this_call->in_idx, CallControl.audio_sample_rate,
 | 
			
		||||
                            CallControl.audio_frame_duration, CallControl.audio_channels);
 | 
			
		||||
                /* 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;
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
    on_error:
 | 
			
		||||
on_error:
 | 
			
		||||
    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 ( self->call_idx > -1) {
 | 
			
		||||
        Call* this_call = &ASettins.calls[self->call_idx];
 | 
			
		||||
    if ( self->is_call ) {
 | 
			
		||||
        Call *this_call = &CallControl.calls[self->num];
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_lock(&this_call->mutex);
 | 
			
		||||
        if (type == input) {
 | 
			
		||||
 | 
			
		||||
        if ( type == input ) {
 | 
			
		||||
            device_mute(type, this_call->in_idx);
 | 
			
		||||
            self->chatwin->infobox.in_is_muted ^= 1;
 | 
			
		||||
        } else {
 | 
			
		||||
            device_mute(type, this_call->out_idx);
 | 
			
		||||
            self->chatwin->infobox.out_is_muted ^= 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_unlock(&this_call->mutex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
    on_error:
 | 
			
		||||
on_error:
 | 
			
		||||
    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 */
 | 
			
		||||
    if ( self->call_idx > -1) {
 | 
			
		||||
        device_set_VAD_treshold(ASettins.calls[self->call_idx].in_idx, value);
 | 
			
		||||
    if ( self->is_call ) {
 | 
			
		||||
        device_set_VAD_treshold(CallControl.calls[self->num].in_idx, 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 ( ASettins.av != NULL && self->call_idx != -1 &&
 | 
			
		||||
       ( callstate = toxav_get_call_state(ASettins.av, self->call_idx) ) != av_CallNonExistent) {
 | 
			
		||||
        switch (callstate)
 | 
			
		||||
        {
 | 
			
		||||
        case av_CallActive:
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
    if ( CallControl.pending_call ) {
 | 
			
		||||
        toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL);
 | 
			
		||||
        callback_call_canceled(self->num);
 | 
			
		||||
    } else {
 | 
			
		||||
        stop_transmission(&CallControl.calls[self->num], self->num);
 | 
			
		||||
        callback_call_ended(self->num);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CallControl.pending_call = false;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,14 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef AUDIO_H
 | 
			
		||||
#define AUDIO_H
 | 
			
		||||
#ifndef AUDIO_CALL_H
 | 
			
		||||
#define AUDIO_CALL_H
 | 
			
		||||
 | 
			
		||||
#include <tox/toxav.h>
 | 
			
		||||
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_CALLS 10
 | 
			
		||||
 | 
			
		||||
typedef enum _AudioError {
 | 
			
		||||
    ae_None = 0,
 | 
			
		||||
@@ -34,18 +36,58 @@ typedef enum _AudioError {
 | 
			
		||||
    ae_StartingCoreAudio = 1 << 2
 | 
			
		||||
} 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 {
 | 
			
		||||
    pthread_t ttid; /* Transmission thread id */
 | 
			
		||||
    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;
 | 
			
		||||
} Call;
 | 
			
		||||
 | 
			
		||||
struct CallControl {
 | 
			
		||||
    AudioError audio_errors;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    VideoError video_errors;
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    ToxAV *av;
 | 
			
		||||
    ToxWindow *prompt;
 | 
			
		||||
 | 
			
		||||
    Call calls[MAX_CALLS];
 | 
			
		||||
    uint32_t call_state;
 | 
			
		||||
    bool pending_call;
 | 
			
		||||
    bool audio_enabled;
 | 
			
		||||
    bool video_enabled;
 | 
			
		||||
 | 
			
		||||
    uint32_t audio_bit_rate;
 | 
			
		||||
    int32_t audio_frame_duration;
 | 
			
		||||
    uint32_t audio_sample_rate;
 | 
			
		||||
    uint8_t audio_channels;
 | 
			
		||||
 | 
			
		||||
    uint32_t video_bit_rate;
 | 
			
		||||
    int32_t video_frame_duration;
 | 
			
		||||
 | 
			
		||||
} CallControl;
 | 
			
		||||
 | 
			
		||||
struct CallControl CallControl;
 | 
			
		||||
 | 
			
		||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
 | 
			
		||||
ToxAv *init_audio(ToxWindow *self, Tox *tox);
 | 
			
		||||
ToxAV *init_audio(ToxWindow *self, Tox *tox);
 | 
			
		||||
void terminate_audio();
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO_H */
 | 
			
		||||
#endif /* AUDIO_CALL_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
/*  device.c
 | 
			
		||||
/*  audio_device.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#include "audio_call.h"
 | 
			
		||||
@@ -56,8 +56,8 @@ typedef struct Device {
 | 
			
		||||
    ALCdevice  *dhndl;                     /* Handle of device selected/opened */
 | 
			
		||||
    ALCcontext *ctx;                       /* Device context */
 | 
			
		||||
    DataHandleCallback cb;                 /* Use this to handle data from input device usually */
 | 
			
		||||
    void* cb_data;                         /* Data to be passed to callback */
 | 
			
		||||
    int32_t call_idx;                      /* ToxAv call index */
 | 
			
		||||
    void *cb_data;                         /* Data to be passed to callback */
 | 
			
		||||
    int32_t friend_number;                      /* ToxAV friend number */
 | 
			
		||||
 | 
			
		||||
    uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
 | 
			
		||||
    uint32_t ref_count;
 | 
			
		||||
@@ -80,7 +80,7 @@ Device *running[2][MAX_DEVICES] = {{NULL}};     /* Running devices */
 | 
			
		||||
uint32_t primary_device[2];          /* Primary device */
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
static ToxAv* av = NULL;
 | 
			
		||||
static ToxAV *av = NULL;
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
/* q_mutex */
 | 
			
		||||
@@ -92,10 +92,10 @@ pthread_mutex_t mutex;
 | 
			
		||||
bool thread_running = true,
 | 
			
		||||
     thread_paused = true;               /* Thread control */
 | 
			
		||||
 | 
			
		||||
void* thread_poll(void*);
 | 
			
		||||
void *thread_poll(void *);
 | 
			
		||||
/* Meet devices */
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
DeviceError init_devices(ToxAv* av_)
 | 
			
		||||
DeviceError init_devices(ToxAV *av_)
 | 
			
		||||
#else
 | 
			
		||||
DeviceError init_devices()
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
@@ -103,6 +103,7 @@ DeviceError init_devices()
 | 
			
		||||
    const char *stringed_device_list;
 | 
			
		||||
 | 
			
		||||
    size[input] = 0;
 | 
			
		||||
 | 
			
		||||
    if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
 | 
			
		||||
        ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
 | 
			
		||||
 | 
			
		||||
@@ -113,7 +114,13 @@ DeviceError init_devices()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size[output] = 0;
 | 
			
		||||
    if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
 | 
			
		||||
 | 
			
		||||
    if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
 | 
			
		||||
        stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
 | 
			
		||||
    else
 | 
			
		||||
        stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
 | 
			
		||||
 | 
			
		||||
    if (stringed_device_list) {
 | 
			
		||||
        ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
 | 
			
		||||
 | 
			
		||||
        for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
 | 
			
		||||
@@ -127,6 +134,7 @@ DeviceError init_devices()
 | 
			
		||||
        return de_InternalError;
 | 
			
		||||
 | 
			
		||||
    pthread_t thread_id;
 | 
			
		||||
 | 
			
		||||
    if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
 | 
			
		||||
        return de_InternalError;
 | 
			
		||||
 | 
			
		||||
@@ -155,9 +163,10 @@ DeviceError terminate_devices()
 | 
			
		||||
DeviceError device_mute(DeviceType type, uint32_t device_idx)
 | 
			
		||||
{
 | 
			
		||||
    if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
 | 
			
		||||
 | 
			
		||||
    lock;
 | 
			
		||||
 | 
			
		||||
    Device* device = running[type][device_idx];
 | 
			
		||||
    Device *device = running[type][device_idx];
 | 
			
		||||
 | 
			
		||||
    if (!device) {
 | 
			
		||||
        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)
 | 
			
		||||
{
 | 
			
		||||
    if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
 | 
			
		||||
 | 
			
		||||
    lock;
 | 
			
		||||
 | 
			
		||||
    Device* device = running[input][device_idx];
 | 
			
		||||
    Device *device = running[input][device_idx];
 | 
			
		||||
 | 
			
		||||
    if (!device) {
 | 
			
		||||
        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)
 | 
			
		||||
{
 | 
			
		||||
    if (size[type] <= selection || selection < 0) return de_InvalidSelection;
 | 
			
		||||
 | 
			
		||||
    primary_device[type] = selection;
 | 
			
		||||
 | 
			
		||||
    return de_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
 | 
			
		||||
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
 | 
			
		||||
                                uint8_t channels)
 | 
			
		||||
{
 | 
			
		||||
    return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
 | 
			
		||||
}
 | 
			
		||||
@@ -210,7 +222,8 @@ void get_primary_device_name(DeviceType type, char *buf, int size)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: generate buffers separately
 | 
			
		||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
 | 
			
		||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
 | 
			
		||||
                        uint32_t frame_duration, uint8_t channels)
 | 
			
		||||
{
 | 
			
		||||
    if (size[type] <= selection || selection < 0) return de_InvalidSelection;
 | 
			
		||||
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
 | 
			
		||||
 | 
			
		||||
    if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
 | 
			
		||||
    else *device_idx = i;
 | 
			
		||||
    if (i == MAX_DEVICES) {
 | 
			
		||||
        unlock;
 | 
			
		||||
        return de_AllDevicesBusy;
 | 
			
		||||
    } else *device_idx = i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
 | 
			
		||||
        if ( running[type][i] && running[type][i]->selection == selection ) {
 | 
			
		||||
@@ -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->sample_rate = sample_rate;
 | 
			
		||||
@@ -254,12 +270,12 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
 | 
			
		||||
    if (type == input) {
 | 
			
		||||
        device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
 | 
			
		||||
                                             sample_rate, device->sound_mode, frame_size * 2);
 | 
			
		||||
    #ifdef AUDIO
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
        device->VAD_treshold = user_settings->VAD_treshold;
 | 
			
		||||
    #endif
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        device->dhndl = alcOpenDevice(devices_names[type][selection]);
 | 
			
		||||
 | 
			
		||||
        if ( !device->dhndl ) {
 | 
			
		||||
            free(device);
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
        uint16_t zeros[frame_size];
 | 
			
		||||
        memset(zeros, 0, frame_size*2);
 | 
			
		||||
        memset(zeros, 0, frame_size * 2);
 | 
			
		||||
 | 
			
		||||
        for ( i = 0; i < OPENAL_BUFS; ++i ) {
 | 
			
		||||
            alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate);
 | 
			
		||||
            alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
    lock;
 | 
			
		||||
    Device* device = running[type][device_idx];
 | 
			
		||||
    Device *device = running[type][device_idx];
 | 
			
		||||
    DeviceError rc = de_None;
 | 
			
		||||
 | 
			
		||||
    if (!device) {
 | 
			
		||||
@@ -317,32 +333,30 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
 | 
			
		||||
    running[type][device_idx] = NULL;
 | 
			
		||||
 | 
			
		||||
    if ( !device->ref_count ) {
 | 
			
		||||
 | 
			
		||||
//         printf("Closed device ");
 | 
			
		||||
 | 
			
		||||
        if (type == input) {
 | 
			
		||||
            if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
        } else {
 | 
			
		||||
            if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
 | 
			
		||||
 | 
			
		||||
            alDeleteSources(1, &device->source);
 | 
			
		||||
            alDeleteBuffers(OPENAL_BUFS, device->buffers);
 | 
			
		||||
 | 
			
		||||
            alcMakeContextCurrent(NULL);
 | 
			
		||||
 | 
			
		||||
            if ( device->ctx ) alcDestroyContext(device->ctx);
 | 
			
		||||
 | 
			
		||||
            if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        free(device);
 | 
			
		||||
    }
 | 
			
		||||
    else device->ref_count--;
 | 
			
		||||
    } else device->ref_count--;
 | 
			
		||||
 | 
			
		||||
    unlock;
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD)
 | 
			
		||||
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
 | 
			
		||||
                                      void *data, bool enable_VAD)
 | 
			
		||||
{
 | 
			
		||||
    if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
 | 
			
		||||
        return de_InvalidSelection;
 | 
			
		||||
@@ -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_data = data;
 | 
			
		||||
    running[input][device_idx]->enable_VAD = enable_VAD;
 | 
			
		||||
    running[input][device_idx]->call_idx = call_idx;
 | 
			
		||||
    running[input][device_idx]->friend_number = friend_number;
 | 
			
		||||
    unlock;
 | 
			
		||||
 | 
			
		||||
    return de_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline__ DeviceError write_out(uint32_t device_idx, 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;
 | 
			
		||||
 | 
			
		||||
    Device* device = running[output][device_idx];
 | 
			
		||||
    Device *device = running[output][device_idx];
 | 
			
		||||
 | 
			
		||||
    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_QUEUED, &queued);
 | 
			
		||||
 | 
			
		||||
    if(processed) {
 | 
			
		||||
    if (processed) {
 | 
			
		||||
        ALuint bufids[processed];
 | 
			
		||||
        alSourceUnqueueBuffers(device->source, processed, bufids);
 | 
			
		||||
        alDeleteBuffers(processed - 1, bufids + 1);
 | 
			
		||||
        bufid = bufids[0];
 | 
			
		||||
    }
 | 
			
		||||
    else if(queued < 16) alGenBuffers(1, &bufid);
 | 
			
		||||
    } else if (queued < 16) alGenBuffers(1, &bufid);
 | 
			
		||||
    else {
 | 
			
		||||
        pthread_mutex_unlock(device->mutex);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
    ALint state;
 | 
			
		||||
    alGetSourcei(device->source, AL_SOURCE_STATE, &state);
 | 
			
		||||
 | 
			
		||||
    if(state != AL_PLAYING) alSourcePlay(device->source);
 | 
			
		||||
    if (state != AL_PLAYING) alSourcePlay(device->source);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(device->mutex);
 | 
			
		||||
    return de_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* thread_poll (void* arg) // TODO: maybe use thread for every input source
 | 
			
		||||
void *thread_poll (void *arg) // TODO: maybe use thread for every input source
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * NOTE: We only need to poll input devices for data.
 | 
			
		||||
@@ -409,23 +424,27 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
 | 
			
		||||
    int32_t sample = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    while (true)
 | 
			
		||||
    {
 | 
			
		||||
    while (1) {
 | 
			
		||||
        lock;
 | 
			
		||||
 | 
			
		||||
        if (!thread_running) {
 | 
			
		||||
            unlock;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool paused = thread_paused;
 | 
			
		||||
        unlock;
 | 
			
		||||
 | 
			
		||||
        if (thread_paused) usleep(10000); /* Wait for unpause. */
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            for (i = 0; i < size[input]; ++i)
 | 
			
		||||
            {
 | 
			
		||||
        /* Wait for unpause. */
 | 
			
		||||
        if (paused) {
 | 
			
		||||
            usleep(10000);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        else {
 | 
			
		||||
            for (i = 0; i < size[input]; ++i) {
 | 
			
		||||
                lock;
 | 
			
		||||
                if (running[input][i] != NULL)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                if (running[input][i] != NULL) {
 | 
			
		||||
                    alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
 | 
			
		||||
 | 
			
		||||
                    int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000);
 | 
			
		||||
@@ -434,7 +453,8 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
 | 
			
		||||
                        unlock;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    Device* device = running[input][i];
 | 
			
		||||
 | 
			
		||||
                    Device *device = running[input][i];
 | 
			
		||||
 | 
			
		||||
                    int16_t frame[16000];
 | 
			
		||||
                    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);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                unlock;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            usleep(5000);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -455,7 +477,7 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void print_devices(ToxWindow* self, DeviceType type)
 | 
			
		||||
void print_devices(ToxWindow *self, DeviceType type)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
@@ -470,7 +492,7 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
 | 
			
		||||
    return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* get_device_callback_data(uint32_t device_idx)
 | 
			
		||||
void *get_device_callback_data(uint32_t device_idx)
 | 
			
		||||
{
 | 
			
		||||
    if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
 | 
			
		||||
        return NULL;
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
/*  device.h
 | 
			
		||||
/*  audio_device.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
@@ -26,8 +26,8 @@
 | 
			
		||||
 * Read from running input device(s) via select()/callback combo.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef DEVICE_H
 | 
			
		||||
#define DEVICE_H
 | 
			
		||||
#ifndef AUDIO_DEVICE_H
 | 
			
		||||
#define AUDIO_DEVICE_H
 | 
			
		||||
 | 
			
		||||
#define OPENAL_BUFS 5
 | 
			
		||||
#define MAX_DEVICES 32
 | 
			
		||||
@@ -52,11 +52,11 @@ typedef enum DeviceError {
 | 
			
		||||
    de_AlError = -9,
 | 
			
		||||
} DeviceError;
 | 
			
		||||
 | 
			
		||||
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
 | 
			
		||||
typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
DeviceError init_devices(ToxAv* av);
 | 
			
		||||
DeviceError init_devices(ToxAV *av);
 | 
			
		||||
#else
 | 
			
		||||
DeviceError init_devices();
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
@@ -64,8 +64,9 @@ DeviceError init_devices();
 | 
			
		||||
DeviceError terminate_devices();
 | 
			
		||||
 | 
			
		||||
/* Callback handles ready data from INPUT device */
 | 
			
		||||
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
 | 
			
		||||
void* get_device_callback_data(uint32_t device_idx);
 | 
			
		||||
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
 | 
			
		||||
                                     void *data, bool enable_VAD);
 | 
			
		||||
void *get_device_callback_data(uint32_t device_idx);
 | 
			
		||||
 | 
			
		||||
/* toggle device mute */
 | 
			
		||||
DeviceError device_mute(DeviceType type, uint32_t device_idx);
 | 
			
		||||
@@ -75,17 +76,20 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
DeviceError set_primary_device(DeviceType type, int32_t selection);
 | 
			
		||||
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
 | 
			
		||||
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
 | 
			
		||||
                                uint8_t channels);
 | 
			
		||||
/* Start device */
 | 
			
		||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
 | 
			
		||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
 | 
			
		||||
                        uint32_t frame_duration, uint8_t channels);
 | 
			
		||||
/* Stop device */
 | 
			
		||||
DeviceError close_device(DeviceType type, uint32_t device_idx);
 | 
			
		||||
 | 
			
		||||
/* Write data to device */
 | 
			
		||||
DeviceError write_out(uint32_t device_idx, 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);
 | 
			
		||||
 | 
			
		||||
DeviceError selection_valid(DeviceType type, int32_t selection);
 | 
			
		||||
#endif /* DEVICE_H */
 | 
			
		||||
#endif /* AUDIO_DEVICE_H */
 | 
			
		||||
@@ -25,10 +25,10 @@
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
    #include <sys/types.h>
 | 
			
		||||
    #include <sys/dir.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/dir.h>
 | 
			
		||||
#else
 | 
			
		||||
    #include <dirent.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#endif /* ifdef __APPLE__ */
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
@@ -53,12 +53,14 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* puts match in match buffer. if more than one match, add first n chars that are identical.
 | 
			
		||||
   e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */
 | 
			
		||||
static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n)
 | 
			
		||||
 * e.g. if matches contains: [foo, foobar, foe] we put fo in match.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the length of the match.
 | 
			
		||||
 */
 | 
			
		||||
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n)
 | 
			
		||||
{
 | 
			
		||||
    if (n == 1) {
 | 
			
		||||
        strcpy(match, matches[0]);
 | 
			
		||||
        return;
 | 
			
		||||
        return snprintf(match, match_sz, "%s", matches[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
@@ -71,14 +73,14 @@ static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_
 | 
			
		||||
            char ch2 = matches[j][i];
 | 
			
		||||
 | 
			
		||||
            if (ch1 != ch2 || !ch1) {
 | 
			
		||||
                strcpy(match, matches[0]);
 | 
			
		||||
                snprintf(match, match_sz, "%s", matches[0]);
 | 
			
		||||
                match[i] = '\0';
 | 
			
		||||
                return;
 | 
			
		||||
                return i;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    strcpy(match, matches[0]);
 | 
			
		||||
    return snprintf(match, match_sz, "%s", matches[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* looks for all instances in list that begin with the last entered word in line according to pos,
 | 
			
		||||
@@ -114,7 +116,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
 | 
			
		||||
    tmp[ctx->pos] = '\0';
 | 
			
		||||
 | 
			
		||||
    const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
 | 
			
		||||
    char *sub = malloc(strlen(ubuf) + 1);
 | 
			
		||||
    char *sub = calloc(1, strlen(ubuf) + 1);
 | 
			
		||||
 | 
			
		||||
    if (sub == NULL)
 | 
			
		||||
        exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
 | 
			
		||||
@@ -142,14 +144,14 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int s_len = strlen(sub);
 | 
			
		||||
    const char *str;
 | 
			
		||||
    int n_matches = 0;
 | 
			
		||||
    char matches[n_items][MAX_STR_SIZE];
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    /* put all list matches in matches array */
 | 
			
		||||
    for (i = 0; i < n_items; ++i) {
 | 
			
		||||
        str = &L[i * size];
 | 
			
		||||
        char str[MAX_CMDNAME_SIZE + 1];
 | 
			
		||||
        snprintf(str, sizeof(str), "%s", &L[i * size]);
 | 
			
		||||
 | 
			
		||||
        if (strncasecmp(str, sub, s_len) == 0)
 | 
			
		||||
            strcpy(matches[n_matches++], str);
 | 
			
		||||
@@ -164,11 +166,15 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
 | 
			
		||||
        print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
 | 
			
		||||
 | 
			
		||||
    char match[MAX_STR_SIZE];
 | 
			
		||||
    get_str_match(self, match, matches, n_matches);
 | 
			
		||||
    size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches);
 | 
			
		||||
 | 
			
		||||
    if (match_len == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dir_search) {
 | 
			
		||||
        if (n_matches == 1)
 | 
			
		||||
            endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/";
 | 
			
		||||
            endchrs = char_rfind(match, '.', match_len) ? "\"" : "/";
 | 
			
		||||
        else
 | 
			
		||||
            endchrs = "";
 | 
			
		||||
    } else if (n_matches > 1) {
 | 
			
		||||
@@ -177,24 +183,29 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
 | 
			
		||||
 | 
			
		||||
    /* put match in correct spot in buf and append endchars */
 | 
			
		||||
    int n_endchrs = strlen(endchrs);
 | 
			
		||||
    int m_len = strlen(match);
 | 
			
		||||
    int strt = ctx->pos - s_len;
 | 
			
		||||
    int diff = m_len - s_len + n_endchrs;
 | 
			
		||||
    int diff = match_len - s_len + n_endchrs;
 | 
			
		||||
 | 
			
		||||
    if (ctx->len + diff >= MAX_STR_SIZE)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    char tmpend[MAX_STR_SIZE];
 | 
			
		||||
    snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
 | 
			
		||||
 | 
			
		||||
    if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    strcpy(&ubuf[strt], match);
 | 
			
		||||
    strcpy(&ubuf[strt + m_len], endchrs);
 | 
			
		||||
    strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
 | 
			
		||||
    strcpy(&ubuf[strt + match_len], endchrs);
 | 
			
		||||
    strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
 | 
			
		||||
 | 
			
		||||
    /* convert to widechar and copy back to original buf */
 | 
			
		||||
    wchar_t newbuf[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1)
 | 
			
		||||
    if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wcscpy(ctx->line, newbuf);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,7 @@ int avatar_send(Tox *m, uint32_t friendnum)
 | 
			
		||||
    TOX_ERR_FILE_SEND err;
 | 
			
		||||
    uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
 | 
			
		||||
                                     NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
 | 
			
		||||
 | 
			
		||||
    if (Avatar.size == 0)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
@@ -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) {
 | 
			
		||||
                ft->state = FILE_TRANSFER_STARTED;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        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 */
 | 
			
		||||
							
								
								
									
										226
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										226
									
								
								src/chat.c
									
									
									
									
									
								
							@@ -48,7 +48,10 @@
 | 
			
		||||
#include "message_queue.h"
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    #include "audio_call.h"
 | 
			
		||||
#include "audio_call.h"
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
#include "video_call.h"
 | 
			
		||||
#endif  /* VIDEO */
 | 
			
		||||
#endif  /* AUDIO */
 | 
			
		||||
 | 
			
		||||
extern char *DATA_FILE;
 | 
			
		||||
@@ -63,9 +66,9 @@ static void kill_infobox(ToxWindow *self);
 | 
			
		||||
#endif  /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#define AC_NUM_CHAT_COMMANDS 27
 | 
			
		||||
#define AC_NUM_CHAT_COMMANDS 31
 | 
			
		||||
#else
 | 
			
		||||
#define AC_NUM_CHAT_COMMANDS 20
 | 
			
		||||
#define AC_NUM_CHAT_COMMANDS 23
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
/* 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"      },
 | 
			
		||||
    { "/connect"    },
 | 
			
		||||
    { "/exit"       },
 | 
			
		||||
    { "/gaccept"    },
 | 
			
		||||
    { "/group"      },
 | 
			
		||||
    { "/help"       },
 | 
			
		||||
    { "/invite"     },
 | 
			
		||||
    { "/join"       },
 | 
			
		||||
    { "/log"        },
 | 
			
		||||
    { "/myid"       },
 | 
			
		||||
    { "/myqr"       },
 | 
			
		||||
    { "/nick"       },
 | 
			
		||||
    { "/note"       },
 | 
			
		||||
    { "/nospam"     },
 | 
			
		||||
    { "/quit"       },
 | 
			
		||||
    { "/savefile"   },
 | 
			
		||||
    { "/sendfile"   },
 | 
			
		||||
@@ -100,6 +106,7 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
 | 
			
		||||
    { "/sdev"       },
 | 
			
		||||
    { "/mute"       },
 | 
			
		||||
    { "/sense"      },
 | 
			
		||||
    { "/video"      },
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
};
 | 
			
		||||
@@ -120,15 +127,18 @@ void kill_chat_window(ToxWindow *self, Tox *m)
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
    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);
 | 
			
		||||
    log_disable(ctx->log);
 | 
			
		||||
    line_info_cleanup(ctx->hst);
 | 
			
		||||
    cqueue_cleanup(ctx->cqueue);
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    stop_current_call(self);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    delwin(ctx->linewin);
 | 
			
		||||
    delwin(ctx->history);
 | 
			
		||||
    delwin(statusbar->topline);
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        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,
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        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)
 | 
			
		||||
@@ -206,7 +220,14 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
 | 
			
		||||
    char nick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    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
 | 
			
		||||
                                      ? tox_friend_get_typing(m, num, NULL) : false;
 | 
			
		||||
        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);
 | 
			
		||||
        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)
 | 
			
		||||
@@ -440,7 +459,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
 | 
			
		||||
                char progline[MAX_STR_SIZE];
 | 
			
		||||
                init_progress_bar(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;
 | 
			
		||||
            } else if (ft->state == FILE_TRANSFER_PAUSED) {    /* transfer is resumed */
 | 
			
		||||
                ft->state = FILE_TRANSFER_STARTED;
 | 
			
		||||
@@ -448,10 +467,12 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_FILE_CONTROL_PAUSE: {
 | 
			
		||||
            ft->state = FILE_TRANSFER_PAUSED;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_FILE_CONTROL_CANCEL: {
 | 
			
		||||
            snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name);
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
    if (self->active_box != -1)
 | 
			
		||||
        box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,
 | 
			
		||||
                    "Incoming file: %s", filename );
 | 
			
		||||
        box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans,
 | 
			
		||||
                    self->active_box, "Incoming file: %s", filename );
 | 
			
		||||
    else
 | 
			
		||||
        box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name,
 | 
			
		||||
                    "Incoming file: %s", filename );
 | 
			
		||||
        box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans,
 | 
			
		||||
                   &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,
 | 
			
		||||
                               uint16_t length)
 | 
			
		||||
static void chat_onGroupInvite(ToxWindow *self, Tox *m, uint32_t friendnumber, const char *invite_data,
 | 
			
		||||
                               size_t length)
 | 
			
		||||
{
 | 
			
		||||
    if (self->num != friendnumber)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (Friends.list[friendnumber].group_invite.key != NULL)
 | 
			
		||||
        free(Friends.list[friendnumber].group_invite.key);
 | 
			
		||||
    if (Friends.list[friendnumber].group_invite.data)
 | 
			
		||||
        free(Friends.list[friendnumber].group_invite.data);
 | 
			
		||||
 | 
			
		||||
    char *k = malloc(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.data = malloc(length * sizeof(uint8_t));
 | 
			
		||||
    memcpy(Friends.list[friendnumber].group_invite.data, invite_data, 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];
 | 
			
		||||
    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");
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
    /* 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\"");
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        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!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it.");
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
 | 
			
		||||
    if (self->ringing_sound == -1)
 | 
			
		||||
        sound_notify(self, call_outgoing, NT_LOOP, &self->ringing_sound);
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
    init_infobox(self);
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    stop_sound(self->ringing_sound);
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
    kill_infobox(self);
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    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_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;
 | 
			
		||||
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!");
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
@@ -700,11 +719,14 @@ void chat_onError (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
    /* call is flagged active here */
 | 
			
		||||
    self->is_call = true;
 | 
			
		||||
 | 
			
		||||
    init_infobox(self);
 | 
			
		||||
 | 
			
		||||
    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 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
    kill_infobox(self);
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!");
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
@@ -728,54 +750,27 @@ void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!");
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    stop_sound(self->ringing_sound);
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
    kill_infobox(self);
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
 | 
			
		||||
 | 
			
		||||
#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!");
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    stop_sound(self->ringing_sound);
 | 
			
		||||
@@ -788,6 +783,10 @@ static void init_infobox(ToxWindow *self)
 | 
			
		||||
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    (void) y2;
 | 
			
		||||
 | 
			
		||||
    memset(&ctx->infobox, 0, sizeof(struct infobox));
 | 
			
		||||
@@ -824,7 +823,7 @@ static void draw_infobox(ToxWindow *self)
 | 
			
		||||
    if (x2 < INFOBOX_WIDTH || y2 < INFOBOX_HEIGHT)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    uint64_t curtime = get_unix_time();
 | 
			
		||||
    time_t curtime = get_unix_time();
 | 
			
		||||
 | 
			
		||||
    /* update elapsed time string once per second */
 | 
			
		||||
    if (curtime > infobox->lastupdate)
 | 
			
		||||
@@ -861,7 +860,7 @@ static void draw_infobox(ToxWindow *self)
 | 
			
		||||
    wprintw(infobox->win, "%.2f\n", infobox->vad_lvl);
 | 
			
		||||
 | 
			
		||||
    wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' ');
 | 
			
		||||
    wrefresh(infobox->win);
 | 
			
		||||
    wnoutrefresh(infobox->win);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
    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);
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (x2 <= 0)
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (ctx->pastemode && key == '\r')
 | 
			
		||||
        key = '\n';
 | 
			
		||||
 | 
			
		||||
    if (self->help->active) {
 | 
			
		||||
        help_onKey(self, key);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ltr) {    /* char is printable */
 | 
			
		||||
    if (ltr || key == '\n') {    /* char is printable */
 | 
			
		||||
        input_new_char(self, key, x, y, x2, y2);
 | 
			
		||||
 | 
			
		||||
        if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE)
 | 
			
		||||
@@ -924,7 +925,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
            diff = dir_match(self, m, ctx->line, L"/sendfile");
 | 
			
		||||
        } else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
 | 
			
		||||
            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] = {
 | 
			
		||||
                {"online"},
 | 
			
		||||
                {"away"},
 | 
			
		||||
@@ -944,17 +945,19 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
            sound_notify(self, notif_error, 0, NULL);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    } else if (key == '\n') {
 | 
			
		||||
    } else if (key == '\r') {
 | 
			
		||||
        rm_trailing_spaces_buf(ctx);
 | 
			
		||||
 | 
			
		||||
        char line[MAX_STR_SIZE];
 | 
			
		||||
        if (!wstring_is_empty(ctx->line)) {
 | 
			
		||||
            add_line_to_hist(ctx);
 | 
			
		||||
 | 
			
		||||
            wstrsubst(ctx->line, L'¶', L'\n');
 | 
			
		||||
 | 
			
		||||
            char line[MAX_STR_SIZE] = {0};
 | 
			
		||||
 | 
			
		||||
            if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
 | 
			
		||||
                memset(&line, 0, sizeof(line));
 | 
			
		||||
 | 
			
		||||
        if (!string_is_empty(line))
 | 
			
		||||
            add_line_to_hist(ctx);
 | 
			
		||||
 | 
			
		||||
            if (line[0] == '/') {
 | 
			
		||||
                if (strcmp(line, "/close") == 0) {
 | 
			
		||||
                    kill_chat_window(self, m);
 | 
			
		||||
@@ -964,7 +967,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
                } else {
 | 
			
		||||
                    execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
 | 
			
		||||
                }
 | 
			
		||||
        } else if (!string_is_empty(line)) {
 | 
			
		||||
            } else {
 | 
			
		||||
                char selfname[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
                tox_self_get_name(m, (uint8_t *) selfname);
 | 
			
		||||
 | 
			
		||||
@@ -977,6 +980,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
                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);
 | 
			
		||||
        wmove(self->window, y2 - CURS_Y_OFFSET, 0);
 | 
			
		||||
@@ -992,6 +996,9 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
@@ -1019,9 +1026,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
            case TOX_USER_STATUS_NONE:
 | 
			
		||||
                colour = GREEN;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case TOX_USER_STATUS_AWAY:
 | 
			
		||||
                colour = YELLOW;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case TOX_USER_STATUS_BUSY:
 | 
			
		||||
                colour = RED;
 | 
			
		||||
                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));
 | 
			
		||||
    wmove(self->window, y + 1, new_x);
 | 
			
		||||
 | 
			
		||||
    wrefresh(self->window);
 | 
			
		||||
    wnoutrefresh(self->window);
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
    if (ctx->infobox.active) {
 | 
			
		||||
        draw_infobox(self);
 | 
			
		||||
        wrefresh(self->window);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (self->help->active)
 | 
			
		||||
@@ -1116,6 +1126,10 @@ static void chat_onInit(ToxWindow *self, Tox *m)
 | 
			
		||||
    curs_set(1);
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        exit_toxic_err("failed in chat_onInit", FATALERR_CURSES);
 | 
			
		||||
 | 
			
		||||
    self->x = x2;
 | 
			
		||||
 | 
			
		||||
    /* Init statusbar info */
 | 
			
		||||
@@ -1186,7 +1200,6 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
 | 
			
		||||
    ret.onMessage = &chat_onMessage;
 | 
			
		||||
    ret.onConnectionChange = &chat_onConnectionChange;
 | 
			
		||||
    ret.onTypingChange = & chat_onTypingChange;
 | 
			
		||||
    ret.onGroupInvite = &chat_onGroupInvite;
 | 
			
		||||
    ret.onNickChange = &chat_onNickChange;
 | 
			
		||||
    ret.onStatusChange = &chat_onStatusChange;
 | 
			
		||||
    ret.onStatusMessageChange = &chat_onStatusMessageChange;
 | 
			
		||||
@@ -1195,6 +1208,7 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
 | 
			
		||||
    ret.onFileControl = &chat_onFileControl;
 | 
			
		||||
    ret.onFileRecv = &chat_onFileRecv;
 | 
			
		||||
    ret.onReadReceipt = &chat_onReadReceipt;
 | 
			
		||||
    ret.onGroupInvite = &chat_onGroupInvite;
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    ret.onInvite = &chat_onInvite;
 | 
			
		||||
@@ -1206,10 +1220,8 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
 | 
			
		||||
    ret.onCancel = &chat_onCancel;
 | 
			
		||||
    ret.onReject = &chat_onReject;
 | 
			
		||||
    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.ringing_sound = -1;
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 
 | 
			
		||||
@@ -45,9 +45,9 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
 | 
			
		||||
 | 
			
		||||
    char msg[MAX_STR_SIZE];
 | 
			
		||||
    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.");
 | 
			
		||||
        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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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])
 | 
			
		||||
{
 | 
			
		||||
    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]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (groupnum == 0 && strcmp(argv[1], "0")) {    /* atoi returns 0 value on invalid input */
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (tox_invite_friend(m, self->num, groupnum) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
 | 
			
		||||
    TOX_ERR_GROUP_INVITE_FRIEND err;
 | 
			
		||||
 | 
			
		||||
    if (!tox_group_invite_friend(m, groupnum, self->num, &err)) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *groupkey = Friends.list[self->num].group_invite.key;
 | 
			
		||||
    uint16_t length = Friends.list[self->num].group_invite.length;
 | 
			
		||||
    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])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
@@ -146,9 +151,9 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
 | 
			
		||||
        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.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -190,6 +195,7 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
on_recv_error:
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
 | 
			
		||||
@@ -282,6 +288,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
on_send_error:
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
 | 
			
		||||
            errmsg = "File transfer failed: Invalid friend.";
 | 
			
		||||
 
 | 
			
		||||
@@ -26,9 +26,9 @@
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
 | 
			
		||||
void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
 | 
			
		||||
@@ -43,4 +43,9 @@ void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]
 | 
			
		||||
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#endif /* #define CHAT_COMMANDS_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -31,8 +31,9 @@
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "configdir.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
 | 
			
		||||
/* get the user's home directory */
 | 
			
		||||
/* get the user's home directory. */
 | 
			
		||||
void get_home_dir(char *home, int size)
 | 
			
		||||
{
 | 
			
		||||
    struct passwd pwd;
 | 
			
		||||
@@ -69,8 +70,8 @@ char *get_user_config_dir(void)
 | 
			
		||||
    char home[NSS_BUFLEN_PASSWD] = {0};
 | 
			
		||||
    get_home_dir(home, sizeof(home));
 | 
			
		||||
 | 
			
		||||
    char *user_config_dir;
 | 
			
		||||
    size_t len;
 | 
			
		||||
    char *user_config_dir = NULL;
 | 
			
		||||
    size_t len = 0;
 | 
			
		||||
 | 
			
		||||
# if defined(__APPLE__)
 | 
			
		||||
    len = strlen(home) + strlen("/Library/Application Support") + 1;
 | 
			
		||||
@@ -82,9 +83,9 @@ char *get_user_config_dir(void)
 | 
			
		||||
    snprintf(user_config_dir, len, "%s/Library/Application Support", home);
 | 
			
		||||
# else /* __APPLE__ */
 | 
			
		||||
 | 
			
		||||
    const char *tmp;
 | 
			
		||||
    const char *tmp = getenv("XDG_CONFIG_HOME");
 | 
			
		||||
 | 
			
		||||
    if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
 | 
			
		||||
    if (tmp == NULL) {
 | 
			
		||||
        len = strlen(home) + strlen("/.config") + 1;
 | 
			
		||||
        user_config_dir = malloc(len);
 | 
			
		||||
 | 
			
		||||
@@ -101,8 +102,10 @@ char *get_user_config_dir(void)
 | 
			
		||||
    return user_config_dir;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Creates the config and chatlog directories.
 | 
			
		||||
/* Creates the config and chatlog directories.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int create_user_config_dirs(char *path)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -34,8 +34,23 @@
 | 
			
		||||
#define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Get the user's config directory.
 | 
			
		||||
 *
 | 
			
		||||
 * This is without a trailing slash. Resulting string must be freed.
 | 
			
		||||
 *
 | 
			
		||||
 * @return The users config dir or NULL on error.
 | 
			
		||||
 */
 | 
			
		||||
char *get_user_config_dir(void);
 | 
			
		||||
 | 
			
		||||
/* get the user's home directory. */
 | 
			
		||||
void get_home_dir(char *home, int size);
 | 
			
		||||
 | 
			
		||||
/* Creates the config and chatlog directories.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int create_user_config_dirs(char *path);
 | 
			
		||||
 | 
			
		||||
#endif /* #define CONFIGDIR_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										93
									
								
								src/curl_util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/curl_util.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
/*  curl_util.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
#include <tox/tox.h>
 | 
			
		||||
 | 
			
		||||
#include "curl_util.h"
 | 
			
		||||
 | 
			
		||||
/* Sets proxy info for given CURL handler.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success or if no proxy is set by the client.
 | 
			
		||||
 * Returns -1 if proxy info is invalid.
 | 
			
		||||
 * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
 | 
			
		||||
 */
 | 
			
		||||
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
 | 
			
		||||
{
 | 
			
		||||
    if (proxy_type == TOX_PROXY_TYPE_NONE)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    if (proxy_address == NULL || port == 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Callback function for CURL to write received data.
 | 
			
		||||
 *
 | 
			
		||||
 * This function will append data from an http request to the data buffer
 | 
			
		||||
 * until the request is complete or the buffer is full. Buffer will be null terminated.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns number of bytes received from http request on success (don't change this).
 | 
			
		||||
 * Returns 0 if data exceeds buffer size.
 | 
			
		||||
 */
 | 
			
		||||
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer)
 | 
			
		||||
{
 | 
			
		||||
    struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer;
 | 
			
		||||
 | 
			
		||||
    size_t length = size * nmemb;
 | 
			
		||||
    size_t total_size = length + recv_data->length;
 | 
			
		||||
 | 
			
		||||
    if (total_size > MAX_RECV_CURL_DATA_SIZE) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(recv_data->data + recv_data->length, data, length);
 | 
			
		||||
    recv_data->data[total_size] = '\0';
 | 
			
		||||
    recv_data->length += length;
 | 
			
		||||
 | 
			
		||||
    return length;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								src/curl_util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/curl_util.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
/*  curl_util.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef CURL_UTIL_H
 | 
			
		||||
#define CURL_UTIL_H
 | 
			
		||||
 | 
			
		||||
/* List based on Mozilla's recommended configurations for modern browsers */
 | 
			
		||||
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
 | 
			
		||||
 | 
			
		||||
/* Max size of an http response that we can store in Recv_Data */
 | 
			
		||||
#define MAX_RECV_CURL_DATA_SIZE 32767
 | 
			
		||||
 | 
			
		||||
/* Holds data received from curl lookup */
 | 
			
		||||
struct Recv_Curl_Data {
 | 
			
		||||
    char data[MAX_RECV_CURL_DATA_SIZE + 1];   /* Data received from curl write data callback */
 | 
			
		||||
    size_t length;  /* Total number of bytes written to data buffer (doesn't include null) */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Sets proxy info for given CURL handler.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success or if no proxy is set by the client.
 | 
			
		||||
 * Returns -1 if proxy info is invalid.
 | 
			
		||||
 * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
 | 
			
		||||
 */
 | 
			
		||||
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type);
 | 
			
		||||
 | 
			
		||||
/* Callback function for CURL to write received data.
 | 
			
		||||
 *
 | 
			
		||||
 * This function will append data from an http request to the data buffer
 | 
			
		||||
 * until the request is complete or the buffer is full. Buffer will be null terminated.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns size of bytes written to the data buffer.
 | 
			
		||||
 */
 | 
			
		||||
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer);
 | 
			
		||||
 | 
			
		||||
#endif  /* CURL_UTIL_H */
 | 
			
		||||
							
								
								
									
										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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								src/execute.c
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								src/execute.c
									
									
									
									
									
								
							@@ -34,6 +34,8 @@
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_NUM_ARGS 10     /* Includes command */
 | 
			
		||||
 | 
			
		||||
struct cmd_func {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    void (*func)(WINDOW *w, ToxWindow *, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
@@ -49,10 +51,13 @@ static struct cmd_func global_commands[] = {
 | 
			
		||||
    { "/exit",      cmd_quit          },
 | 
			
		||||
    { "/group",     cmd_groupchat     },
 | 
			
		||||
    { "/help",      cmd_prompt_help   },
 | 
			
		||||
    { "/join",      cmd_join          },
 | 
			
		||||
    { "/log",       cmd_log           },
 | 
			
		||||
    { "/myid",      cmd_myid          },
 | 
			
		||||
    { "/myqr",      cmd_myqr          },
 | 
			
		||||
    { "/nick",      cmd_nick          },
 | 
			
		||||
    { "/note",      cmd_note          },
 | 
			
		||||
    { "/nospam",    cmd_nospam        },
 | 
			
		||||
    { "/q",         cmd_quit          },
 | 
			
		||||
    { "/quit",      cmd_quit          },
 | 
			
		||||
    { "/requests",  cmd_requests      },
 | 
			
		||||
@@ -61,13 +66,17 @@ static struct cmd_func global_commands[] = {
 | 
			
		||||
    { "/lsdev",     cmd_list_devices  },
 | 
			
		||||
    { "/sdev",      cmd_change_device },
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    { "/lsvdev",    cmd_list_video_devices },
 | 
			
		||||
    { "/svdev" ,    cmd_change_video_device },
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    { NULL,         NULL              },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct cmd_func chat_commands[] = {
 | 
			
		||||
    { "/cancel",    cmd_cancelfile  },
 | 
			
		||||
    { "/gaccept",   cmd_groupaccept },
 | 
			
		||||
    { "/invite",    cmd_groupinvite },
 | 
			
		||||
    { "/join",      cmd_join_group  },
 | 
			
		||||
    { "/savefile",  cmd_savefile    },
 | 
			
		||||
    { "/sendfile",  cmd_sendfile    },
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
@@ -78,12 +87,30 @@ static struct cmd_func chat_commands[] = {
 | 
			
		||||
    { "/mute",      cmd_mute        },
 | 
			
		||||
    { "/sense",     cmd_sense       },
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    { "/video",     cmd_video       },
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    { NULL,         NULL            },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    { "/mute",      cmd_mute           },
 | 
			
		||||
    { "/sense",     cmd_sense          },
 | 
			
		||||
@@ -91,10 +118,75 @@ static struct cmd_func group_commands[] = {
 | 
			
		||||
    { NULL,         NULL               },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Parses input command and puts args into arg array.
 | 
			
		||||
   Returns number of arguments on success, -1 on failure. */
 | 
			
		||||
#define NUM_SPECIAL_COMMANDS 15
 | 
			
		||||
static const char special_commands[NUM_SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
 | 
			
		||||
    "/ban",
 | 
			
		||||
    "/gaccept",
 | 
			
		||||
    "/group",
 | 
			
		||||
    "/ignore",
 | 
			
		||||
    "/kick",
 | 
			
		||||
    "/mod",
 | 
			
		||||
    "/nick",
 | 
			
		||||
    "/note",
 | 
			
		||||
    "/passwd",
 | 
			
		||||
    "/silence",
 | 
			
		||||
    "/topic",
 | 
			
		||||
    "/unignore",
 | 
			
		||||
    "/unmod",
 | 
			
		||||
    "/unsilence",
 | 
			
		||||
    "/whois",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* return true if input command is in the special_commands array. False otherwise.*/
 | 
			
		||||
static bool is_special_command(const char *input)
 | 
			
		||||
{
 | 
			
		||||
    int s = char_find(0, input, ' ');
 | 
			
		||||
 | 
			
		||||
    if (s == strlen(input))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < NUM_SPECIAL_COMMANDS; ++i) {
 | 
			
		||||
        if (strncmp(input, special_commands[i], s) == 0)
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parses commands in the special_commands array which take exactly one argument that may contain spaces.
 | 
			
		||||
 * Unlike parse_command, this function does not split the input string at spaces.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns number of arguments on success
 | 
			
		||||
 * Returns -1 on failure
 | 
			
		||||
 */
 | 
			
		||||
static int parse_special_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    int len = strlen(input);
 | 
			
		||||
    int s = char_find(0, input, ' ');
 | 
			
		||||
 | 
			
		||||
    if (s + 1 >= len)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    memcpy(args[0], input, s);
 | 
			
		||||
    args[0][s++] = '\0';    /* increment to remove space after /command */
 | 
			
		||||
    memcpy(args[1], input + s, len - s);
 | 
			
		||||
    args[1][len - s] = '\0';
 | 
			
		||||
 | 
			
		||||
    return 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parses input command and puts args (split by spaces) into args array.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns number of arguments on success
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (is_special_command(input))
 | 
			
		||||
        return parse_special_command(w, self, input, args);
 | 
			
		||||
 | 
			
		||||
    char *cmd = strdup(input);
 | 
			
		||||
 | 
			
		||||
    if (cmd == NULL)
 | 
			
		||||
@@ -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 */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Ugly special case concatinates all args after arg1 for multi-word group passwords */
 | 
			
		||||
    if (num_args > 2 && strcmp(args[0], "/join") == 0)
 | 
			
		||||
        strcpy(args[2], input + strlen(args[0]) + 1 + strlen(args[1]) + 1);
 | 
			
		||||
 | 
			
		||||
    free(cmd);
 | 
			
		||||
    return num_args;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Matches command to respective function. Returns 0 on match, 1 on no match */
 | 
			
		||||
/* Matches command to respective function.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on match,
 | 
			
		||||
 * Returns -1 on no match
 | 
			
		||||
 */
 | 
			
		||||
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
 | 
			
		||||
                      char (*args)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
@@ -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)
 | 
			
		||||
@@ -177,6 +277,7 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
 | 
			
		||||
        case GROUPCHAT_COMMAND_MODE:
 | 
			
		||||
            if (do_command(w, self, m, num_args, group_commands, args) == 0)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,13 +26,11 @@
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_NUM_ARGS 4     /* Includes command */
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    GLOBAL_COMMAND_MODE,
 | 
			
		||||
    CHAT_COMMAND_MODE,
 | 
			
		||||
    GROUPCHAT_COMMAND_MODE,
 | 
			
		||||
};
 | 
			
		||||
} COMMAND_MODE;
 | 
			
		||||
 | 
			
		||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,87 +38,48 @@ extern FriendsList Friends;
 | 
			
		||||
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
 | 
			
		||||
#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.
 | 
			
		||||
   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)
 | 
			
		||||
{
 | 
			
		||||
    strcpy(progline, "0.0 B/s [");
 | 
			
		||||
    strcpy(progline, "0% [");
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < NUM_PROG_MARKS; ++i)
 | 
			
		||||
        strcat(progline, "-");
 | 
			
		||||
 | 
			
		||||
    strcat(progline, "] 0%");
 | 
			
		||||
    strcat(progline, "] 0.0 B/s");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* prints a progress bar for file transfers.
 | 
			
		||||
   if friendnum is -1 we're sending the file, otherwise we're receiving.  */
 | 
			
		||||
/* prints a progress bar for file transfers. */
 | 
			
		||||
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
 | 
			
		||||
{
 | 
			
		||||
    if (bps < 0 || pct_done < 0 || pct_done > 100)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    char msg[MAX_STR_SIZE];
 | 
			
		||||
    bytes_convert_str(msg, sizeof(msg), bps);
 | 
			
		||||
    strcat(msg, "/s [");
 | 
			
		||||
    char pct_str[24];
 | 
			
		||||
    snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
 | 
			
		||||
 | 
			
		||||
    char bps_str[24];
 | 
			
		||||
    bytes_convert_str(bps_str, sizeof(bps_str), bps);
 | 
			
		||||
 | 
			
		||||
    char prog_line[NUM_PROG_MARKS + 1] = {0};
 | 
			
		||||
    int n = pct_done / (100 / NUM_PROG_MARKS);
 | 
			
		||||
    int i, j;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < n; ++i)
 | 
			
		||||
        strcat(msg, "#");
 | 
			
		||||
        strcat(prog_line, "=");
 | 
			
		||||
 | 
			
		||||
    for (j = i; j < NUM_PROG_MARKS; ++j)
 | 
			
		||||
        strcat(msg, "-");
 | 
			
		||||
    if (pct_done < 100)
 | 
			
		||||
        strcpy(prog_line + n, ">");
 | 
			
		||||
 | 
			
		||||
    strcat(msg, "] ");
 | 
			
		||||
    for (j = i; j < NUM_PROG_MARKS - 1; ++j)
 | 
			
		||||
        strcat(prog_line, "-");
 | 
			
		||||
 | 
			
		||||
    char pctstr[16];
 | 
			
		||||
    const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%";
 | 
			
		||||
    snprintf(pctstr, sizeof(pctstr), frmt, pct_done);
 | 
			
		||||
    strcat(msg, pctstr);
 | 
			
		||||
    char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7];
 | 
			
		||||
    snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str);
 | 
			
		||||
 | 
			
		||||
    line_info_set(self, line_id, msg);
 | 
			
		||||
    line_info_set(self, line_id, full_line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,6 @@
 | 
			
		||||
#define GiB 1073741824    /* 1024^3 */
 | 
			
		||||
 | 
			
		||||
#define MAX_FILES 32
 | 
			
		||||
#define TIMEOUT_FILESENDER 120
 | 
			
		||||
 | 
			
		||||
typedef enum FILE_TRANSFER_STATE {
 | 
			
		||||
    FILE_TRANSFER_INACTIVE,
 | 
			
		||||
@@ -62,15 +61,12 @@ struct FileTransfer {
 | 
			
		||||
    size_t   index;
 | 
			
		||||
    uint64_t file_size;
 | 
			
		||||
    uint64_t position;
 | 
			
		||||
    uint64_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_line_progress;   /* The last time we updated the progress bar */
 | 
			
		||||
    time_t   last_keep_alive;  /* The last time we sent or received data */
 | 
			
		||||
    uint32_t line_id;
 | 
			
		||||
    uint8_t  file_id[TOX_FILE_ID_LENGTH];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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.
 | 
			
		||||
   progline must be at lesat MAX_STR_SIZE bytes */
 | 
			
		||||
void init_progress_bar(char *progline);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										128
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								src/friendlist.c
									
									
									
									
									
								
							@@ -25,6 +25,7 @@
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
#include <tox/tox.h>
 | 
			
		||||
 | 
			
		||||
@@ -112,31 +113,38 @@ static void realloc_blocklist(int n)
 | 
			
		||||
 | 
			
		||||
void kill_friendlist(void)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < Friends.max_idx; ++i) {
 | 
			
		||||
        if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL)
 | 
			
		||||
            free(Friends.list[i].group_invite.key);
 | 
			
		||||
        if (Friends.list[i].group_invite.data != NULL)
 | 
			
		||||
            free(Friends.list[i].group_invite.data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    realloc_blocklist(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)
 | 
			
		||||
{
 | 
			
		||||
    if (path == NULL)
 | 
			
		||||
    if (path == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int len = sizeof(BlockedFriend) * Blocked.num_blocked;
 | 
			
		||||
    char *data = malloc(len);
 | 
			
		||||
    char *data = malloc(len * sizeof(char));
 | 
			
		||||
 | 
			
		||||
    if (data == NULL)
 | 
			
		||||
        exit_toxic_err("Failed in save_blocklist", FATALERR_MEMORY);
 | 
			
		||||
    if (data == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    int count = 0;
 | 
			
		||||
    int i, count = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < Blocked.max_idx; ++i) {
 | 
			
		||||
        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) {
 | 
			
		||||
        free(data);
 | 
			
		||||
@@ -169,6 +191,7 @@ static int save_blocklist(char *path)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fwrite(data, len, 1, fp) != 1) {
 | 
			
		||||
        fprintf(stderr, "Failed to write blocklist data.\n");
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        free(data);
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -176,6 +199,11 @@ static int save_blocklist(char *path)
 | 
			
		||||
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
    free(data);
 | 
			
		||||
 | 
			
		||||
    if (rename(temp_path, path) != 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -198,22 +226,15 @@ int load_blocklist(char *path)
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char *data = malloc(len);
 | 
			
		||||
 | 
			
		||||
    if (data == NULL) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        exit_toxic_err("Failed in load_blocklist", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
    char data[len];
 | 
			
		||||
 | 
			
		||||
    if (fread(data, len, 1, fp) != 1) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        free(data);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (len % sizeof(BlockedFriend) != 0) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        free(data);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -231,7 +252,7 @@ int load_blocklist(char *path)
 | 
			
		||||
        memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
 | 
			
		||||
        Blocked.list[i].active = true;
 | 
			
		||||
        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].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE);
 | 
			
		||||
 | 
			
		||||
@@ -243,7 +264,6 @@ int load_blocklist(char *path)
 | 
			
		||||
        ++Blocked.num_blocked;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(data);
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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.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 */
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
            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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, const char *group_pub_key,
 | 
			
		||||
                                     uint16_t length)
 | 
			
		||||
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, uint32_t num, const char *data, size_t length)
 | 
			
		||||
{
 | 
			
		||||
    if (num >= Friends.max_idx)
 | 
			
		||||
        return;
 | 
			
		||||
@@ -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)
 | 
			
		||||
{
 | 
			
		||||
    TOX_ERR_FRIEND_DELETE err;
 | 
			
		||||
 | 
			
		||||
    if (tox_friend_delete(m, f_num, &err) != true) {
 | 
			
		||||
        fprintf(stderr, "tox_friend_delete failed with error %d\n", err);
 | 
			
		||||
        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));
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
@@ -625,10 +642,12 @@ static void draw_del_popup(void)
 | 
			
		||||
    wattron(PendingDelete.popup, A_BOLD);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    if (blocklist_view == 0)
 | 
			
		||||
        wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name);
 | 
			
		||||
    else
 | 
			
		||||
        wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -752,7 +771,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    switch (key) {
 | 
			
		||||
        case '\n':
 | 
			
		||||
        case '\r':
 | 
			
		||||
            if (blocklist_view)
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
@@ -779,6 +798,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
                block_friend(m, f);
 | 
			
		||||
            else
 | 
			
		||||
                unblock_friend(m, f);
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            else
 | 
			
		||||
                select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked);
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -890,7 +911,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
    wattron(self->window, A_BOLD);
 | 
			
		||||
@@ -950,9 +971,11 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
                    case TOX_USER_STATUS_NONE:
 | 
			
		||||
                        colour = GREEN;
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case TOX_USER_STATUS_AWAY:
 | 
			
		||||
                        colour = YELLOW;
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case TOX_USER_STATUS_BUSY:
 | 
			
		||||
                        colour = RED;
 | 
			
		||||
                        break;
 | 
			
		||||
@@ -1027,7 +1050,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
                    wattroff(self->window, COLOR_PAIR(BLUE));
 | 
			
		||||
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
                if (last_seen != 0) {
 | 
			
		||||
@@ -1089,23 +1112,25 @@ void disable_chatwin(uint32_t f_num)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
    assert(0);
 | 
			
		||||
    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 (toxav_get_call_state(av, call_index) == av_CallStarting) { /* Only open windows when call is incoming */
 | 
			
		||||
                Friends.list[id].chatwin = add_window(m, new_chat(m, Friends.list[id].num));
 | 
			
		||||
            if (state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
 | 
			
		||||
                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 {
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
            const char *errmsg = "* Warning: Too many windows are open.";
 | 
			
		||||
@@ -1137,22 +1162,21 @@ ToxWindow new_friendlist(void)
 | 
			
		||||
    ret.onGroupInvite = &friendlist_onGroupInvite;
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    ret.onInvite = &friendlist_onAv;
 | 
			
		||||
    ret.onRinging = &friendlist_onAv;
 | 
			
		||||
    ret.onStarting = &friendlist_onAv;
 | 
			
		||||
    ret.onEnding = &friendlist_onAv;
 | 
			
		||||
    ret.onError = &friendlist_onAv;
 | 
			
		||||
    ret.onStart = &friendlist_onAv;
 | 
			
		||||
    ret.onCancel = &friendlist_onAv;
 | 
			
		||||
    ret.onReject = &friendlist_onAv;
 | 
			
		||||
    ret.onEnd = &friendlist_onAv;
 | 
			
		||||
    ret.onRequestTimeout = &friendlist_onAv;
 | 
			
		||||
    ret.onPeerTimeout = &friendlist_onAv;
 | 
			
		||||
    ret.onInvite = &friendlist_onAV;
 | 
			
		||||
    ret.onRinging = &friendlist_onAV;
 | 
			
		||||
    ret.onStarting = &friendlist_onAV;
 | 
			
		||||
    ret.onEnding = &friendlist_onAV;
 | 
			
		||||
    ret.onError = &friendlist_onAV;
 | 
			
		||||
    ret.onStart = &friendlist_onAV;
 | 
			
		||||
    ret.onCancel = &friendlist_onAV;
 | 
			
		||||
    ret.onReject = &friendlist_onAV;
 | 
			
		||||
    ret.onEnd = &friendlist_onAV;
 | 
			
		||||
 | 
			
		||||
    ret.call_idx = -1;
 | 
			
		||||
    ret.is_call = false;
 | 
			
		||||
    ret.device_selection[0] = ret.device_selection[1] = -1;
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
    ret.num = -1;
 | 
			
		||||
    ret.active_box = -1;
 | 
			
		||||
 | 
			
		||||
    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" */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GroupChatInvite {
 | 
			
		||||
    char *key;
 | 
			
		||||
struct GroupInvite {
 | 
			
		||||
    uint8_t *data;
 | 
			
		||||
    uint16_t length;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    bool pending;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -57,7 +55,7 @@ typedef struct {
 | 
			
		||||
    uint8_t status;
 | 
			
		||||
 | 
			
		||||
    struct LastOnline last_online;
 | 
			
		||||
    struct GroupChatInvite group_invite;
 | 
			
		||||
    struct GroupInvite group_invite;
 | 
			
		||||
 | 
			
		||||
    struct FileTransfer file_receiver[MAX_FILES];
 | 
			
		||||
    struct FileTransfer file_sender[MAX_FILES];
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
@@ -30,12 +29,14 @@
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "dns.h"
 | 
			
		||||
#include "groupchat.h"
 | 
			
		||||
#include "prompt.h"
 | 
			
		||||
#include "help.h"
 | 
			
		||||
#include "term_mplex.h"
 | 
			
		||||
#include "avatars.h"
 | 
			
		||||
#include "name_lookup.h"
 | 
			
		||||
#include "qr_code.h"
 | 
			
		||||
#include "toxic_strings.h"
 | 
			
		||||
 | 
			
		||||
extern char *DATA_FILE;
 | 
			
		||||
extern ToxWindow *prompt;
 | 
			
		||||
@@ -50,9 +51,9 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int req = atoi(argv[1]);
 | 
			
		||||
    long int req = strtol(argv[1], NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
 | 
			
		||||
    if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -129,6 +130,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_FRIEND_ADD_NULL:
 | 
			
		||||
 | 
			
		||||
        /* fallthrough */
 | 
			
		||||
        default:
 | 
			
		||||
            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);
 | 
			
		||||
    } else {    /* assume id is a username@domain address and do DNS lookup */
 | 
			
		||||
        dns3_lookup(self, m, id_bin, id, msg);
 | 
			
		||||
    } else {    /* assume id is a username@domain address and do http name server lookup */
 | 
			
		||||
        name_lookup(self, m, id_bin, id, msg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -248,23 +250,26 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *ip = argv[1];
 | 
			
		||||
    const char *port = argv[2];
 | 
			
		||||
    const char *port_str = argv[2];
 | 
			
		||||
    const char *ascii_key = argv[3];
 | 
			
		||||
 | 
			
		||||
    if (atoi(port) == 0) {
 | 
			
		||||
    long int port = strtol(port_str, NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if (port <= 0 || port > MAX_PORT_RANGE) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
 | 
			
		||||
 | 
			
		||||
    if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_BOOTSTRAP err;
 | 
			
		||||
    tox_bootstrap(m, ip, atoi(port), (uint8_t *) key_binary, &err);
 | 
			
		||||
    tox_add_tcp_relay(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, port, (uint8_t *) key_binary, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        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:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -290,9 +296,9 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int req = atoi(argv[1]);
 | 
			
		||||
    long int req = strtol(argv[1], NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
 | 
			
		||||
    if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -323,42 +329,133 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    const char *tmp_name = argv[1];
 | 
			
		||||
    int len = strlen(tmp_name);
 | 
			
		||||
 | 
			
		||||
    if (!strcasecmp(argv[1], "audio"))
 | 
			
		||||
        type = TOX_GROUPCHAT_TYPE_AV;
 | 
			
		||||
    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");
 | 
			
		||||
    if (len == 0 || len > TOX_GROUP_MAX_GROUP_NAME_LENGTH) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group name.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int groupnum = -1;
 | 
			
		||||
    char name[TOX_GROUP_MAX_GROUP_NAME_LENGTH];
 | 
			
		||||
 | 
			
		||||
    if (type == TOX_GROUPCHAT_TYPE_TEXT)
 | 
			
		||||
        groupnum = tox_add_groupchat(m);
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    else
 | 
			
		||||
        groupnum = toxav_add_av_groupchat(m, NULL, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
    if (argv[1][0] == '\"') {    /* remove opening and closing quotes */
 | 
			
		||||
        snprintf(name, sizeof(name), "%s", &argv[1][1]);
 | 
			
		||||
        len -= 2;
 | 
			
		||||
        name[len] = '\0';
 | 
			
		||||
    } else {
 | 
			
		||||
        snprintf(name, sizeof(name), "%s", argv[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_NEW err;
 | 
			
		||||
    uint32_t groupnum = tox_group_new(m, TOX_GROUP_PRIVACY_STATE_PUBLIC, (uint8_t *) name, len, &err);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_GROUP_NEW_OK) {
 | 
			
		||||
        switch (err) {
 | 
			
		||||
            case TOX_ERR_GROUP_NEW_TOO_LONG: {
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name length cannot exceed %d.",
 | 
			
		||||
                              TOX_GROUP_MAX_GROUP_NAME_LENGTH);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            case TOX_ERR_GROUP_NEW_EMPTY: {
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name cannot be empty.");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            default: {
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d).", err);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
    int init = init_groupchat_win(m, groupnum, name, len);
 | 
			
		||||
 | 
			
		||||
    if (init == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
 | 
			
		||||
        tox_del_groupchat(m, groupnum);
 | 
			
		||||
        tox_group_leave(m, groupnum, NULL, 0, NULL);
 | 
			
		||||
    } else if (init == -2) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
 | 
			
		||||
                      "You have been kicked from a group. Close the window and try again.");
 | 
			
		||||
        tox_group_leave(m, groupnum, NULL, 0, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum);
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Chat ID is required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *chat_id = argv[1];
 | 
			
		||||
 | 
			
		||||
    if (strlen(chat_id) != TOX_GROUP_CHAT_ID_SIZE * 2) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char id_bin[TOX_GROUP_CHAT_ID_SIZE] = {0};
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
    char xch[3];
 | 
			
		||||
    uint32_t x;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
 | 
			
		||||
        xch[0] = chat_id[2 * i];
 | 
			
		||||
        xch[1] = chat_id[2 * i + 1];
 | 
			
		||||
        xch[2] = '\0';
 | 
			
		||||
 | 
			
		||||
        if (sscanf(xch, "%02x", &x) != 1) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        id_bin[i] = x;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *passwd = NULL;
 | 
			
		||||
    uint16_t passwd_len = 0;
 | 
			
		||||
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        passwd = argv[2];
 | 
			
		||||
        passwd_len = strlen(passwd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_JOIN err;
 | 
			
		||||
    uint32_t groupnum = tox_group_join(m, (uint8_t *) id_bin, (uint8_t *) passwd, passwd_len, &err);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_GROUP_JOIN_OK) {
 | 
			
		||||
        if (err == TOX_ERR_GROUP_JOIN_TOO_LONG)
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length cannot exceed %d.", TOX_GROUP_MAX_PASSWORD_SIZE);
 | 
			
		||||
        else
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err);
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int init = init_groupchat_win(m, groupnum, NULL, 0);
 | 
			
		||||
 | 
			
		||||
    if (init == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
 | 
			
		||||
        tox_group_leave(m, groupnum, NULL, 0, NULL);
 | 
			
		||||
    } else if (init == -2) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
 | 
			
		||||
                      "You have been kicked from a group. Close the window and try again.");
 | 
			
		||||
        tox_group_leave(m, groupnum, NULL, 0, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
@@ -368,9 +465,9 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
 | 
			
		||||
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
        if (log->log_on)
 | 
			
		||||
            msg = "Logging for this window is ON. Type \"/log off\" to disable.";
 | 
			
		||||
            msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)";
 | 
			
		||||
        else
 | 
			
		||||
            msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
 | 
			
		||||
            msg = "Logging for this window is OFF; type \"/log on\" to enable.";
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
        return;
 | 
			
		||||
@@ -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])
 | 
			
		||||
{
 | 
			
		||||
    char id[TOX_ADDRESS_SIZE * 2 + 1] = {0};
 | 
			
		||||
    char address[TOX_ADDRESS_SIZE];
 | 
			
		||||
    tox_self_get_address(m, (uint8_t *) address);
 | 
			
		||||
    char id_string[TOX_ADDRESS_SIZE * 2 + 1];
 | 
			
		||||
    char bin_id[TOX_ADDRESS_SIZE];
 | 
			
		||||
    tox_self_get_address(m, (uint8_t *) bin_id);
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
 | 
			
		||||
        char xx[3];
 | 
			
		||||
        snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff);
 | 
			
		||||
        strcat(id, xx);
 | 
			
		||||
    if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    char id_string[TOX_ADDRESS_SIZE * 2 + 1];
 | 
			
		||||
    char bin_id[TOX_ADDRESS_SIZE];
 | 
			
		||||
    tox_self_get_address(m, (uint8_t *) bin_id);
 | 
			
		||||
 | 
			
		||||
    if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char nick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    tox_self_get_name(m, (uint8_t *) nick);
 | 
			
		||||
    size_t nick_len = tox_self_get_name_size(m);
 | 
			
		||||
    nick[nick_len] = '\0';
 | 
			
		||||
 | 
			
		||||
    size_t data_file_len = strlen(DATA_FILE);
 | 
			
		||||
    char dir[data_file_len + 1];
 | 
			
		||||
    size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
 | 
			
		||||
 | 
			
		||||
#ifdef QRPNG
 | 
			
		||||
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'");
 | 
			
		||||
        return;
 | 
			
		||||
    } else if (!strcmp(argv[1], "txt")) {
 | 
			
		||||
 | 
			
		||||
#endif /* QRPNG */
 | 
			
		||||
        char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1];
 | 
			
		||||
        snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
 | 
			
		||||
 | 
			
		||||
        if (ID_to_QRcode_txt(id_string, qr_path) == -1) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
 | 
			
		||||
 | 
			
		||||
#ifdef QRPNG
 | 
			
		||||
    } else if (!strcmp(argv[1], "png")) {
 | 
			
		||||
        char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1];
 | 
			
		||||
        snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG);
 | 
			
		||||
 | 
			
		||||
        if (ID_to_QRcode_png(id_string, qr_path) == -1) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif /* QRPNG */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
@@ -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);
 | 
			
		||||
    prompt_update_nick(prompt, nick);
 | 
			
		||||
    set_nick_all_groups(m, nick, len);
 | 
			
		||||
 | 
			
		||||
    store_data(m, DATA_FILE);
 | 
			
		||||
}
 | 
			
		||||
@@ -468,18 +621,32 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argv[1][0] != '\"') {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
 | 
			
		||||
    prompt_update_statusmessage(prompt, m, argv[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    long int nospam = rand();
 | 
			
		||||
 | 
			
		||||
    if (argc > 0) {
 | 
			
		||||
        nospam = strtol(argv[1], NULL, 16);
 | 
			
		||||
 | 
			
		||||
        if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* remove opening and closing quotes */
 | 
			
		||||
    char msg[MAX_STR_SIZE];
 | 
			
		||||
    snprintf(msg, sizeof(msg), "%s", &argv[1][1]);
 | 
			
		||||
    int len = strlen(msg) - 1;
 | 
			
		||||
    msg[len] = '\0';
 | 
			
		||||
    uint32_t old_nospam = tox_self_get_nospam(m);
 | 
			
		||||
    tox_self_set_nospam(m, (uint32_t) nospam);
 | 
			
		||||
 | 
			
		||||
    prompt_update_statusmessage(prompt, m, msg);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:");
 | 
			
		||||
    cmd_myid(window, self, m, 0, NULL);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
 | 
			
		||||
                  "Any services that relied on your old ID will need to be updated manually.");
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'",
 | 
			
		||||
                  old_nospam);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
@@ -554,6 +721,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
 | 
			
		||||
 | 
			
		||||
    tox_self_set_status(m, status);
 | 
			
		||||
    prompt_update_status(prompt, status);
 | 
			
		||||
    set_status_all_groups(m, status);
 | 
			
		||||
 | 
			
		||||
    if (have_note) {
 | 
			
		||||
        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_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
@@ -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]);
 | 
			
		||||
#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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -21,59 +21,707 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "groupchat.h"
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH);
 | 
			
		||||
 | 
			
		||||
        if (tlen != -1) {
 | 
			
		||||
            title[tlen] = '\0';
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
 | 
			
		||||
        } else {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argv[1][0] != '\"') {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* remove opening and closing quotes */
 | 
			
		||||
    snprintf(title, sizeof(title), "%s", &argv[1][1]);
 | 
			
		||||
    int len = strlen(title) - 1;
 | 
			
		||||
    title[len] = '\0';
 | 
			
		||||
    TOX_ERR_GROUP_TOGGLE_IGNORE err;
 | 
			
		||||
 | 
			
		||||
    if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title.");
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    set_window_title(self, title, len);
 | 
			
		||||
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    char selfnick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
 | 
			
		||||
    tox_self_get_name(m, (uint8_t *) selfnick);
 | 
			
		||||
    size_t sn_len = tox_self_get_name_size(m);
 | 
			
		||||
    line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- Ignoring %s", nick);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_kickban_helper(ToxWindow *self, Tox *m, const char *nick, bool set_ban)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t peer_id;
 | 
			
		||||
 | 
			
		||||
    if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *type_str = set_ban ? "ban" : "kick";
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_MOD_REMOVE_PEER err;
 | 
			
		||||
    tox_group_mod_remove_peer(m, self->num, peer_id, set_ban, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_REMOVE_PEER_OK: {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "You do not have permission to %s %s.", type_str, nick);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Failed to %s %s from the group (error %d).", type_str, nick,
 | 
			
		||||
                          err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_kickban_helper(self, m, argv[1], false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    TOX_ERR_GROUP_BAN_QUERY err;
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        size_t num_banned = tox_group_ban_get_list_size(m, self->num, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list size (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (num_banned == 0) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list is empty.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint32_t ban_list[num_banned];
 | 
			
		||||
 | 
			
		||||
        if (!tox_group_ban_get_list(m, self->num, ban_list, &err)) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint16_t i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < num_banned; ++i) {
 | 
			
		||||
            uint32_t id = ban_list[i];
 | 
			
		||||
            size_t len = tox_group_ban_get_name_size(m, self->num, id, &err);
 | 
			
		||||
 | 
			
		||||
            if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name length for ban %d (error %d).", id, err);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            char tmp_nick[len];
 | 
			
		||||
 | 
			
		||||
            if (!tox_group_ban_get_name(m, self->num, id, (uint8_t *) tmp_nick, &err)) {
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name for ban %d (error %d).", id, err);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            char nick[len + 1];
 | 
			
		||||
            copy_tox_str(nick, sizeof(nick), tmp_nick, len);
 | 
			
		||||
 | 
			
		||||
            uint64_t time_set = tox_group_ban_get_time_set(m, self->num, id, &err);
 | 
			
		||||
 | 
			
		||||
            if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve timestamp for ban %d (error %d).", id, err);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            struct tm tm_set = *localtime((const time_t *) &time_set);
 | 
			
		||||
 | 
			
		||||
            char time_str[64];
 | 
			
		||||
 | 
			
		||||
            strftime(time_str, sizeof(time_str), "%e %b %Y %H:%M:%S%p", &tm_set);
 | 
			
		||||
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "ID %d : %s [Set:%s]", id, nick, time_str);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_kickban_helper(self, m, argv[1], true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be specified.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ban_id = atoi(argv[1]);
 | 
			
		||||
 | 
			
		||||
    if (ban_id == 0 && strcmp(argv[1], "0")) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be a non-negative interger.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_MOD_REMOVE_BAN err;
 | 
			
		||||
    tox_group_mod_remove_ban(m, self->num, ban_id, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_REMOVE_BAN_OK: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list entry with id %d has been removed.", ban_id);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unban peers.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID does not exist.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to remove ban list entry (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *nick = argv[1];
 | 
			
		||||
    uint32_t peer_id;
 | 
			
		||||
 | 
			
		||||
    if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_MOD_SET_ROLE err;
 | 
			
		||||
    tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_MODERATOR, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "You do not have permission to promote moderators.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "This peer is already a moderator.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Failed to promote peer to moderator (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *nick = argv[1];
 | 
			
		||||
    uint32_t peer_id;
 | 
			
		||||
 | 
			
		||||
    if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_MODERATOR) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not a moderator", nick);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_MOD_SET_ROLE err;
 | 
			
		||||
    tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Nice try.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Failed to revoke moderator powers from %s (error %d).", nick,
 | 
			
		||||
                          err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
 | 
			
		||||
    char pk[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_SELF_QUERY err;
 | 
			
		||||
 | 
			
		||||
    if (!tox_group_self_get_public_key(m, self->num, (uint8_t *) pk, &err)) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch your public key (error %d)", err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
 | 
			
		||||
        char d[3];
 | 
			
		||||
        snprintf(d, sizeof(d), "%02X", pk[i] & 0xff);
 | 
			
		||||
        strcat(pk_string, d);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", pk_string);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    const char *passwd = NULL;
 | 
			
		||||
    size_t len = 0;
 | 
			
		||||
 | 
			
		||||
    if (argc > 0) {
 | 
			
		||||
        passwd = argv[1];
 | 
			
		||||
        len = strlen(passwd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_FOUNDER_SET_PASSWORD err;
 | 
			
		||||
    tox_group_founder_set_password(m, self->num, (uint8_t *) passwd, len, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK: {
 | 
			
		||||
            if (len > 0)
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been set to %s.", passwd);
 | 
			
		||||
            else
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been unset.");
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length must not exceed %d.",
 | 
			
		||||
                          TOX_GROUP_MAX_PASSWORD_SIZE);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the password.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set password (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    int maxpeers = 0;
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        TOX_ERR_GROUP_STATE_QUERIES err;
 | 
			
		||||
        uint32_t maxpeers = tox_group_get_peer_limit(m, self->num, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve peer limit (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit is set to %d", maxpeers);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    maxpeers = atoi(argv[1]);
 | 
			
		||||
 | 
			
		||||
    if (maxpeers <= 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit must be a value greater than 0.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT err;
 | 
			
		||||
    tox_group_founder_set_peer_limit(m, self->num, maxpeers, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit has been set to %d.", maxpeers);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the peer limit.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the peer limit (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    const char *pstate_str = NULL;
 | 
			
		||||
    TOX_GROUP_PRIVACY_STATE privacy_state;
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        TOX_ERR_GROUP_STATE_QUERIES err;
 | 
			
		||||
        privacy_state = tox_group_get_privacy_state(m, self->num, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve privacy state (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pstate_str = privacy_state == TOX_GROUP_PRIVACY_STATE_PRIVATE ? "private" : "public";
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state is set to %s.", pstate_str);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pstate_str = argv[1];
 | 
			
		||||
 | 
			
		||||
    if (strcasecmp(pstate_str, "private") != 0 && strcasecmp(pstate_str, "public") != 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state must be \"private\" or \"public\".");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    privacy_state = strcasecmp(pstate_str,
 | 
			
		||||
                               "private") == 0 ? TOX_GROUP_PRIVACY_STATE_PRIVATE : TOX_GROUP_PRIVACY_STATE_PUBLIC;
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE err;
 | 
			
		||||
    tox_group_founder_set_privacy_state(m, self->num, privacy_state, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state has been set to %s.", pstate_str);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the privacy state.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error setting privacy state (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *nick = argv[1];
 | 
			
		||||
    uint32_t peer_id;
 | 
			
		||||
 | 
			
		||||
    if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_MOD_SET_ROLE err;
 | 
			
		||||
    tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_OBSERVER, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to silence %s.", nick);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to silence %s (error %d).", nick, err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *nick = argv[1];
 | 
			
		||||
    uint32_t peer_id;
 | 
			
		||||
 | 
			
		||||
    if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,  "Invalid peer name '%s'.", nick);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_OBSERVER) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_MOD_SET_ROLE err;
 | 
			
		||||
    tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unsilence %s.", nick);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unsilence %s (error %d).", nick, err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    TOX_ERR_GROUP_RECONNECT err;
 | 
			
		||||
 | 
			
		||||
    if (!tox_group_reconnect(m, self->num, &err)) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to rejoin group (error %d).", err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Reconnecting to group...");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        TOX_ERR_GROUP_STATE_QUERIES err;
 | 
			
		||||
        size_t tlen = tox_group_get_topic_size(m, self->num, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic length (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (tlen > 0) {
 | 
			
		||||
            char cur_topic[tlen];
 | 
			
		||||
 | 
			
		||||
            if (!tox_group_get_topic(m, self->num, (uint8_t *) cur_topic, &err)) {
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic (error %d).", err);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            cur_topic[tlen] = '\0';
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is set to: %s", cur_topic);
 | 
			
		||||
        } else {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is not set.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *topic = argv[1];
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_TOPIC_SET err;
 | 
			
		||||
    tox_group_set_topic(m, self->num, (uint8_t *) topic, strlen(topic), &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_GROUP_TOPIC_SET_OK: {
 | 
			
		||||
            /* handled below switch */
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_TOPIC_SET_TOO_LONG: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic length must not exceed %d.", TOX_GROUP_MAX_TOPIC_LENGTH);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default: {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the topic (error %d).", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_GROUP_SELF_QUERY sn_err;
 | 
			
		||||
    size_t sn_len = tox_group_self_get_name_size(m, self->num, &sn_err);
 | 
			
		||||
    char selfnick[sn_len];
 | 
			
		||||
 | 
			
		||||
    if (!tox_group_self_get_name(m, self->num, (uint8_t *) selfnick, &sn_err)) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve your own name (error %d).", sn_err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    selfnick[sn_len] = '\0';
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, timefrmt, 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];
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 "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 */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1459
									
								
								src/groupchat.c
									
									
									
									
									
								
							
							
						
						
									
										1459
									
								
								src/groupchat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -26,61 +26,42 @@
 | 
			
		||||
#include "toxic.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 SDBAR_OFST 2    /* Offset for the peer number box at the top of the statusbar */
 | 
			
		||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
 | 
			
		||||
#define GROUP_EVENT_WAIT 3
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
struct GAudio {
 | 
			
		||||
    ALCdevice  *dvhandle;    /* Handle of device selected/opened */
 | 
			
		||||
    ALCcontext *dvctx;
 | 
			
		||||
    ALuint source;
 | 
			
		||||
    ALuint buffers[OPENAL_BUFS];
 | 
			
		||||
struct GroupPeer {
 | 
			
		||||
    bool       active;
 | 
			
		||||
    char       name[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    size_t     name_length;
 | 
			
		||||
    uint32_t   peer_id;
 | 
			
		||||
    uint8_t    public_key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
 | 
			
		||||
    TOX_USER_STATUS status;
 | 
			
		||||
    TOX_GROUP_ROLE  role;
 | 
			
		||||
    uint64_t   last_active;
 | 
			
		||||
};
 | 
			
		||||
#endif  /* AUDIO */
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    struct GroupPeer *peer_list;
 | 
			
		||||
    char       *name_list;    /* List of peer names, needed for tab completion */
 | 
			
		||||
    uint32_t   num_peers;     /* Number of peers in the chat/name_list array */
 | 
			
		||||
    uint32_t   max_idx;       /* Maximum peer list index - 1 */
 | 
			
		||||
    uint32_t   groupnumber;
 | 
			
		||||
    int        chatwin;
 | 
			
		||||
    bool       active;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    int num_peers;
 | 
			
		||||
    uint64_t   time_connected;    /* The time we successfully connected to the group */
 | 
			
		||||
    int        side_pos;     /* current position of the sidebar - used for scrolling up and down */
 | 
			
		||||
    uint64_t start_time;
 | 
			
		||||
    uint8_t  *peer_names;
 | 
			
		||||
    uint8_t  *oldpeer_names;
 | 
			
		||||
    uint16_t *peer_name_lengths;
 | 
			
		||||
    uint16_t *oldpeer_name_lengths;
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    struct GAudio audio;
 | 
			
		||||
#endif
 | 
			
		||||
} GroupChat;
 | 
			
		||||
 | 
			
		||||
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
 | 
			
		||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type);
 | 
			
		||||
void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum);
 | 
			
		||||
int init_groupchat_win(Tox *m, uint32_t groupnum, const char *groupname, size_t length);
 | 
			
		||||
void set_nick_all_groups(Tox *m, const char *nick, size_t length);
 | 
			
		||||
void set_status_all_groups(Tox *m, uint8_t status);
 | 
			
		||||
int group_get_nick_peer_id(uint32_t groupnum, const char *nick, uint32_t *peer_id);
 | 
			
		||||
int get_peer_index(uint32_t groupnum, uint32_t peer_id);
 | 
			
		||||
 | 
			
		||||
/* destroys and re-creates groupchat window with or without the peerlist */
 | 
			
		||||
/* destroys and re-creates groupchat window */
 | 
			
		||||
void redraw_groupchat_win(ToxWindow *self);
 | 
			
		||||
 | 
			
		||||
ToxWindow new_group_chat(Tox *m, int groupnum);
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
    getmaxyx(stdscr, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    height = MIN(height, y2);
 | 
			
		||||
    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, "  /status <type> <msg>       : Set status with optional note\n");
 | 
			
		||||
    wprintw(win, "  /note <msg>                : Set a personal note\n");
 | 
			
		||||
    wprintw(win, "  /group                     : Create a group chat\n");
 | 
			
		||||
    wprintw(win, "  /join <chat id> <passwd>   : Join a group chat with optional password\n");
 | 
			
		||||
    wprintw(win, "  /nick <nick>               : Set your nickname\n");
 | 
			
		||||
    wprintw(win, "  /nospam <value>            : Change part of your Tox ID to stop spam\n");
 | 
			
		||||
    wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n");
 | 
			
		||||
    wprintw(win, "  /group <type>              : Create a group chat where type: text | audio\n");
 | 
			
		||||
    wprintw(win, "  /myid                      : Print your Tox ID\n");
 | 
			
		||||
#ifdef QRPNG
 | 
			
		||||
    wprintw(win, "  /myqr <txt> or <png>       : Print your Tox ID's QR code to a file.\n");
 | 
			
		||||
#else
 | 
			
		||||
    wprintw(win, "  /myqr                      : Print your Tox ID's QR code to a file.\n");
 | 
			
		||||
#endif /* QRPNG */
 | 
			
		||||
    wprintw(win, "  /clear                     : Clear window history\n");
 | 
			
		||||
    wprintw(win, "  /close                     : Close the current chat window\n");
 | 
			
		||||
    wprintw(win, "  /quit or /exit             : Exit Toxic\n");
 | 
			
		||||
@@ -167,6 +177,15 @@ static void help_draw_global(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "  /sdev <type> <id>          : Set active device\n");
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    wattron(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "\n Video:\n");
 | 
			
		||||
    wattroff(win, A_BOLD);
 | 
			
		||||
 | 
			
		||||
    wprintw(win, "  /lsvdev <type>             : List video devices where type: in|out\n");
 | 
			
		||||
    wprintw(win, "  /svdev <type> <id>         : Set active video device\n");
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
@@ -183,8 +202,7 @@ static void help_draw_chat(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "Chat Commands:\n");
 | 
			
		||||
    wattroff(win, A_BOLD | COLOR_PAIR(RED));
 | 
			
		||||
 | 
			
		||||
    wprintw(win, "  /invite <n>                : Invite contact to a group chat\n");
 | 
			
		||||
    wprintw(win, "  /join                      : Join a pending group chat\n");
 | 
			
		||||
    wprintw(win, "  /gaccept <password>        : Accept a group invite with optional password\n");
 | 
			
		||||
    wprintw(win, "  /sendfile <path>           : Send a file\n");
 | 
			
		||||
    wprintw(win, "  /savefile <id>             : Receive a file\n");
 | 
			
		||||
    wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n");
 | 
			
		||||
@@ -203,6 +221,13 @@ static void help_draw_chat(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n");
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    wattron(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "\n Video:\n");
 | 
			
		||||
    wattroff(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "  /video                     : Toggle video call\n");
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
@@ -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+H                    : Move to the bottom of window history\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+J                    : Insert new line\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+T                    : Toggle paste mode\n\n");
 | 
			
		||||
    wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n");
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
@@ -243,7 +270,32 @@ static void help_draw_group(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "Group commands:\n");
 | 
			
		||||
    wattroff(win, A_BOLD | COLOR_PAIR(RED));
 | 
			
		||||
 | 
			
		||||
    wprintw(win, "  /title <msg>               : Set group title (show current title if no msg)\n\n");
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
@@ -275,32 +327,36 @@ static void help_draw_contacts(ToxWindow *self)
 | 
			
		||||
 | 
			
		||||
void help_onKey(ToxWindow *self, wint_t key)
 | 
			
		||||
{
 | 
			
		||||
    switch(key) {
 | 
			
		||||
    switch (key) {
 | 
			
		||||
        case 'x':
 | 
			
		||||
        case T_KEY_ESC:
 | 
			
		||||
            help_exit(self);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'c':
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
            help_init_window(self, 19, 80);
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            help_init_window(self, 21, 80);
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
            help_init_window(self, 18, 80);
 | 
			
		||||
#else
 | 
			
		||||
            help_init_window(self, 9, 80);
 | 
			
		||||
            help_init_window(self, 10, 80);
 | 
			
		||||
#endif
 | 
			
		||||
            self->help->type = HELP_CHAT;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'g':
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
            help_init_window(self, 24, 80);
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            help_init_window(self, 30, 80);
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
            help_init_window(self, 26, 80);
 | 
			
		||||
#else
 | 
			
		||||
            help_init_window(self, 20, 80);
 | 
			
		||||
            help_init_window(self, 22, 80);
 | 
			
		||||
#endif
 | 
			
		||||
            self->help->type = HELP_GLOBAL;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'r':
 | 
			
		||||
            help_init_window(self, 6, 80);
 | 
			
		||||
            help_init_window(self, 25, 80);
 | 
			
		||||
            self->help->type = HELP_GROUP;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
@@ -310,7 +366,7 @@ void help_onKey(ToxWindow *self, wint_t key)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'k':
 | 
			
		||||
            help_init_window(self, 13, 80);
 | 
			
		||||
            help_init_window(self, 15, 80);
 | 
			
		||||
            self->help->type = HELP_KEYS;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
@@ -325,7 +381,7 @@ void help_onDraw(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    curs_set(0);
 | 
			
		||||
 | 
			
		||||
    switch(self->help->type) {
 | 
			
		||||
    switch (self->help->type) {
 | 
			
		||||
        case HELP_MENU:
 | 
			
		||||
            help_draw_menu(self);
 | 
			
		||||
            return;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								src/input.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								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;
 | 
			
		||||
 | 
			
		||||
    /* this is the only place we need to do this check */
 | 
			
		||||
    if (key == '\n')
 | 
			
		||||
        key = L'¶';
 | 
			
		||||
 | 
			
		||||
    int cur_len = wcwidth(key);
 | 
			
		||||
 | 
			
		||||
    /* this is the only place we need to do this check */
 | 
			
		||||
    if (cur_len == -1) {
 | 
			
		||||
        sound_notify(self, notif_error, 0, NULL);
 | 
			
		||||
        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.
 | 
			
		||||
       maybe convert entire function to if/else and make them all customizable keys? */
 | 
			
		||||
    if (!match && key == user_settings->key_toggle_peerlist) {
 | 
			
		||||
    if (!match) {
 | 
			
		||||
        if (key == user_settings->key_toggle_peerlist) {
 | 
			
		||||
            if (self->is_groupchat) {
 | 
			
		||||
                self->show_peerlist ^= 1;
 | 
			
		||||
                redraw_groupchat_win(self);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            match = true;
 | 
			
		||||
        } else if (key == user_settings->key_toggle_pastemode) {
 | 
			
		||||
            self->chatwin->pastemode ^= 1;
 | 
			
		||||
            match = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return match;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -158,17 +158,24 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
 | 
			
		||||
    /* for type-specific formatting in print function */
 | 
			
		||||
    switch (type) {
 | 
			
		||||
        case IN_ACTION:
 | 
			
		||||
 | 
			
		||||
        /* fallthrough */
 | 
			
		||||
        case OUT_ACTION:
 | 
			
		||||
            len += strlen(user_settings->line_normal) + 2;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case IN_MSG:
 | 
			
		||||
 | 
			
		||||
        /* fallthrough */
 | 
			
		||||
        case OUT_MSG:
 | 
			
		||||
            len += strlen(user_settings->line_normal) + 3;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case IN_PRVT_MSG:
 | 
			
		||||
        case OUT_PRVT_MSG:
 | 
			
		||||
            len += strlen(user_settings->line_special) + 3;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case CONNECTION:
 | 
			
		||||
            len += strlen(user_settings->line_join) + 2;
 | 
			
		||||
            break;
 | 
			
		||||
@@ -304,10 +311,14 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
 | 
			
		||||
        switch (type) {
 | 
			
		||||
            case OUT_MSG:
 | 
			
		||||
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case OUT_MSG_READ:
 | 
			
		||||
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case IN_MSG:
 | 
			
		||||
            case IN_PRVT_MSG:
 | 
			
		||||
            case OUT_PRVT_MSG:
 | 
			
		||||
                wattron(win, COLOR_PAIR(BLUE));
 | 
			
		||||
                wprintw(win, "%s ", line->timestr);
 | 
			
		||||
                wattroff(win, COLOR_PAIR(BLUE));
 | 
			
		||||
@@ -320,21 +331,34 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
                    nameclr = CYAN;
 | 
			
		||||
 | 
			
		||||
                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));
 | 
			
		||||
 | 
			
		||||
                if (line->msg[0] == '>')
 | 
			
		||||
                char *msg = line->msg;
 | 
			
		||||
 | 
			
		||||
                while (msg) {
 | 
			
		||||
                    char *line = strsep(&msg, "\n");
 | 
			
		||||
 | 
			
		||||
                    if (line[0] == '>')
 | 
			
		||||
                        wattron(win, COLOR_PAIR(GREEN));
 | 
			
		||||
                else if (line->msg[0] == '<')
 | 
			
		||||
                    else if (line[0] == '<')
 | 
			
		||||
                        wattron(win, COLOR_PAIR(RED));
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "%s", line->msg);
 | 
			
		||||
                    wprintw(win, "%s%c", line, msg ? '\n' : '\0');
 | 
			
		||||
 | 
			
		||||
                if (line->msg[0] == '>')
 | 
			
		||||
                    if (line[0] == '>')
 | 
			
		||||
                        wattroff(win, COLOR_PAIR(GREEN));
 | 
			
		||||
                else if (line->msg[0] == '<')
 | 
			
		||||
                    else if (line[0] == '<')
 | 
			
		||||
                        wattroff(win, COLOR_PAIR(RED));
 | 
			
		||||
 | 
			
		||||
                    // change the \0 set by strsep back to \n
 | 
			
		||||
                    if (msg)
 | 
			
		||||
                        msg[-1] = '\n';
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
 | 
			
		||||
                    wattron(win, COLOR_PAIR(RED));
 | 
			
		||||
                    wprintw(win, " x", line->msg);
 | 
			
		||||
@@ -350,8 +374,10 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case OUT_ACTION_READ:
 | 
			
		||||
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case OUT_ACTION:
 | 
			
		||||
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case IN_ACTION:
 | 
			
		||||
                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) {
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
    else if (key == user_settings->key_scroll_line_up) {
 | 
			
		||||
    } else if (key == user_settings->key_scroll_line_up) {
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
    else if (key == user_settings->key_page_bottom) {
 | 
			
		||||
    } else if (key == user_settings->key_page_bottom) {
 | 
			
		||||
        line_info_reset_start(self, hst);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
    } else {
 | 
			
		||||
        match = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,10 +35,12 @@ enum {
 | 
			
		||||
    SYS_MSG,
 | 
			
		||||
    IN_MSG,
 | 
			
		||||
    OUT_MSG,
 | 
			
		||||
    OUT_MSG_READ,    /* for sent messages that have received a read reply. don't set this with line_info_add */
 | 
			
		||||
    OUT_MSG_READ,    /* for sent messages that have received a read reply. */
 | 
			
		||||
    IN_ACTION,
 | 
			
		||||
    OUT_ACTION,
 | 
			
		||||
    OUT_ACTION_READ,     /* same as OUT_MSG_READ but for actions */
 | 
			
		||||
    IN_PRVT_MSG,   /* PRVT should only be used for groups */
 | 
			
		||||
    OUT_PRVT_MSG,
 | 
			
		||||
    PROMPT,
 | 
			
		||||
    CONNECTION,
 | 
			
		||||
    DISCONNECTION,
 | 
			
		||||
@@ -50,7 +52,7 @@ struct line_info {
 | 
			
		||||
    char name1[TOXIC_MAX_NAME_LENGTH + 1];
 | 
			
		||||
    char name2[TOXIC_MAX_NAME_LENGTH + 1];
 | 
			
		||||
    char msg[MAX_LINE_INFO_MSG_SIZE];
 | 
			
		||||
    uint64_t timestamp;
 | 
			
		||||
    time_t timestamp;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    uint8_t bold;
 | 
			
		||||
    uint8_t colour;
 | 
			
		||||
 
 | 
			
		||||
@@ -262,6 +262,7 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
on_error:
 | 
			
		||||
 | 
			
		||||
    if (log_on)
 | 
			
		||||
        log_enable(src, selfkey, otherkey, log, LOG_CHAT);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
 | 
			
		||||
struct chatlog {
 | 
			
		||||
    FILE *file;
 | 
			
		||||
    uint64_t lastwrite;
 | 
			
		||||
    time_t lastwrite;
 | 
			
		||||
    char path[MAX_STR_SIZE];
 | 
			
		||||
    bool log_on;    /* specific to current chat window */
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ struct cqueue_msg {
 | 
			
		||||
    int line_id;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    uint32_t receipt;
 | 
			
		||||
    uint64_t last_send_try;
 | 
			
		||||
    time_t last_send_try;
 | 
			
		||||
    struct cqueue_msg *next;
 | 
			
		||||
    struct cqueue_msg *prev;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										174
									
								
								src/misc_tools.c
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								src/misc_tools.c
									
									
									
									
									
								
							@@ -26,6 +26,12 @@
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#if defined(__FreeBSD__)
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
@@ -37,8 +43,6 @@
 | 
			
		||||
extern ToxWindow *prompt;
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
 | 
			
		||||
static uint64_t current_unix_time;
 | 
			
		||||
 | 
			
		||||
void hst_to_net(uint8_t *num, uint16_t numbytes)
 | 
			
		||||
{
 | 
			
		||||
#ifndef WORDS_BIGENDIAN
 | 
			
		||||
@@ -54,19 +58,13 @@ void hst_to_net(uint8_t *num, uint16_t numbytes)
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Note: The time functions are not thread safe */
 | 
			
		||||
void update_unix_time(void)
 | 
			
		||||
time_t get_unix_time(void)
 | 
			
		||||
{
 | 
			
		||||
    current_unix_time = (uint64_t) time(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t get_unix_time(void)
 | 
			
		||||
{
 | 
			
		||||
    return current_unix_time;
 | 
			
		||||
    return time(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns 1 if connection has timed out, 0 otherwise */
 | 
			
		||||
int timed_out(uint64_t timestamp, uint64_t timeout)
 | 
			
		||||
int timed_out(time_t timestamp, time_t timeout)
 | 
			
		||||
{
 | 
			
		||||
    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 *timeinfo;
 | 
			
		||||
    uint64_t t = get_unix_time();
 | 
			
		||||
    timeinfo = localtime((const time_t*) &t);
 | 
			
		||||
    time_t t = get_unix_time();
 | 
			
		||||
    timeinfo = localtime((const time_t *) &t);
 | 
			
		||||
    return timeinfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*Puts the current time in buf in the format of [HH:mm:ss] */
 | 
			
		||||
/* Puts the current time in buf in the format of [HH:mm:ss] */
 | 
			
		||||
void get_time_str(char *buf, int bufsize)
 | 
			
		||||
{
 | 
			
		||||
    if (user_settings->timestamps == TIMESTAMPS_OFF) {
 | 
			
		||||
@@ -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 */
 | 
			
		||||
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
 | 
			
		||||
void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
 | 
			
		||||
{
 | 
			
		||||
    if (!secs)
 | 
			
		||||
        return;
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Converts seconds to string in format H hours, m minutes, s seconds */
 | 
			
		||||
void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs)
 | 
			
		||||
{
 | 
			
		||||
    if (!secs)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    long int seconds = secs % 60;
 | 
			
		||||
    long int minutes = (secs % 3600) / 60;
 | 
			
		||||
    long int hours = secs / 3600;
 | 
			
		||||
 | 
			
		||||
    if (!minutes && !hours)
 | 
			
		||||
        snprintf(buf, bufsize, "%ld seconds", seconds);
 | 
			
		||||
    else if (!hours)
 | 
			
		||||
        snprintf(buf, bufsize, "%ld minutes, %ld seconds", minutes, seconds);
 | 
			
		||||
    else
 | 
			
		||||
        snprintf(buf, bufsize, "%ld hours, %ld minutes, %ld seconds", hours, minutes, seconds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
 | 
			
		||||
 * output_size must be exactly half of hex_len.
 | 
			
		||||
@@ -149,6 +165,24 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Converts a binary representation of a Tox ID into a string.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size)
 | 
			
		||||
{
 | 
			
		||||
    if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1))
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
 | 
			
		||||
        snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns 1 if the string is empty, 0 otherwise */
 | 
			
		||||
int string_is_empty(const char *string)
 | 
			
		||||
{
 | 
			
		||||
@@ -158,6 +192,15 @@ int string_is_empty(const char *string)
 | 
			
		||||
    return string[0] == '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns 1 if the string is empty, 0 otherwise */
 | 
			
		||||
int wstring_is_empty(const wchar_t *string)
 | 
			
		||||
{
 | 
			
		||||
    if (!string)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    return string[0] == L'\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* convert a multibyte string to a wide character string and puts in buf. */
 | 
			
		||||
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
 | 
			
		||||
{
 | 
			
		||||
@@ -166,7 +209,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
 | 
			
		||||
    if (n < len)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if ((len = mbstowcs(buf, string, n)) == (size_t) -1)
 | 
			
		||||
    if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return len;
 | 
			
		||||
@@ -180,7 +223,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
 | 
			
		||||
    if (n < len)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if ((len = wcstombs(buf, string, n)) == (size_t) -1)
 | 
			
		||||
    if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return len;
 | 
			
		||||
@@ -263,6 +306,27 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
 | 
			
		||||
    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 */
 | 
			
		||||
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 */
 | 
			
		||||
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);
 | 
			
		||||
        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);
 | 
			
		||||
@@ -309,19 +381,28 @@ int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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 len = MIN(length, size - 1);
 | 
			
		||||
    memcpy(msg, data, len);
 | 
			
		||||
    msg[len] = '\0';
 | 
			
		||||
    return len;
 | 
			
		||||
    if (length > size - 1) {
 | 
			
		||||
        msg[0] = '\0';
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(msg, data, length);
 | 
			
		||||
    msg[length] = '\0';
 | 
			
		||||
    return length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns index of the first instance of ch in s starting at idx.
 | 
			
		||||
   returns length of s if char not found */
 | 
			
		||||
   returns length of s if char not found or 0 if s is NULL. */
 | 
			
		||||
int char_find(int idx, const char *s, char ch)
 | 
			
		||||
{
 | 
			
		||||
    if (!s) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i = idx;
 | 
			
		||||
 | 
			
		||||
    for (i = idx; s[i]; ++i) {
 | 
			
		||||
@@ -332,10 +413,14 @@ int char_find(int idx, const char *s, char ch)
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns index of the last instance of ch in s starting at len
 | 
			
		||||
   returns 0 if char not found (skips 0th index) */
 | 
			
		||||
/* returns index of the last instance of ch in s starting at len.
 | 
			
		||||
   returns 0 if char not found or s is NULL (skips 0th index). */
 | 
			
		||||
int char_rfind(const char *s, char ch, int len)
 | 
			
		||||
{
 | 
			
		||||
    if (!s) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = len; i > 0; --i) {
 | 
			
		||||
@@ -422,3 +507,36 @@ void set_window_title(ToxWindow *self, const char *title, int len)
 | 
			
		||||
 | 
			
		||||
    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 */
 | 
			
		||||
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) */
 | 
			
		||||
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) */
 | 
			
		||||
void get_time_str(char *buf, int bufsize);
 | 
			
		||||
 | 
			
		||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
 | 
			
		||||
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs);
 | 
			
		||||
void get_elapsed_time_str(char *buf, int bufsize, time_t secs);
 | 
			
		||||
 | 
			
		||||
/* Converts seconds to string in format H hours, m minutes, s seconds */
 | 
			
		||||
void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs);
 | 
			
		||||
 | 
			
		||||
/* get the current local time (not thread safe) */
 | 
			
		||||
struct tm *get_time(void);
 | 
			
		||||
 | 
			
		||||
/* updates current unix time (should be run once per do_toxic loop) */
 | 
			
		||||
void update_unix_time(void);
 | 
			
		||||
 | 
			
		||||
/* Returns 1 if the string is empty, 0 otherwise */
 | 
			
		||||
int string_is_empty(const char *string);
 | 
			
		||||
 | 
			
		||||
/* Same as above but for wide character strings */
 | 
			
		||||
int wstring_is_empty(const wchar_t *string);
 | 
			
		||||
 | 
			
		||||
/* convert a multibyte string to a wide character string (must provide buffer) */
 | 
			
		||||
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
 | 
			
		||||
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
/* 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 */
 | 
			
		||||
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 */
 | 
			
		||||
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
 | 
			
		||||
 | 
			
		||||
/* Gets the base directory of path and puts it in dir.
 | 
			
		||||
 * dir must have at least as much space as path_len.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the length of the base directory on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
size_t get_base_dir(const char *path, size_t path_len, char *dir);
 | 
			
		||||
 | 
			
		||||
/* converts str to all lowercase */
 | 
			
		||||
void str_to_lower(char *str);
 | 
			
		||||
 | 
			
		||||
@@ -111,18 +129,19 @@ void str_to_lower(char *str);
 | 
			
		||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
 | 
			
		||||
 | 
			
		||||
/* same as get_nick_truncate but for groupchats */
 | 
			
		||||
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum);
 | 
			
		||||
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum);
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
 | 
			
		||||
/* returns index of the first instance of ch in s starting at idx.
 | 
			
		||||
   returns length of s if char not found */
 | 
			
		||||
   returns length of s if char not found or 0 if s is NULL. */
 | 
			
		||||
int char_find(int idx, const char *s, char ch);
 | 
			
		||||
 | 
			
		||||
/* returns index of the last instance of ch in s
 | 
			
		||||
   returns 0 if char not found */
 | 
			
		||||
/* returns index of the last instance of ch in s starting at len.
 | 
			
		||||
   returns 0 if char not found or s is NULL (skips 0th index). */
 | 
			
		||||
int char_rfind(const char *s, char ch, int len);
 | 
			
		||||
 | 
			
		||||
/* Converts bytes to appropriate unit and puts in buf as a string */
 | 
			
		||||
@@ -143,4 +162,16 @@ int check_file_signature(const char *signature, size_t size, FILE *fp);
 | 
			
		||||
/* sets window title in tab bar. */
 | 
			
		||||
void set_window_title(ToxWindow *self, const char *title, int len);
 | 
			
		||||
 | 
			
		||||
/* Return true if address appears to be a valid ipv4 address. */
 | 
			
		||||
bool is_ip4_address(const char *address);
 | 
			
		||||
 | 
			
		||||
/* Return true if address roughly appears to be a valid ipv6 address.
 | 
			
		||||
 *
 | 
			
		||||
 * TODO: Improve this function (inet_pton behaves strangely with ipv6).
 | 
			
		||||
 * for now the only guarantee is that it won't return true if the
 | 
			
		||||
 * address is a domain or ipv4 address, and should only be used if you're
 | 
			
		||||
 * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
 | 
			
		||||
 */
 | 
			
		||||
bool is_ip6_address(const char *address);
 | 
			
		||||
 | 
			
		||||
#endif /* #define MISC_TOOLS_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										401
									
								
								src/name_lookup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										401
									
								
								src/name_lookup.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,401 @@
 | 
			
		||||
/*  name_lookup.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "global_commands.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "configdir.h"
 | 
			
		||||
#include "curl_util.h"
 | 
			
		||||
 | 
			
		||||
extern struct arg_opts arg_opts;
 | 
			
		||||
extern struct Winthread Winthread;
 | 
			
		||||
 | 
			
		||||
#define NAMESERVER_API_PATH "api"
 | 
			
		||||
#define SERVER_KEY_SIZE 32
 | 
			
		||||
#define MAX_SERVERS 50
 | 
			
		||||
#define MAX_DOMAIN_SIZE 32
 | 
			
		||||
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
 | 
			
		||||
 | 
			
		||||
struct Nameservers {
 | 
			
		||||
    int     lines;
 | 
			
		||||
    char    names[MAX_SERVERS][MAX_DOMAIN_SIZE];
 | 
			
		||||
    char    keys[MAX_SERVERS][SERVER_KEY_SIZE];
 | 
			
		||||
} Nameservers;
 | 
			
		||||
 | 
			
		||||
static struct thread_data {
 | 
			
		||||
    Tox       *m;
 | 
			
		||||
    ToxWindow *self;
 | 
			
		||||
    char    id_bin[TOX_ADDRESS_SIZE];
 | 
			
		||||
    char    addr[MAX_STR_SIZE];
 | 
			
		||||
    char    msg[MAX_STR_SIZE];
 | 
			
		||||
    bool    disabled;
 | 
			
		||||
    volatile bool busy;
 | 
			
		||||
} t_data;
 | 
			
		||||
 | 
			
		||||
static struct lookup_thread {
 | 
			
		||||
    pthread_t tid;
 | 
			
		||||
    pthread_attr_t attr;
 | 
			
		||||
} lookup_thread;
 | 
			
		||||
 | 
			
		||||
static int lookup_error(ToxWindow *self, const char *errmsg, ...)
 | 
			
		||||
{
 | 
			
		||||
    char frmt_msg[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start(args, errmsg);
 | 
			
		||||
    vsnprintf(frmt_msg, sizeof(frmt_msg), errmsg, args);
 | 
			
		||||
    va_end(args);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg);
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void kill_lookup_thread(void)
 | 
			
		||||
{
 | 
			
		||||
    memset(&t_data, 0, sizeof(struct thread_data));
 | 
			
		||||
    pthread_attr_destroy(&lookup_thread.attr);
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Attempts to load the nameserver list pointed at by path into the Nameservers structure.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * -1 is reserved.
 | 
			
		||||
 * Returns -2 if the supplied path does not exist.
 | 
			
		||||
 * Returns -3 if the list does not contain any valid entries.
 | 
			
		||||
 */
 | 
			
		||||
static int load_nameserver_list(const char *path)
 | 
			
		||||
{
 | 
			
		||||
    FILE *fp = fopen(path, "r");
 | 
			
		||||
 | 
			
		||||
    if (fp == NULL)
 | 
			
		||||
        return -2;
 | 
			
		||||
 | 
			
		||||
    char line[MAX_SERVER_LINE];
 | 
			
		||||
 | 
			
		||||
    while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) {
 | 
			
		||||
        int linelen = strlen(line);
 | 
			
		||||
 | 
			
		||||
        if (linelen < SERVER_KEY_SIZE * 2 + 5)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if (line[linelen - 1] == '\n')
 | 
			
		||||
            line[--linelen] = '\0';
 | 
			
		||||
 | 
			
		||||
        const char *name = strtok(line, " ");
 | 
			
		||||
        const char *keystr = strtok(NULL, " ");
 | 
			
		||||
 | 
			
		||||
        if (name == NULL || keystr == NULL)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if (strlen(keystr) != SERVER_KEY_SIZE * 2)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name);
 | 
			
		||||
        int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr);
 | 
			
		||||
 | 
			
		||||
        if (res == -1)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        ++Nameservers.lines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
 | 
			
		||||
    if (Nameservers.lines < 1)
 | 
			
		||||
        return -3;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Takes address addr in the form "username@domain", puts the username in namebuf,
 | 
			
		||||
 * and the domain in dombuf.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure
 | 
			
		||||
 */
 | 
			
		||||
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
 | 
			
		||||
{
 | 
			
		||||
    if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH)))
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    char tmpaddr[MAX_STR_SIZE];
 | 
			
		||||
    char *tmpname = NULL;
 | 
			
		||||
    char *tmpdom = NULL;
 | 
			
		||||
 | 
			
		||||
    snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr);
 | 
			
		||||
    tmpname = strtok(tmpaddr, "@");
 | 
			
		||||
    tmpdom = strtok(NULL, "");
 | 
			
		||||
 | 
			
		||||
    if (tmpname == NULL || tmpdom == NULL)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    str_to_lower(tmpdom);
 | 
			
		||||
    snprintf(namebuf, namebuf_sz, "%s", tmpname);
 | 
			
		||||
    snprintf(dombuf, dombuf_sz, "%s", tmpdom);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* matches input domain name with domains in list and obtains key.
 | 
			
		||||
 * Turns out_domain into the full domain we need to make a POST request.
 | 
			
		||||
 *
 | 
			
		||||
 * Return true on match.
 | 
			
		||||
 * Returns false on no match.
 | 
			
		||||
 */
 | 
			
		||||
static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_size, const char *inputdomain)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < Nameservers.lines; ++i) {
 | 
			
		||||
        if (strcmp(Nameservers.names[i], inputdomain) == 0) {
 | 
			
		||||
            memcpy(pubkey, Nameservers.keys[i], SERVER_KEY_SIZE);
 | 
			
		||||
            snprintf(out_domain, out_domain_size, "https://%s/%s", Nameservers.names[i], NAMESERVER_API_PATH);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
#define ID_PREFIX "\"tox_id\": \""
 | 
			
		||||
static int process_response(struct Recv_Curl_Data *recv_data)
 | 
			
		||||
{
 | 
			
		||||
    size_t prefix_size = strlen(ID_PREFIX);
 | 
			
		||||
 | 
			
		||||
    if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    const char *IDstart = strstr(recv_data->data, ID_PREFIX);
 | 
			
		||||
 | 
			
		||||
    if (IDstart == NULL)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    char ID_string[TOX_ADDRESS_SIZE * 2 + 1];
 | 
			
		||||
    memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2);
 | 
			
		||||
    ID_string[TOX_ADDRESS_SIZE * 2] = 0;
 | 
			
		||||
 | 
			
		||||
    if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *lookup_thread_func(void *data)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow *self = t_data.self;
 | 
			
		||||
 | 
			
		||||
    char input_domain[MAX_STR_SIZE];
 | 
			
		||||
    char name[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    if (parse_addr(t_data.addr, name, sizeof(name), input_domain, sizeof(input_domain)) == -1) {
 | 
			
		||||
        lookup_error(self, "Input must be a 76 character Tox ID or an address in the form: username@domain");
 | 
			
		||||
        kill_lookup_thread();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char nameserver_key[SERVER_KEY_SIZE];
 | 
			
		||||
    char real_domain[MAX_DOMAIN_SIZE];
 | 
			
		||||
 | 
			
		||||
    if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) {
 | 
			
		||||
        if (!strcasecmp(input_domain, "utox.org"))
 | 
			
		||||
            lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic.");
 | 
			
		||||
        else
 | 
			
		||||
            lookup_error(self, "Name server domain not found.");
 | 
			
		||||
 | 
			
		||||
        kill_lookup_thread();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CURL *c_handle = curl_easy_init();
 | 
			
		||||
 | 
			
		||||
    if (!c_handle) {
 | 
			
		||||
        lookup_error(self, "curl handler error");
 | 
			
		||||
        kill_lookup_thread();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct Recv_Curl_Data recv_data;
 | 
			
		||||
 | 
			
		||||
    memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
 | 
			
		||||
 | 
			
		||||
    char post_data[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name);
 | 
			
		||||
 | 
			
		||||
    struct curl_slist *headers = NULL;
 | 
			
		||||
 | 
			
		||||
    headers = curl_slist_append(headers, "Content-Type: application/json");
 | 
			
		||||
 | 
			
		||||
    headers = curl_slist_append(headers, "charsets: utf-8");
 | 
			
		||||
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
 | 
			
		||||
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_URL, real_domain);
 | 
			
		||||
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data);
 | 
			
		||||
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, &recv_data);
 | 
			
		||||
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
 | 
			
		||||
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data);
 | 
			
		||||
 | 
			
		||||
    int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
 | 
			
		||||
 | 
			
		||||
    if (proxy_ret != 0) {
 | 
			
		||||
        lookup_error(self, "Failed to set proxy (error %d)\n");
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        lookup_error(self, "TLS could not be enabled (libcurl error %d)", ret);
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        lookup_error(self, "TLSv1.2 could not be set (libcurl error %d)", ret);
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        lookup_error(self, "Failed to set TLS cipher list (libcurl error %d)", ret);
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_perform(c_handle);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        /* If system doesn't support any of the specified ciphers suites, fall back to default */
 | 
			
		||||
        if (ret == CURLE_SSL_CIPHER) {
 | 
			
		||||
            curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
 | 
			
		||||
            ret = curl_easy_perform(c_handle);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ret != CURLE_OK) {
 | 
			
		||||
            lookup_error(self, "HTTPS lookup error (libcurl error %d)", ret);
 | 
			
		||||
            goto on_exit;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (process_response(&recv_data) == -1) {
 | 
			
		||||
        lookup_error(self, "Bad response.");
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
on_exit:
 | 
			
		||||
    curl_slist_free_all(headers);
 | 
			
		||||
    curl_easy_cleanup(c_handle);
 | 
			
		||||
    kill_lookup_thread();
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message)
 | 
			
		||||
{
 | 
			
		||||
    if (t_data.disabled) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (t_data.busy) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
 | 
			
		||||
    snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
 | 
			
		||||
    snprintf(t_data.msg, sizeof(t_data.msg), "%s", message);
 | 
			
		||||
    t_data.self = self;
 | 
			
		||||
    t_data.m = m;
 | 
			
		||||
    t_data.busy = true;
 | 
			
		||||
 | 
			
		||||
    if (pthread_attr_init(&lookup_thread.attr) != 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
 | 
			
		||||
        memset(&t_data, 0, sizeof(struct thread_data));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set");
 | 
			
		||||
        pthread_attr_destroy(&lookup_thread.attr);
 | 
			
		||||
        memset(&t_data, 0, sizeof(struct thread_data));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init");
 | 
			
		||||
        pthread_attr_destroy(&lookup_thread.attr);
 | 
			
		||||
        memset(&t_data, 0, sizeof(struct thread_data));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initializes http based name lookups. Note: This function must be called only once before additional
 | 
			
		||||
 * threads are spawned.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 if curl failed to init.
 | 
			
		||||
 * Returns -2 if the nameserver list cannot be found.
 | 
			
		||||
 * Returns -3 if the nameserver list does not contain any valid entries.
 | 
			
		||||
 */
 | 
			
		||||
int name_lookup_init(int curl_init_status)
 | 
			
		||||
{
 | 
			
		||||
    if (curl_init_status != 0) {
 | 
			
		||||
        t_data.disabled = true;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *path = arg_opts.nameserver_path[0] ? arg_opts.nameserver_path : PACKAGE_DATADIR "/nameservers";
 | 
			
		||||
    int ret = load_nameserver_list(path);
 | 
			
		||||
 | 
			
		||||
    if (ret != 0) {
 | 
			
		||||
        t_data.disabled = true;
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -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.
 | 
			
		||||
 *
 | 
			
		||||
@@ -20,13 +20,17 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
 | 
			
		||||
   Return 0 on success, -1 on failure. */
 | 
			
		||||
#ifndef NAME_LOOKUP
 | 
			
		||||
#define NAME_LOOKUP
 | 
			
		||||
 | 
			
		||||
#ifndef DNS_H
 | 
			
		||||
#define DNS_H
 | 
			
		||||
/* Initializes http based name lookups. Note: This function must be called only once before additional
 | 
			
		||||
 * threads are spawned.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int name_lookup_init(int curl_init_status);
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);
 | 
			
		||||
 | 
			
		||||
#endif /* #define DNS_H */
 | 
			
		||||
#endif /* NAME_LOOKUP */
 | 
			
		||||
							
								
								
									
										167
									
								
								src/notify.c
									
									
									
									
									
								
							
							
						
						
									
										167
									
								
								src/notify.c
									
									
									
									
									
								
							@@ -31,31 +31,31 @@
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "xtra.h"
 | 
			
		||||
 | 
			
		||||
#if defined(AUDIO) || defined(SOUND_NOTIFY)
 | 
			
		||||
    #ifdef __APPLE__
 | 
			
		||||
        #include <OpenAL/al.h>
 | 
			
		||||
        #include <OpenAL/alc.h>
 | 
			
		||||
    #else
 | 
			
		||||
        #include <AL/al.h>
 | 
			
		||||
        #include <AL/alc.h>
 | 
			
		||||
        /* compatibility with older versions of OpenAL */
 | 
			
		||||
        #ifndef ALC_ALL_DEVICES_SPECIFIER
 | 
			
		||||
            #include <AL/alext.h>
 | 
			
		||||
        #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    #ifdef SOUND_NOTIFY
 | 
			
		||||
        #include <AL/alut.h> /* freealut packet */
 | 
			
		||||
    #endif
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
#include <OpenAL/al.h>
 | 
			
		||||
#include <OpenAL/alc.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <AL/al.h>
 | 
			
		||||
#include <AL/alc.h>
 | 
			
		||||
/* compatibility with older versions of OpenAL */
 | 
			
		||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
 | 
			
		||||
#include <AL/alext.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
#include <AL/alut.h> /* freealut packet */
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
    #include <libnotify/notify.h>
 | 
			
		||||
#include <libnotify/notify.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define MAX_BOX_MSG_LEN 127
 | 
			
		||||
@@ -75,7 +75,7 @@ struct Control {
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    uint32_t device_idx; /* index of output device */
 | 
			
		||||
    char* sounds[SOUNDS_SIZE];
 | 
			
		||||
    char *sounds[SOUNDS_SIZE];
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
} Control = {0};
 | 
			
		||||
 | 
			
		||||
@@ -88,7 +88,7 @@ struct _ActiveNotifications {
 | 
			
		||||
    bool active;
 | 
			
		||||
    int *id_indicator;
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
    NotifyNotification* box;
 | 
			
		||||
    NotifyNotification *box;
 | 
			
		||||
    char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
 | 
			
		||||
    char title[64];
 | 
			
		||||
    size_t size;
 | 
			
		||||
@@ -188,15 +188,17 @@ void graceful_clear()
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
 | 
			
		||||
            if (actives[i].active) {
 | 
			
		||||
            #ifdef BOX_NOTIFY
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
 | 
			
		||||
                if (actives[i].box) {
 | 
			
		||||
                    GError* ignore;
 | 
			
		||||
                    GError *ignore;
 | 
			
		||||
                    notify_notification_close(actives[i].box, &ignore);
 | 
			
		||||
                    actives[i].box = NULL;
 | 
			
		||||
                }
 | 
			
		||||
            #endif
 | 
			
		||||
 | 
			
		||||
                if(actives[i].id_indicator)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
                if (actives[i].id_indicator)
 | 
			
		||||
                    *actives[i].id_indicator = -1;    /* reset indicator value */
 | 
			
		||||
 | 
			
		||||
                if ( actives[i].looping ) {
 | 
			
		||||
@@ -221,11 +223,11 @@ void graceful_clear()
 | 
			
		||||
    control_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* do_playing(void* _p)
 | 
			
		||||
void *do_playing(void *_p)
 | 
			
		||||
{
 | 
			
		||||
    (void)_p;
 | 
			
		||||
 | 
			
		||||
    while(true) {
 | 
			
		||||
    while (true) {
 | 
			
		||||
        control_lock();
 | 
			
		||||
 | 
			
		||||
        if (!Control.poll_active) {
 | 
			
		||||
@@ -234,18 +236,20 @@ void* do_playing(void* _p)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool has_looping = false;
 | 
			
		||||
        bool test_active_notify = false;
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
 | 
			
		||||
 | 
			
		||||
            if (actives[i].looping) has_looping = true;
 | 
			
		||||
 | 
			
		||||
            if (actives[i].active && !actives[i].looping
 | 
			
		||||
                #ifdef BOX_NOTIFY
 | 
			
		||||
                    && !actives[i].box
 | 
			
		||||
                #endif
 | 
			
		||||
            ) {
 | 
			
		||||
                if(actives[i].id_indicator)
 | 
			
		||||
            test_active_notify = actives[i].active && !actives[i].looping;
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
            test_active_notify = test_active_notify && !actives[i].box;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
            if (test_active_notify) {
 | 
			
		||||
                if (actives[i].id_indicator)
 | 
			
		||||
                    *actives[i].id_indicator = -1;    /* reset indicator value */
 | 
			
		||||
 | 
			
		||||
                if (!is_playing(actives[i].source)) {
 | 
			
		||||
@@ -256,13 +260,14 @@ void* do_playing(void* _p)
 | 
			
		||||
                    memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        #ifdef BOX_NOTIFY
 | 
			
		||||
            else if (actives[i].box && time(NULL) >= actives[i].n_timeout)
 | 
			
		||||
            {
 | 
			
		||||
                GError* ignore;
 | 
			
		||||
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
            else if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
 | 
			
		||||
                GError *ignore;
 | 
			
		||||
                notify_notification_close(actives[i].box, &ignore);
 | 
			
		||||
                actives[i].box = NULL;
 | 
			
		||||
                if(actives[i].id_indicator)
 | 
			
		||||
 | 
			
		||||
                if (actives[i].id_indicator)
 | 
			
		||||
                    *actives[i].id_indicator = -1;    /* reset indicator value */
 | 
			
		||||
 | 
			
		||||
                if (!actives[i].looping && !is_playing(actives[i].source)) {
 | 
			
		||||
@@ -273,7 +278,8 @@ void* do_playing(void* _p)
 | 
			
		||||
                    memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        #endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
 | 
			
		||||
@@ -281,18 +287,22 @@ void* do_playing(void* _p)
 | 
			
		||||
                (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
 | 
			
		||||
            m_close_device();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        has_looping = false;
 | 
			
		||||
 | 
			
		||||
        control_unlock();
 | 
			
		||||
        usleep(10000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int play_source(uint32_t source, uint32_t buffer, bool looping)
 | 
			
		||||
{
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
 | 
			
		||||
 | 
			
		||||
    if ( i == ACTIVE_NOTIFS_MAX ) {
 | 
			
		||||
        return -1; /* Full */
 | 
			
		||||
    }
 | 
			
		||||
@@ -308,11 +318,11 @@ int play_source(uint32_t source, uint32_t buffer, bool looping)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#elif BOX_NOTIFY
 | 
			
		||||
void* do_playing(void* _p)
 | 
			
		||||
void *do_playing(void *_p)
 | 
			
		||||
{
 | 
			
		||||
    (void)_p;
 | 
			
		||||
 | 
			
		||||
    while(true) {
 | 
			
		||||
    while (true) {
 | 
			
		||||
        control_lock();
 | 
			
		||||
 | 
			
		||||
        if (!Control.poll_active) {
 | 
			
		||||
@@ -323,20 +333,22 @@ void* do_playing(void* _p)
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
 | 
			
		||||
            if (actives[i].box && time(NULL) >= actives[i].n_timeout)
 | 
			
		||||
            {
 | 
			
		||||
                GError* ignore;
 | 
			
		||||
            if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
 | 
			
		||||
                GError *ignore;
 | 
			
		||||
                notify_notification_close(actives[i].box, &ignore);
 | 
			
		||||
                actives[i].box = NULL;
 | 
			
		||||
                if(actives[i].id_indicator)
 | 
			
		||||
 | 
			
		||||
                if (actives[i].id_indicator)
 | 
			
		||||
                    *actives[i].id_indicator = -1;    /* reset indicator value */
 | 
			
		||||
 | 
			
		||||
                memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        control_unlock();
 | 
			
		||||
        usleep(10000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -347,7 +359,7 @@ void graceful_clear()
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
 | 
			
		||||
        if (actives[i].box) {
 | 
			
		||||
            GError* ignore;
 | 
			
		||||
            GError *ignore;
 | 
			
		||||
            notify_notification_close(actives[i].box, &ignore);
 | 
			
		||||
            actives[i].box = NULL;
 | 
			
		||||
        }
 | 
			
		||||
@@ -378,6 +390,7 @@ int init_notify(int login_cooldown, int notification_timeout)
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
 | 
			
		||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
 | 
			
		||||
 | 
			
		||||
    if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
@@ -419,7 +432,9 @@ void terminate_notify()
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
 | 
			
		||||
 | 
			
		||||
    alutExit();
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
 | 
			
		||||
@@ -429,7 +444,7 @@ void terminate_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;
 | 
			
		||||
 | 
			
		||||
@@ -457,10 +472,11 @@ int play_sound_internal(Notification what, bool loop)
 | 
			
		||||
    alSourcei(source, AL_LOOPING, loop);
 | 
			
		||||
 | 
			
		||||
    int rc = play_source(source, buffer, loop);
 | 
			
		||||
 | 
			
		||||
    if (rc < 0) {
 | 
			
		||||
        alSourceStop(source);
 | 
			
		||||
        alDeleteSources(1, &source);
 | 
			
		||||
        alDeleteBuffers(1,&buffer);
 | 
			
		||||
        alDeleteBuffers(1, &buffer);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -472,7 +488,8 @@ int play_notify_sound(Notification notif, uint64_t flags)
 | 
			
		||||
    int rc = -1;
 | 
			
		||||
 | 
			
		||||
    if (flags & NT_BEEP) beep();
 | 
			
		||||
    else if (notif != silent) {
 | 
			
		||||
 | 
			
		||||
    if (notif != silent) {
 | 
			
		||||
        if ( !Control.poll_active || !Control.sounds[notif] )
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
@@ -487,17 +504,21 @@ void stop_sound(int id)
 | 
			
		||||
{
 | 
			
		||||
    if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) {
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
 | 
			
		||||
        if (actives[id].box) {
 | 
			
		||||
            GError* ignore;
 | 
			
		||||
            GError *ignore;
 | 
			
		||||
            notify_notification_close(actives[id].box, &ignore);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        if (actives[id].id_indicator)
 | 
			
		||||
            *actives[id].id_indicator = -1;
 | 
			
		||||
 | 
			
		||||
//         alSourcei(actives[id].source, AL_LOOPING, false);
 | 
			
		||||
        alSourceStop(actives[id].source);
 | 
			
		||||
        alDeleteSources(1, &actives[id].source);
 | 
			
		||||
        alDeleteBuffers(1,&actives[id].buffer);
 | 
			
		||||
        alDeleteBuffers(1, &actives[id].buffer);
 | 
			
		||||
        memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -508,6 +529,7 @@ static int m_play_sound(Notification notif, uint64_t flags)
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    return play_notify_sound(notif, flags);
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
    if (notif != silent)
 | 
			
		||||
        beep();
 | 
			
		||||
 | 
			
		||||
@@ -516,12 +538,12 @@ static int m_play_sound(Notification notif, uint64_t flags)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
void m_notify_action(NotifyNotification *box, char *action, void* data)
 | 
			
		||||
void m_notify_action(NotifyNotification *box, char *action, void *data)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
 | 
			
		||||
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator)
 | 
			
		||||
{
 | 
			
		||||
    tab_notify(self, flags);
 | 
			
		||||
 | 
			
		||||
@@ -540,6 +562,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
 | 
			
		||||
 | 
			
		||||
    if (id == -1) {
 | 
			
		||||
        for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
 | 
			
		||||
 | 
			
		||||
        if ( id == ACTIVE_NOTIFS_MAX ) {
 | 
			
		||||
            control_unlock();
 | 
			
		||||
            return -1; /* Full */
 | 
			
		||||
@@ -558,7 +581,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
@@ -566,6 +589,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
@@ -578,8 +602,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
 | 
			
		||||
 | 
			
		||||
    alSourceStop(actives[id].source);
 | 
			
		||||
    alDeleteSources(1, &actives[id].source);
 | 
			
		||||
    alDeleteBuffers(1,&actives[id].buffer);
 | 
			
		||||
 | 
			
		||||
    alDeleteBuffers(1, &actives[id].buffer);
 | 
			
		||||
 | 
			
		||||
    alGenSources(1, &actives[id].source);
 | 
			
		||||
    alGenBuffers(1, &actives[id].buffer);
 | 
			
		||||
@@ -593,6 +616,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
 | 
			
		||||
 | 
			
		||||
    return id;
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
    if (notif != silent)
 | 
			
		||||
        beep();
 | 
			
		||||
 | 
			
		||||
@@ -600,7 +624,8 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
 | 
			
		||||
#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)) {
 | 
			
		||||
        tab_notify(self, flags);
 | 
			
		||||
@@ -614,9 +639,11 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
 | 
			
		||||
    if (id == -1) { /* Could not play */
 | 
			
		||||
 | 
			
		||||
        for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
 | 
			
		||||
 | 
			
		||||
        if ( id == ACTIVE_NOTIFS_MAX ) {
 | 
			
		||||
            control_unlock();
 | 
			
		||||
            return -1; /* Full */
 | 
			
		||||
@@ -624,17 +651,23 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
 | 
			
		||||
 | 
			
		||||
        actives[id].active = 1;
 | 
			
		||||
        actives[id].id_indicator = id_indicator;
 | 
			
		||||
 | 
			
		||||
        if (id_indicator) *id_indicator = id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
    if (id == -1)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
#endif    /* SOUND_NOTIFY */
 | 
			
		||||
 | 
			
		||||
    snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
 | 
			
		||||
 | 
			
		||||
    if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
 | 
			
		||||
 | 
			
		||||
    va_list __ARGS__; va_start (__ARGS__, format);
 | 
			
		||||
    va_list __ARGS__;
 | 
			
		||||
    va_start (__ARGS__, format);
 | 
			
		||||
    vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end (__ARGS__);
 | 
			
		||||
 | 
			
		||||
@@ -657,7 +690,7 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
 | 
			
		||||
#endif   /* BOX_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
 | 
			
		||||
int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...)
 | 
			
		||||
{
 | 
			
		||||
    if (notifications_are_disabled(flags)) {
 | 
			
		||||
        tab_notify(self, flags);
 | 
			
		||||
@@ -676,7 +709,8 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    va_list __ARGS__; va_start (__ARGS__, format);
 | 
			
		||||
    va_list __ARGS__;
 | 
			
		||||
    va_start (__ARGS__, format);
 | 
			
		||||
    vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end (__ARGS__);
 | 
			
		||||
 | 
			
		||||
@@ -689,7 +723,8 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
 | 
			
		||||
    char formated[128 * 129] = {'\0'};
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (; i <actives[id].size; i ++) {
 | 
			
		||||
 | 
			
		||||
    for (; i < actives[id].size; i ++) {
 | 
			
		||||
        strcat(formated, actives[id].messages[i]);
 | 
			
		||||
        strcat(formated, "\n");
 | 
			
		||||
    }
 | 
			
		||||
@@ -707,7 +742,7 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
 | 
			
		||||
int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...)
 | 
			
		||||
{
 | 
			
		||||
    tab_notify(self, flags);
 | 
			
		||||
 | 
			
		||||
@@ -719,7 +754,9 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
    int id;
 | 
			
		||||
 | 
			
		||||
    for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
 | 
			
		||||
 | 
			
		||||
    if ( id == ACTIVE_NOTIFS_MAX ) {
 | 
			
		||||
        control_unlock();
 | 
			
		||||
        return -1; /* Full */
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
    if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
 | 
			
		||||
 | 
			
		||||
    va_list __ARGS__; va_start (__ARGS__, format);
 | 
			
		||||
    va_list __ARGS__;
 | 
			
		||||
    va_start (__ARGS__, format);
 | 
			
		||||
    vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end (__ARGS__);
 | 
			
		||||
 | 
			
		||||
@@ -757,7 +796,7 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
 | 
			
		||||
int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...)
 | 
			
		||||
{
 | 
			
		||||
    tab_notify(self, flags);
 | 
			
		||||
 | 
			
		||||
@@ -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__);
 | 
			
		||||
    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'};
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (; i <actives[id].size; i ++) {
 | 
			
		||||
 | 
			
		||||
    for (; i < actives[id].size; i ++) {
 | 
			
		||||
        strcat(formated, actives[id].messages[i]);
 | 
			
		||||
        strcat(formated, "\n");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								src/notify.h
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/notify.h
									
									
									
									
									
								
							@@ -26,8 +26,7 @@
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
typedef enum _Notification
 | 
			
		||||
{
 | 
			
		||||
typedef enum _Notification {
 | 
			
		||||
    silent = -1,
 | 
			
		||||
    notif_error,
 | 
			
		||||
    self_log_in,
 | 
			
		||||
@@ -63,18 +62,19 @@ typedef enum _Flags {
 | 
			
		||||
int init_notify(int login_cooldown, int notification_timeout);
 | 
			
		||||
void terminate_notify();
 | 
			
		||||
 | 
			
		||||
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator);
 | 
			
		||||
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id);
 | 
			
		||||
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator);
 | 
			
		||||
int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id);
 | 
			
		||||
 | 
			
		||||
void stop_sound(int id);
 | 
			
		||||
 | 
			
		||||
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...);
 | 
			
		||||
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...);
 | 
			
		||||
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...);
 | 
			
		||||
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...);
 | 
			
		||||
int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title,
 | 
			
		||||
               const char *format, ...);
 | 
			
		||||
int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...);
 | 
			
		||||
int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...);
 | 
			
		||||
int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...);
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
int set_sound(Notification sound, const char* value);
 | 
			
		||||
int set_sound(Notification sound, const char *value);
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
 | 
			
		||||
#endif /* NOTIFY_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								src/osx_video.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/osx_video.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
/*  osx_video.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef OSX_VIDEO_H
 | 
			
		||||
#define OSX_VIDEO_H
 | 
			
		||||
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __OBJC__
 | 
			
		||||
#import <Foundation/Foundation.h>
 | 
			
		||||
#import <AVFoundation/AVFoundation.h>
 | 
			
		||||
#endif /* __OBJC__ */
 | 
			
		||||
 | 
			
		||||
#define RELEASE_CHK(func, obj) if ((obj))\
 | 
			
		||||
    func((obj));
 | 
			
		||||
 | 
			
		||||
void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height);
 | 
			
		||||
 | 
			
		||||
#ifdef __OBJC__
 | 
			
		||||
@interface OSXVideo :
 | 
			
		||||
    NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
 | 
			
		||||
- (instancetype)initWithDeviceNames:
 | 
			
		||||
    (char **)device_names AmtDevices:
 | 
			
		||||
    (int *)size;
 | 
			
		||||
@end
 | 
			
		||||
#endif /* __OBJC__ */
 | 
			
		||||
 | 
			
		||||
int osx_video_init(char **device_names, int *size);
 | 
			
		||||
void osx_video_release();
 | 
			
		||||
/* Start device */
 | 
			
		||||
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height);
 | 
			
		||||
/* Stop device */
 | 
			
		||||
void osx_video_close_device(uint32_t device_idx);
 | 
			
		||||
/* Read data from device */
 | 
			
		||||
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* OSX_VIDEO_H */
 | 
			
		||||
							
								
								
									
										342
									
								
								src/osx_video.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								src/osx_video.m
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,342 @@
 | 
			
		||||
/*  osx_video.m
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef __OBJC__
 | 
			
		||||
#include "osx_video.h"
 | 
			
		||||
 | 
			
		||||
#import <Foundation/Foundation.h>
 | 
			
		||||
#import <AVFoundation/AVFoundation.h>
 | 
			
		||||
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Helper video format functions
 | 
			
		||||
 */
 | 
			
		||||
static uint8_t rgb_to_y(int r, int g, int b)
 | 
			
		||||
{
 | 
			
		||||
    int y = ((9798 * r + 19235 * g + 3736 * b) >> 15);
 | 
			
		||||
    return y > 255 ? 255 : y < 0 ? 0 : y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t rgb_to_u(int r, int g, int b)
 | 
			
		||||
{
 | 
			
		||||
    int u = ((-5538 * r + -10846 * g + 16351 * b) >> 15) + 128;
 | 
			
		||||
    return u > 255 ? 255 : u < 0 ? 0 : u;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t rgb_to_v(int r, int g, int b)
 | 
			
		||||
{
 | 
			
		||||
    int v = ((16351 * r + -13697 * g + -2664 * b) >> 15) + 128;
 | 
			
		||||
    return v > 255 ? 255 : v < 0 ? 0 : v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height)
 | 
			
		||||
{
 | 
			
		||||
    uint16_t x, y;
 | 
			
		||||
    uint8_t *p;
 | 
			
		||||
    uint8_t r, g, b;
 | 
			
		||||
 | 
			
		||||
    for (y = 0; y != height; y += 2) {
 | 
			
		||||
        p = rgb;
 | 
			
		||||
 | 
			
		||||
        for (x = 0; x != width; x++) {
 | 
			
		||||
            b = *rgb++;
 | 
			
		||||
            g = *rgb++;
 | 
			
		||||
            r = *rgb++;
 | 
			
		||||
            rgb++;
 | 
			
		||||
 | 
			
		||||
            *plane_y++ = rgb_to_y(r, g, b);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (x = 0; x != width / 2; x++) {
 | 
			
		||||
            b = *rgb++;
 | 
			
		||||
            g = *rgb++;
 | 
			
		||||
            r = *rgb++;
 | 
			
		||||
            rgb++;
 | 
			
		||||
 | 
			
		||||
            *plane_y++ = rgb_to_y(r, g, b);
 | 
			
		||||
 | 
			
		||||
            b = *rgb++;
 | 
			
		||||
            g = *rgb++;
 | 
			
		||||
            r = *rgb++;
 | 
			
		||||
            rgb++;
 | 
			
		||||
 | 
			
		||||
            *plane_y++ = rgb_to_y(r, g, b);
 | 
			
		||||
 | 
			
		||||
            b = ((int)b + (int) * (rgb - 8) + (int) * p + (int) * (p + 4) + 2) / 4;
 | 
			
		||||
            p++;
 | 
			
		||||
            g = ((int)g + (int) * (rgb - 7) + (int) * p + (int) * (p + 4) + 2) / 4;
 | 
			
		||||
            p++;
 | 
			
		||||
            r = ((int)r + (int) * (rgb - 6) + (int) * p + (int) * (p + 4) + 2) / 4;
 | 
			
		||||
            p++;
 | 
			
		||||
            p++;
 | 
			
		||||
 | 
			
		||||
            *plane_u++ = rgb_to_u(r, g, b);
 | 
			
		||||
            *plane_v++ = rgb_to_v(r, g, b);
 | 
			
		||||
 | 
			
		||||
            p += 4;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * End of helper video format functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Implementation for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
@implementation OSXVideo {
 | 
			
		||||
    dispatch_queue_t _processingQueue;
 | 
			
		||||
    AVCaptureSession *_session;
 | 
			
		||||
    AVCaptureVideoDataOutput *_linkerVideo;
 | 
			
		||||
 | 
			
		||||
    CVImageBufferRef _currentFrame;
 | 
			
		||||
    pthread_mutex_t _frameLock;
 | 
			
		||||
    BOOL _shouldMangleDimensions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (instancetype)initWithDeviceNames:
 | 
			
		||||
    (char **)device_names AmtDevices:
 | 
			
		||||
    (int *)size
 | 
			
		||||
{
 | 
			
		||||
    _session = [[AVCaptureSession alloc] init];
 | 
			
		||||
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < [devices count]; ++i) {
 | 
			
		||||
        AVCaptureDevice *device = [devices objectAtIndex: i];
 | 
			
		||||
        char *video_input_name;
 | 
			
		||||
        NSString *localizedName = [device localizedName];
 | 
			
		||||
        video_input_name = (char *)malloc(strlen([localizedName cStringUsingEncoding: NSUTF8StringEncoding]) + 1);
 | 
			
		||||
        strcpy(video_input_name, (char *)[localizedName cStringUsingEncoding: NSUTF8StringEncoding]);
 | 
			
		||||
        device_names[i] = video_input_name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( i <= 0 )
 | 
			
		||||
        return nil;
 | 
			
		||||
 | 
			
		||||
    *size = i;
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)dealloc
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_destroy(&_frameLock);
 | 
			
		||||
    [_session release];
 | 
			
		||||
    [_linkerVideo release];
 | 
			
		||||
    dispatch_release(_processingQueue);
 | 
			
		||||
    [super dealloc];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (int)openVideoDeviceIndex:
 | 
			
		||||
    (uint32_t)device_idx Width:
 | 
			
		||||
    (uint16_t *)width Height:
 | 
			
		||||
    (uint16_t *)height
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_init(&_frameLock, NULL);
 | 
			
		||||
    pthread_mutex_lock(&_frameLock);
 | 
			
		||||
    _processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL);
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
 | 
			
		||||
    AVCaptureDevice *device = [devices objectAtIndex: device_idx];
 | 
			
		||||
    NSError *error = NULL;
 | 
			
		||||
    AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
 | 
			
		||||
 | 
			
		||||
    if ( error != NULL ) {
 | 
			
		||||
        [input release];
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [_session beginConfiguration];
 | 
			
		||||
    [_session addInput: input];
 | 
			
		||||
    //_session.sessionPreset = AVCaptureSessionPreset640x480;
 | 
			
		||||
    //*width = 640;
 | 
			
		||||
    //*height = 480;
 | 
			
		||||
    _shouldMangleDimensions = YES;
 | 
			
		||||
    [_session commitConfiguration];
 | 
			
		||||
    [input release];
 | 
			
		||||
    [device release];
 | 
			
		||||
 | 
			
		||||
    /* Obtain device resolution */
 | 
			
		||||
    AVCaptureInputPort *port = [input.ports objectAtIndex: 0];
 | 
			
		||||
    CMFormatDescriptionRef format_description = port.formatDescription;
 | 
			
		||||
 | 
			
		||||
    if ( format_description ) {
 | 
			
		||||
        CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
 | 
			
		||||
        *width = dimensions.width;
 | 
			
		||||
        *height = dimensions.height;
 | 
			
		||||
    } else {
 | 
			
		||||
        *width = 0;
 | 
			
		||||
        *height = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
 | 
			
		||||
    [_linkerVideo setSampleBufferDelegate: self queue: _processingQueue];
 | 
			
		||||
 | 
			
		||||
    // TODO possibly get a better pixel format
 | 
			
		||||
    if (_shouldMangleDimensions) {
 | 
			
		||||
        [_linkerVideo setVideoSettings: @ {
 | 
			
		||||
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
 | 
			
		||||
(id)kCVPixelBufferWidthKey: @640,
 | 
			
		||||
(id)kCVPixelBufferHeightKey: @480
 | 
			
		||||
        }];
 | 
			
		||||
    } else {
 | 
			
		||||
        [_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [_session addOutput: _linkerVideo];
 | 
			
		||||
    [_session startRunning];
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(&_frameLock);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)closeVideoDeviceIndex:
 | 
			
		||||
    (uint32_t)device_idx
 | 
			
		||||
{
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
 | 
			
		||||
    AVCaptureDevice *device = [devices objectAtIndex: device_idx];
 | 
			
		||||
    NSError *error = NULL;
 | 
			
		||||
    AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
 | 
			
		||||
    [_session stopRunning];
 | 
			
		||||
    [_session removeOutput: _linkerVideo];
 | 
			
		||||
    [_session removeInput: input];
 | 
			
		||||
    [_linkerVideo release];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)captureOutput:
 | 
			
		||||
    (AVCaptureOutput *)captureOutput didOutputSampleBuffer:
 | 
			
		||||
    (CMSampleBufferRef)sampleBuffer fromConnection:
 | 
			
		||||
    (AVCaptureConnection *)connection
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_lock(&_frameLock);
 | 
			
		||||
    CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer);
 | 
			
		||||
 | 
			
		||||
    if (!img) {
 | 
			
		||||
        NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!");
 | 
			
		||||
    } else {
 | 
			
		||||
        CVPixelBufferUnlockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
 | 
			
		||||
        RELEASE_CHK(CFRelease, _currentFrame);
 | 
			
		||||
 | 
			
		||||
        _currentFrame = (CVImageBufferRef)CFRetain(img);
 | 
			
		||||
        // we're not going to do anything to it, so it's safe to lock it always
 | 
			
		||||
        CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(&_frameLock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (int)getVideoFrameY:
 | 
			
		||||
    (uint8_t *)y U:
 | 
			
		||||
    (uint8_t *)u V:
 | 
			
		||||
    (uint8_t *)v Width:
 | 
			
		||||
    (uint16_t *)width Height:
 | 
			
		||||
    (uint16_t *)height
 | 
			
		||||
{
 | 
			
		||||
    if (!_currentFrame) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&_frameLock);
 | 
			
		||||
    CFRetain(_currentFrame);
 | 
			
		||||
 | 
			
		||||
    CFTypeID imageType = CFGetTypeID(_currentFrame);
 | 
			
		||||
 | 
			
		||||
    if (imageType == CVPixelBufferGetTypeID()) {
 | 
			
		||||
        // TODO maybe handle other formats
 | 
			
		||||
        bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height);
 | 
			
		||||
    } else if (imageType == CVOpenGLBufferGetTypeID()) {
 | 
			
		||||
        // OpenGL pbuffer
 | 
			
		||||
    } else if (imageType == CVOpenGLTextureGetTypeID()) {
 | 
			
		||||
        // OpenGL Texture (Do we need to handle these?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CVPixelBufferRelease(_currentFrame);
 | 
			
		||||
    pthread_mutex_unlock(&_frameLock);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
/*
 | 
			
		||||
 * End of implementation for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * C-interface for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
static OSXVideo *_OSXVideo = nil;
 | 
			
		||||
 | 
			
		||||
int osx_video_init(char **device_names, int *size)
 | 
			
		||||
{
 | 
			
		||||
    _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size];
 | 
			
		||||
 | 
			
		||||
    if ( _OSXVideo == nil )
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osx_video_release()
 | 
			
		||||
{
 | 
			
		||||
    [_OSXVideo release];
 | 
			
		||||
    _OSXVideo = nil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height)
 | 
			
		||||
{
 | 
			
		||||
    if ( _OSXVideo == nil )
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osx_video_close_device(uint32_t device_idx)
 | 
			
		||||
{
 | 
			
		||||
    [_OSXVideo closeVideoDeviceIndex: device_idx];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height)
 | 
			
		||||
{
 | 
			
		||||
    if ( _OSXVideo == nil )
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * End of C-interface for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#endif /* __OBJC__ */
 | 
			
		||||
							
								
								
									
										65
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								src/prompt.c
									
									
									
									
									
								
							@@ -49,12 +49,13 @@ extern struct Winthread Winthread;
 | 
			
		||||
 | 
			
		||||
extern FriendsList Friends;
 | 
			
		||||
FriendRequests FrndRequests;
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 18
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 23
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 21
 | 
			
		||||
#else
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 16
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 19
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Array of global command names used for tab completion. */
 | 
			
		||||
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"       },
 | 
			
		||||
    { "/group"      },
 | 
			
		||||
    { "/help"       },
 | 
			
		||||
    { "/join"       },
 | 
			
		||||
    { "/log"        },
 | 
			
		||||
    { "/myid"       },
 | 
			
		||||
    { "/myqr"       },
 | 
			
		||||
    { "/nick"       },
 | 
			
		||||
    { "/note"       },
 | 
			
		||||
    { "/nospam"     },
 | 
			
		||||
    { "/quit"       },
 | 
			
		||||
    { "/requests"   },
 | 
			
		||||
    { "/status"     },
 | 
			
		||||
@@ -81,6 +85,14 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
 | 
			
		||||
    { "/sdev"        },
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
 | 
			
		||||
    { "/lsvdev"      },
 | 
			
		||||
    { "/svdev"       },
 | 
			
		||||
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void kill_prompt_window(ToxWindow *self)
 | 
			
		||||
@@ -175,16 +187,19 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
    getyx(self->window, y, x);
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (x2 <= 0)
 | 
			
		||||
    if (x2 <= 0 || y2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (ctx->pastemode && key == '\r')
 | 
			
		||||
        key = '\n';
 | 
			
		||||
 | 
			
		||||
    /* ignore non-menu related input if active */
 | 
			
		||||
    if (self->help->active) {
 | 
			
		||||
        help_onKey(self, key);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ltr) {    /* char is printable */
 | 
			
		||||
    if (ltr || key == '\n') {    /* char is printable */
 | 
			
		||||
        input_new_char(self, key, x, y, x2, y2);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -200,7 +215,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
 | 
			
		||||
            if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0)
 | 
			
		||||
                diff = dir_match(self, m, ctx->line, L"/avatar");
 | 
			
		||||
            else 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] = {
 | 
			
		||||
                    {"online"},
 | 
			
		||||
                    {"away"},
 | 
			
		||||
@@ -221,19 +236,21 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
        } else {
 | 
			
		||||
            sound_notify(self, notif_error, 0, NULL);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (key == '\n') {
 | 
			
		||||
    } else if (key == '\r') {
 | 
			
		||||
        rm_trailing_spaces_buf(ctx);
 | 
			
		||||
 | 
			
		||||
        if (!wstring_is_empty(ctx->line)) {
 | 
			
		||||
            add_line_to_hist(ctx);
 | 
			
		||||
            wstrsubst(ctx->line, L'¶', L'\n');
 | 
			
		||||
 | 
			
		||||
            char line[MAX_STR_SIZE] = {0};
 | 
			
		||||
 | 
			
		||||
            if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
 | 
			
		||||
                memset(&line, 0, sizeof(line));
 | 
			
		||||
 | 
			
		||||
        if (!string_is_empty(line))
 | 
			
		||||
            add_line_to_hist(ctx);
 | 
			
		||||
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
 | 
			
		||||
            execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wclear(ctx->linewin);
 | 
			
		||||
        wmove(self->window, y2 - CURS_Y_OFFSET, 0);
 | 
			
		||||
@@ -246,6 +263,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
@@ -281,10 +301,12 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
                status_text = "Online";
 | 
			
		||||
                colour = GREEN;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case TOX_USER_STATUS_AWAY:
 | 
			
		||||
                status_text = "Away";
 | 
			
		||||
                colour = YELLOW;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case TOX_USER_STATUS_BUSY:
 | 
			
		||||
                status_text = "Busy";
 | 
			
		||||
                colour = RED;
 | 
			
		||||
@@ -315,7 +337,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
        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';
 | 
			
		||||
        snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", 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));
 | 
			
		||||
    wmove(self->window, y + 1, new_x);
 | 
			
		||||
 | 
			
		||||
    wrefresh(self->window);
 | 
			
		||||
    wnoutrefresh(self->window);
 | 
			
		||||
 | 
			
		||||
    if (self->help->active)
 | 
			
		||||
        help_onDraw(self);
 | 
			
		||||
@@ -369,6 +391,10 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
    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) {
 | 
			
		||||
        msg = "has come online";
 | 
			
		||||
        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
 | 
			
		||||
            box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
 | 
			
		||||
                       "Toxic", "%s has come online", nick );
 | 
			
		||||
    }
 | 
			
		||||
    else if (connection_status == TOX_CONNECTION_NONE) {
 | 
			
		||||
    } else if (connection_status == TOX_CONNECTION_NONE) {
 | 
			
		||||
        msg = "has gone offline";
 | 
			
		||||
        line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
 | 
			
		||||
        write_to_log(msg, nick, ctx->log, true);
 | 
			
		||||
@@ -421,6 +446,10 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES);
 | 
			
		||||
 | 
			
		||||
    (void) y2;
 | 
			
		||||
 | 
			
		||||
    /* Init statusbar info */
 | 
			
		||||
@@ -478,6 +507,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
 | 
			
		||||
    int y2, x2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES);
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
    ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
 | 
			
		||||
    ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
 | 
			
		||||
@@ -510,6 +542,7 @@ ToxWindow new_prompt(void)
 | 
			
		||||
    ToxWindow ret;
 | 
			
		||||
    memset(&ret, 0, sizeof(ret));
 | 
			
		||||
 | 
			
		||||
    ret.num = -1;
 | 
			
		||||
    ret.active = 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"
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    #include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
 | 
			
		||||
#ifndef PACKAGE_DATADIR
 | 
			
		||||
    #define PACKAGE_DATADIR "."
 | 
			
		||||
#define PACKAGE_DATADIR "."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define NO_SOUND "silent"
 | 
			
		||||
 | 
			
		||||
static struct ui_strings {
 | 
			
		||||
    const char* self;
 | 
			
		||||
    const char* timestamps;
 | 
			
		||||
    const char* time_format;
 | 
			
		||||
    const char* timestamp_format;
 | 
			
		||||
    const char* log_timestamp_format;
 | 
			
		||||
    const char* alerts;
 | 
			
		||||
    const char* native_colors;
 | 
			
		||||
    const char* autolog;
 | 
			
		||||
    const char* history_size;
 | 
			
		||||
    const char* show_typing_self;
 | 
			
		||||
    const char* show_typing_other;
 | 
			
		||||
    const char* show_welcome_msg;
 | 
			
		||||
    const char *self;
 | 
			
		||||
    const char *timestamps;
 | 
			
		||||
    const char *time_format;
 | 
			
		||||
    const char *timestamp_format;
 | 
			
		||||
    const char *log_timestamp_format;
 | 
			
		||||
    const char *alerts;
 | 
			
		||||
    const char *bell_on_message;
 | 
			
		||||
    const char *bell_on_filetrans;
 | 
			
		||||
    const char *bell_on_filetrans_accept;
 | 
			
		||||
    const char *bell_on_invite;
 | 
			
		||||
    const char *native_colors;
 | 
			
		||||
    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_quit;
 | 
			
		||||
    const char* line_alert;
 | 
			
		||||
    const char* line_normal;
 | 
			
		||||
    const char *line_join;
 | 
			
		||||
    const char *line_quit;
 | 
			
		||||
    const char *line_alert;
 | 
			
		||||
    const char *line_normal;
 | 
			
		||||
    const char *line_special;
 | 
			
		||||
 | 
			
		||||
    const char* mplex_away;
 | 
			
		||||
    const char* mplex_away_note;
 | 
			
		||||
    const char *mplex_away;
 | 
			
		||||
    const char *mplex_away_note;
 | 
			
		||||
} ui_strings = {
 | 
			
		||||
    "ui",
 | 
			
		||||
    "timestamps",
 | 
			
		||||
@@ -72,21 +79,28 @@ static struct ui_strings {
 | 
			
		||||
    "timestamp_format",
 | 
			
		||||
    "log_timestamp_format",
 | 
			
		||||
    "alerts",
 | 
			
		||||
    "bell_on_message",
 | 
			
		||||
    "bell_on_filetrans",
 | 
			
		||||
    "bell_on_filetrans_accept",
 | 
			
		||||
    "bell_on_invite",
 | 
			
		||||
    "native_colors",
 | 
			
		||||
    "autolog",
 | 
			
		||||
    "history_size",
 | 
			
		||||
    "show_typing_self",
 | 
			
		||||
    "show_typing_other",
 | 
			
		||||
    "show_welcome_msg",
 | 
			
		||||
    "show_connection_msg",
 | 
			
		||||
    "nodeslist_update_freq",
 | 
			
		||||
    "line_join",
 | 
			
		||||
    "line_quit",
 | 
			
		||||
    "line_alert",
 | 
			
		||||
    "line_normal",
 | 
			
		||||
    "line_special",
 | 
			
		||||
    "mplex_away",
 | 
			
		||||
    "mplex_away_note",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ui_defaults(struct user_settings* settings)
 | 
			
		||||
static void ui_defaults(struct user_settings *settings)
 | 
			
		||||
{
 | 
			
		||||
    settings->timestamps = TIMESTAMPS_ON;
 | 
			
		||||
    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->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->history_size = 700;
 | 
			
		||||
    settings->show_typing_self = SHOW_TYPING_ON;
 | 
			
		||||
    settings->show_typing_other = SHOW_TYPING_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_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
 | 
			
		||||
    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_special, LINE_HINT_MAX + 1, "%s", LINE_SPECIAL);
 | 
			
		||||
 | 
			
		||||
    settings->mplex_away = MPLEX_ON;
 | 
			
		||||
    snprintf (settings->mplex_away_note,
 | 
			
		||||
@@ -113,17 +134,18 @@ static void ui_defaults(struct user_settings* settings)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct keys_strings {
 | 
			
		||||
    const char* self;
 | 
			
		||||
    const char* next_tab;
 | 
			
		||||
    const char* prev_tab;
 | 
			
		||||
    const char* scroll_line_up;
 | 
			
		||||
    const char* scroll_line_down;
 | 
			
		||||
    const char* half_page_up;
 | 
			
		||||
    const char* half_page_down;
 | 
			
		||||
    const char* page_bottom;
 | 
			
		||||
    const char* peer_list_up;
 | 
			
		||||
    const char* peer_list_down;
 | 
			
		||||
    const char* toggle_peerlist;
 | 
			
		||||
    const char *self;
 | 
			
		||||
    const char *next_tab;
 | 
			
		||||
    const char *prev_tab;
 | 
			
		||||
    const char *scroll_line_up;
 | 
			
		||||
    const char *scroll_line_down;
 | 
			
		||||
    const char *half_page_up;
 | 
			
		||||
    const char *half_page_down;
 | 
			
		||||
    const char *page_bottom;
 | 
			
		||||
    const char *peer_list_up;
 | 
			
		||||
    const char *peer_list_down;
 | 
			
		||||
    const char *toggle_peerlist;
 | 
			
		||||
    const char *toggle_pastemode;
 | 
			
		||||
} key_strings = {
 | 
			
		||||
    "keys",
 | 
			
		||||
    "next_tab",
 | 
			
		||||
@@ -136,10 +158,11 @@ static const struct keys_strings {
 | 
			
		||||
    "peer_list_up",
 | 
			
		||||
    "peer_list_down",
 | 
			
		||||
    "toggle_peerlist",
 | 
			
		||||
    "toggle_paste_mode",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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_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_down = T_KEY_C_RB;
 | 
			
		||||
    settings->key_toggle_peerlist = T_KEY_C_B;
 | 
			
		||||
    settings->key_toggle_pastemode = T_KEY_C_T;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct tox_strings {
 | 
			
		||||
    const char* self;
 | 
			
		||||
    const char* download_path;
 | 
			
		||||
    const char* chatlogs_path;
 | 
			
		||||
    const char* avatar_path;
 | 
			
		||||
    const char *self;
 | 
			
		||||
    const char *download_path;
 | 
			
		||||
    const char *chatlogs_path;
 | 
			
		||||
    const char *avatar_path;
 | 
			
		||||
    const char *password_eval;
 | 
			
		||||
} tox_strings = {
 | 
			
		||||
    "tox",
 | 
			
		||||
    "download_path",
 | 
			
		||||
    "chatlogs_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->chatlogs_path, "");
 | 
			
		||||
    strcpy(settings->avatar_path, "");
 | 
			
		||||
    strcpy(settings->password_eval, "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
static const struct audio_strings {
 | 
			
		||||
    const char* self;
 | 
			
		||||
    const char* input_device;
 | 
			
		||||
    const char* output_device;
 | 
			
		||||
    const char* VAD_treshold;
 | 
			
		||||
    const char *self;
 | 
			
		||||
    const char *input_device;
 | 
			
		||||
    const char *output_device;
 | 
			
		||||
    const char *VAD_treshold;
 | 
			
		||||
} audio_strings = {
 | 
			
		||||
    "audio",
 | 
			
		||||
    "input_device",
 | 
			
		||||
@@ -185,7 +212,7 @@ static const struct audio_strings {
 | 
			
		||||
    "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_out_dev = 0;
 | 
			
		||||
@@ -195,17 +222,17 @@ static void audio_defaults(struct user_settings* settings)
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
static const struct sound_strings {
 | 
			
		||||
    const char* self;
 | 
			
		||||
    const char* notif_error;
 | 
			
		||||
    const char* self_log_in;
 | 
			
		||||
    const char* self_log_out;
 | 
			
		||||
    const char* user_log_in;
 | 
			
		||||
    const char* user_log_out;
 | 
			
		||||
    const char* call_incoming;
 | 
			
		||||
    const char* call_outgoing;
 | 
			
		||||
    const char* generic_message;
 | 
			
		||||
    const char* transfer_pending;
 | 
			
		||||
    const char* transfer_completed;
 | 
			
		||||
    const char *self;
 | 
			
		||||
    const char *notif_error;
 | 
			
		||||
    const char *self_log_in;
 | 
			
		||||
    const char *self_log_out;
 | 
			
		||||
    const char *user_log_in;
 | 
			
		||||
    const char *user_log_out;
 | 
			
		||||
    const char *call_incoming;
 | 
			
		||||
    const char *call_outgoing;
 | 
			
		||||
    const char *generic_message;
 | 
			
		||||
    const char *transfer_pending;
 | 
			
		||||
    const char *transfer_completed;
 | 
			
		||||
} sound_strings = {
 | 
			
		||||
    "sounds",
 | 
			
		||||
    "notif_error",
 | 
			
		||||
@@ -221,11 +248,12 @@ static const struct sound_strings {
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int key_parse(const char** bind){
 | 
			
		||||
static int key_parse(const char **bind)
 | 
			
		||||
{
 | 
			
		||||
    int len = strlen(*bind);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -238,6 +266,15 @@ static int key_parse(const char** bind){
 | 
			
		||||
    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)
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
        int time = 24;
 | 
			
		||||
 | 
			
		||||
        if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) {
 | 
			
		||||
            if (time == 12) {
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
        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.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_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_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) ) {
 | 
			
		||||
            snprintf(s->line_join, sizeof(s->line_join), "%s", str);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( config_setting_lookup_string(setting, ui_strings.line_quit, &str) ) {
 | 
			
		||||
            snprintf(s->line_quit, sizeof(s->line_quit), "%s", str);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( config_setting_lookup_string(setting, ui_strings.line_alert, &str) ) {
 | 
			
		||||
            snprintf(s->line_alert, sizeof(s->line_alert), "%s", str);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( config_setting_lookup_string(setting, ui_strings.line_normal, &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);
 | 
			
		||||
 | 
			
		||||
        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))
 | 
			
		||||
                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 */
 | 
			
		||||
    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))
 | 
			
		||||
            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))
 | 
			
		||||
            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))
 | 
			
		||||
            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))
 | 
			
		||||
            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))
 | 
			
		||||
            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))
 | 
			
		||||
            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))
 | 
			
		||||
            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))
 | 
			
		||||
            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))
 | 
			
		||||
            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))
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
    if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) {
 | 
			
		||||
        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;
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
 | 
			
		||||
    if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
 | 
			
		||||
        if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
 | 
			
		||||
                !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)
 | 
			
		||||
                set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
    } else {
 | 
			
		||||
        set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
 | 
			
		||||
        set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.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_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    config_destroy(cfg);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,11 +30,19 @@
 | 
			
		||||
/* Represents line_* hints max strlen */
 | 
			
		||||
#define LINE_HINT_MAX 3
 | 
			
		||||
 | 
			
		||||
#define PASSWORD_EVAL_MAX 512
 | 
			
		||||
 | 
			
		||||
/* holds user setting values */
 | 
			
		||||
struct user_settings {
 | 
			
		||||
    int autolog;           /* 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 */
 | 
			
		||||
    char 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_other; /* 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_quit[LINE_HINT_MAX + 1];
 | 
			
		||||
    char line_alert[LINE_HINT_MAX + 1];
 | 
			
		||||
    char line_normal[LINE_HINT_MAX + 1];
 | 
			
		||||
    char line_special[LINE_HINT_MAX + 1];
 | 
			
		||||
 | 
			
		||||
    char download_path[PATH_MAX];
 | 
			
		||||
    char chatlogs_path[PATH_MAX];
 | 
			
		||||
    char avatar_path[PATH_MAX];
 | 
			
		||||
    char password_eval[PASSWORD_EVAL_MAX];
 | 
			
		||||
 | 
			
		||||
    int key_next_tab;
 | 
			
		||||
    int key_prev_tab;
 | 
			
		||||
@@ -64,6 +76,7 @@ struct user_settings {
 | 
			
		||||
    int key_peer_list_up;
 | 
			
		||||
    int key_peer_list_down;
 | 
			
		||||
    int key_toggle_peerlist;
 | 
			
		||||
    int key_toggle_pastemode;
 | 
			
		||||
 | 
			
		||||
    int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */
 | 
			
		||||
    char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH];
 | 
			
		||||
@@ -94,6 +107,9 @@ enum {
 | 
			
		||||
    SHOW_WELCOME_MSG_OFF = 0,
 | 
			
		||||
    SHOW_WELCOME_MSG_ON = 1,
 | 
			
		||||
 | 
			
		||||
    SHOW_CONNECTION_MSG_OFF = 0,
 | 
			
		||||
    SHOW_CONNECTION_MSG_ON = 1,
 | 
			
		||||
 | 
			
		||||
    DFLT_HST_SIZE = 700,
 | 
			
		||||
 | 
			
		||||
    MPLEX_OFF = 0,
 | 
			
		||||
@@ -104,6 +120,7 @@ enum {
 | 
			
		||||
#define LINE_QUIT    "<--"
 | 
			
		||||
#define LINE_ALERT   "-!-"
 | 
			
		||||
#define LINE_NORMAL  "---"
 | 
			
		||||
#define LINE_SPECIAL ">>>"
 | 
			
		||||
#define TIMESTAMP_DEFAULT      "%H:%M:%S"
 | 
			
		||||
#define LOG_TIMESTAMP_DEFAULT  "%Y/%m/%d [%H:%M:%S]"
 | 
			
		||||
#define MPLEX_AWAY_NOTE "Detached from screen"
 | 
			
		||||
 
 | 
			
		||||
@@ -52,8 +52,7 @@ extern struct Winthread Winthread;
 | 
			
		||||
#define PATH_SEP_S "/"
 | 
			
		||||
#define PATH_SEP_C '/'
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
typedef enum {
 | 
			
		||||
    MPLEX_NONE,
 | 
			
		||||
    MPLEX_SCREEN,
 | 
			
		||||
    MPLEX_TMUX,
 | 
			
		||||
@@ -97,13 +96,14 @@ static char *read_into_dyn_buffer (FILE *stream)
 | 
			
		||||
    char *dyn_buffer = NULL;
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
        if (dyn_buffer)
 | 
			
		||||
            dyn_buffer = (char*) realloc (dyn_buffer, length);
 | 
			
		||||
            dyn_buffer = (char *) realloc (dyn_buffer, length);
 | 
			
		||||
        else
 | 
			
		||||
            dyn_buffer = (char*) malloc (length);
 | 
			
		||||
            dyn_buffer = (char *) malloc (length);
 | 
			
		||||
 | 
			
		||||
        strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr);
 | 
			
		||||
        dyn_buffer_size = length;
 | 
			
		||||
    }
 | 
			
		||||
@@ -116,26 +116,29 @@ static char *extract_socket_path (const char *info)
 | 
			
		||||
    const char *search_str = " Socket";
 | 
			
		||||
    const char *pos = strstr (info, search_str);
 | 
			
		||||
    char *end = NULL;
 | 
			
		||||
    char* path = NULL;
 | 
			
		||||
    char *path = NULL;
 | 
			
		||||
 | 
			
		||||
    if (!pos)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    pos += strlen (search_str);
 | 
			
		||||
    pos = strchr (pos, PATH_SEP_C);
 | 
			
		||||
 | 
			
		||||
    if (!pos)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    end = strchr (pos, '\n');
 | 
			
		||||
 | 
			
		||||
    if (!end)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    *end = '\0';
 | 
			
		||||
    end = strrchr (pos, '.');
 | 
			
		||||
 | 
			
		||||
    if (!end)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    path = (char*) malloc (end - pos + 1);
 | 
			
		||||
    path = (char *) malloc (end - pos + 1);
 | 
			
		||||
    *end = '\0';
 | 
			
		||||
    return strcpy (path, pos);
 | 
			
		||||
}
 | 
			
		||||
@@ -147,14 +150,17 @@ static int detect_gnu_screen ()
 | 
			
		||||
    char *dyn_buffer = NULL;
 | 
			
		||||
 | 
			
		||||
    socket_name = getenv ("STY");
 | 
			
		||||
 | 
			
		||||
    if (!socket_name)
 | 
			
		||||
        goto nomplex;
 | 
			
		||||
 | 
			
		||||
    session_info_stream = popen ("env LC_ALL=C screen -ls", "r");
 | 
			
		||||
 | 
			
		||||
    if (!session_info_stream)
 | 
			
		||||
        goto nomplex;
 | 
			
		||||
 | 
			
		||||
    dyn_buffer = read_into_dyn_buffer (session_info_stream);
 | 
			
		||||
 | 
			
		||||
    if (!dyn_buffer)
 | 
			
		||||
        goto nomplex;
 | 
			
		||||
 | 
			
		||||
@@ -162,6 +168,7 @@ static int detect_gnu_screen ()
 | 
			
		||||
    session_info_stream = NULL;
 | 
			
		||||
 | 
			
		||||
    socket_path = extract_socket_path (dyn_buffer);
 | 
			
		||||
 | 
			
		||||
    if (!socket_path)
 | 
			
		||||
        goto nomplex;
 | 
			
		||||
 | 
			
		||||
@@ -181,23 +188,29 @@ static int detect_gnu_screen ()
 | 
			
		||||
    return 1;
 | 
			
		||||
 | 
			
		||||
nomplex:
 | 
			
		||||
 | 
			
		||||
    if (session_info_stream)
 | 
			
		||||
        pclose (session_info_stream);
 | 
			
		||||
 | 
			
		||||
    if (dyn_buffer)
 | 
			
		||||
        free (dyn_buffer);
 | 
			
		||||
 | 
			
		||||
    if (socket_path)
 | 
			
		||||
        free(socket_path);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int detect_tmux ()
 | 
			
		||||
{
 | 
			
		||||
    char *tmux_env = getenv ("TMUX"), *pos;
 | 
			
		||||
 | 
			
		||||
    if (!tmux_env)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    /* find second separator */
 | 
			
		||||
    pos = strrchr (tmux_env, ',');
 | 
			
		||||
 | 
			
		||||
    if (!pos)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
@@ -230,6 +243,7 @@ static int gnu_screen_is_detached ()
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    struct stat sb;
 | 
			
		||||
 | 
			
		||||
    if (stat (mplex_data, &sb) != 0)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
@@ -257,10 +271,12 @@ static int tmux_is_detached ()
 | 
			
		||||
    const int numstr_len = strlen (mplex_data);
 | 
			
		||||
 | 
			
		||||
    session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r");
 | 
			
		||||
 | 
			
		||||
    if (!session_info_stream)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    dyn_buffer = read_into_dyn_buffer (session_info_stream);
 | 
			
		||||
 | 
			
		||||
    if (!dyn_buffer)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
@@ -268,7 +284,7 @@ static int tmux_is_detached ()
 | 
			
		||||
    session_info_stream = NULL;
 | 
			
		||||
 | 
			
		||||
    /* 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';
 | 
			
		||||
    strcpy (search_str + 1, mplex_data);
 | 
			
		||||
    strcat (search_str, ": ");
 | 
			
		||||
@@ -295,12 +311,16 @@ static int tmux_is_detached ()
 | 
			
		||||
    return attached_pos == NULL  ||  attached_pos > nl_pos;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
 | 
			
		||||
    if (session_info_stream)
 | 
			
		||||
        pclose (session_info_stream);
 | 
			
		||||
 | 
			
		||||
    if (dyn_buffer)
 | 
			
		||||
        free (dyn_buffer);
 | 
			
		||||
 | 
			
		||||
    if (search_str)
 | 
			
		||||
        free (search_str);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -332,26 +352,21 @@ static void mplex_timer_handler (Tox *m)
 | 
			
		||||
    current_status = tox_self_get_status (m);
 | 
			
		||||
    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;
 | 
			
		||||
        new_status = prev_status;
 | 
			
		||||
        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;
 | 
			
		||||
        prev_status = current_status;
 | 
			
		||||
        new_status = TOX_USER_STATUS_AWAY;
 | 
			
		||||
        pthread_mutex_lock (&Winthread.lock);
 | 
			
		||||
        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';
 | 
			
		||||
        pthread_mutex_unlock (&Winthread.lock);
 | 
			
		||||
        new_note = user_settings->mplex_away_note;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    } else
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    char argv[3][MAX_STR_SIZE];
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										535
									
								
								src/toxic.c
									
									
									
									
									
								
							
							
						
						
									
										535
									
								
								src/toxic.c
									
									
									
									
									
								
							@@ -39,7 +39,9 @@
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
#include <tox/tox.h>
 | 
			
		||||
#include <tox/toxencryptsave.h>
 | 
			
		||||
 | 
			
		||||
@@ -47,6 +49,7 @@
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "groupchat.h"
 | 
			
		||||
#include "prompt.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
@@ -54,22 +57,27 @@
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
#include "message_queue.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "term_mplex.h"
 | 
			
		||||
#include "name_lookup.h"
 | 
			
		||||
#include "bootstrap.h"
 | 
			
		||||
 | 
			
		||||
#ifdef X11
 | 
			
		||||
    #include "xtra.h"
 | 
			
		||||
#include "xtra.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#include "audio_call.h"
 | 
			
		||||
ToxAv *av;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
#include "video_call.h"
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
ToxAV *av;
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifndef PACKAGE_DATADIR
 | 
			
		||||
    #define PACKAGE_DATADIR "."
 | 
			
		||||
#define PACKAGE_DATADIR "."
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Export for use in Callbacks */
 | 
			
		||||
@@ -77,16 +85,16 @@ char *DATA_FILE = NULL;
 | 
			
		||||
char *BLOCK_FILE = NULL;
 | 
			
		||||
ToxWindow *prompt = NULL;
 | 
			
		||||
 | 
			
		||||
#define DATANAME  "data"
 | 
			
		||||
#define BLOCKNAME "data-blocklist"
 | 
			
		||||
#define DATANAME  "toxic_profile.tox"
 | 
			
		||||
#define BLOCKNAME "toxic_blocklist"
 | 
			
		||||
 | 
			
		||||
#define AUTOSAVE_FREQ 60
 | 
			
		||||
#define AUTOSAVE_FREQ 600
 | 
			
		||||
#define MIN_PASSWORD_LEN 6
 | 
			
		||||
#define MAX_PASSWORD_LEN 64
 | 
			
		||||
 | 
			
		||||
struct Winthread Winthread;
 | 
			
		||||
struct cqueue_thread cqueue_thread;
 | 
			
		||||
struct audio_thread audio_thread;
 | 
			
		||||
struct av_thread av_thread;
 | 
			
		||||
struct arg_opts arg_opts;
 | 
			
		||||
struct user_settings *user_settings = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -149,12 +157,16 @@ void exit_toxic_success(Tox *m)
 | 
			
		||||
    terminate_notify();
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    terminate_video();
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    terminate_audio();
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
    free_global_data();
 | 
			
		||||
    tox_kill(m);
 | 
			
		||||
    endwin();
 | 
			
		||||
    curl_global_cleanup();
 | 
			
		||||
 | 
			
		||||
#ifdef X11
 | 
			
		||||
    /* We have to terminate xtra last coz reasons
 | 
			
		||||
@@ -191,6 +203,7 @@ static void init_term(void)
 | 
			
		||||
    cbreak();
 | 
			
		||||
    keypad(stdscr, 1);
 | 
			
		||||
    noecho();
 | 
			
		||||
    nonl();
 | 
			
		||||
    timeout(100);
 | 
			
		||||
 | 
			
		||||
    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]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MIN_NODE_LINE  50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */
 | 
			
		||||
#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)
 | 
			
		||||
static void load_groups(Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    if (!filename)
 | 
			
		||||
        return 1;
 | 
			
		||||
    size_t i;
 | 
			
		||||
    size_t numgroups = tox_group_get_number_groups(m);
 | 
			
		||||
 | 
			
		||||
    FILE *fp = fopen(filename, "r");
 | 
			
		||||
 | 
			
		||||
    if (fp == 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++;
 | 
			
		||||
    for (i = 0; i < numgroups; ++i) {
 | 
			
		||||
        if (init_groupchat_win(m, i, NULL, 0) == -1)
 | 
			
		||||
            tox_group_leave(m, i, NULL, 0, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
@@ -450,8 +332,10 @@ static int password_prompt(char *buf, int size)
 | 
			
		||||
    /* eat overflowed stdin and return error */
 | 
			
		||||
    if (buf[--len] != '\n') {
 | 
			
		||||
        int ch;
 | 
			
		||||
 | 
			
		||||
        while ((ch = getchar()) != '\n' && ch > 0)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -459,6 +343,49 @@ static int password_prompt(char *buf, int size)
 | 
			
		||||
    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 */
 | 
			
		||||
static void first_time_encrypt(const char *msg)
 | 
			
		||||
{
 | 
			
		||||
@@ -522,29 +449,46 @@ static void first_time_encrypt(const char *msg)
 | 
			
		||||
    system("clear");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Store Tox data to given location
 | 
			
		||||
/* Store Tox profile data to path.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 if stored successfully or ignoring data file.
 | 
			
		||||
 * Return -1 on error
 | 
			
		||||
 * Return 0 if stored successfully.
 | 
			
		||||
 * Return -1 on error.
 | 
			
		||||
 */
 | 
			
		||||
#define TEMP_PROFILE_EXT ".tmp"
 | 
			
		||||
int store_data(Tox *m, const char *path)
 | 
			
		||||
{
 | 
			
		||||
    if (path == NULL)
 | 
			
		||||
    if (path == NULL) {
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    if (user_password.data_is_encrypted && !arg_opts.unencrypt_data) {
 | 
			
		||||
        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_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) {
 | 
			
		||||
            fprintf(stderr, "tox_pass_encrypt() failed with error %d\n", err);
 | 
			
		||||
            fclose(fp);
 | 
			
		||||
            free(data);
 | 
			
		||||
            free(enc_data);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fwrite(enc_data, enc_len, 1, fp) != 1) {
 | 
			
		||||
            fprintf(stderr, "Failed to write profile data.\n");
 | 
			
		||||
            fclose(fp);
 | 
			
		||||
            free(data);
 | 
			
		||||
            free(enc_data);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        free(enc_data);
 | 
			
		||||
    } else {  /* data will not be encrypted */
 | 
			
		||||
        if (fwrite(data, data_len, 1, fp) != 1) {
 | 
			
		||||
            fprintf(stderr, "Failed to write profile data.\n");
 | 
			
		||||
            fclose(fp);
 | 
			
		||||
            free(data);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
    free(data);
 | 
			
		||||
 | 
			
		||||
    if (rename(temp_path, path) != 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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_message(m, on_statusmessagechange, 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_chunk_request(m, on_file_chunk_request, NULL);
 | 
			
		||||
    tox_callback_file_recv_control(m, on_file_control, 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)
 | 
			
		||||
@@ -642,14 +610,14 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
 | 
			
		||||
 | 
			
		||||
        if (len == 0) {
 | 
			
		||||
            fclose(fp);
 | 
			
		||||
            exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
 | 
			
		||||
            exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        char data[len];
 | 
			
		||||
 | 
			
		||||
        if (fread(data, sizeof(data), 1, fp) != 1) {
 | 
			
		||||
            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);
 | 
			
		||||
@@ -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 */
 | 
			
		||||
        if (arg_opts.encrypt_data && is_encrypted) {
 | 
			
		||||
            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)
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
            size_t pwlen = 0;
 | 
			
		||||
            int pweval = user_settings->password_eval[0];
 | 
			
		||||
 | 
			
		||||
            if (!pweval) {
 | 
			
		||||
                system("clear");   // TODO: is this portable?
 | 
			
		||||
                printf("Enter password (q to quit) ");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            size_t plain_len = len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
 | 
			
		||||
            char plain[plain_len];
 | 
			
		||||
 | 
			
		||||
            while (true) {
 | 
			
		||||
                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;
 | 
			
		||||
 | 
			
		||||
                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");
 | 
			
		||||
                    sleep(1);
 | 
			
		||||
                    printf("Invalid password. Try again. ");
 | 
			
		||||
                    pweval = 0;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -713,6 +691,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
 | 
			
		||||
                    system("clear");
 | 
			
		||||
                    sleep(1);
 | 
			
		||||
                    printf("Invalid password. Try again. ");
 | 
			
		||||
                    pweval = 0;
 | 
			
		||||
                } else {
 | 
			
		||||
                    fclose(fp);
 | 
			
		||||
                    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);
 | 
			
		||||
    } else {   /* Data file does not/should not exist */
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
@@ -744,7 +723,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
 | 
			
		||||
            return NULL;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
@@ -780,33 +759,9 @@ static Tox *load_toxic(char *data_path)
 | 
			
		||||
    return m;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define TRY_BOOTSTRAP_INTERVAL 5
 | 
			
		||||
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)
 | 
			
		||||
static void do_toxic(Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    update_unix_time();
 | 
			
		||||
 | 
			
		||||
    if (arg_opts.no_connect) {
 | 
			
		||||
        pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
@@ -814,8 +769,7 @@ static void do_toxic(Tox *m, ToxWindow *prompt)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tox_iterate(m);
 | 
			
		||||
    do_bootstrap(m);
 | 
			
		||||
    check_file_transfer_timeouts(m);
 | 
			
		||||
    do_tox_connection(m);
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -871,16 +825,16 @@ void *thread_cqueue(void *data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
void *thread_audio(void *data)
 | 
			
		||||
void *thread_av(void *data)
 | 
			
		||||
{
 | 
			
		||||
    ToxAv *av = (ToxAv *) data;
 | 
			
		||||
    ToxAV *av = (ToxAV *) data;
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
        pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
        toxav_do(av);
 | 
			
		||||
        toxav_iterate(av);
 | 
			
		||||
        pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
        usleep(toxav_do_interval(av) * 1000);
 | 
			
		||||
        usleep(toxav_iteration_interval(av) * 1000);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#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, "  -p, --SOCKS5-proxy       Use SOCKS5 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, --tcp-server         Act as a TCP relay server: Requires [port]\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'},
 | 
			
		||||
        {"help", no_argument, 0, 'h'},
 | 
			
		||||
        {"noconnect", no_argument, 0, 'o'},
 | 
			
		||||
        {"dnslist", required_argument, 0, 'r'},
 | 
			
		||||
        {"namelist", required_argument, 0, 'r'},
 | 
			
		||||
        {"force-tcp", no_argument, 0, 't'},
 | 
			
		||||
        {"tcp-server", required_argument, 0, 'T'},
 | 
			
		||||
        {"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:";
 | 
			
		||||
    int opt, indexptr;
 | 
			
		||||
    long int port = 0;
 | 
			
		||||
 | 
			
		||||
    while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
@@ -996,10 +951,6 @@ static void parse_args(int argc, char *argv[])
 | 
			
		||||
 | 
			
		||||
            case 'n':
 | 
			
		||||
                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;
 | 
			
		||||
 | 
			
		||||
            case 'o':
 | 
			
		||||
@@ -1011,27 +962,37 @@ static void parse_args(int argc, char *argv[])
 | 
			
		||||
                arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5;
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
                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;
 | 
			
		||||
 | 
			
		||||
            case 'P':
 | 
			
		||||
                arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP;
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
                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;
 | 
			
		||||
 | 
			
		||||
            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))
 | 
			
		||||
                    queue_init_message("DNSservers file not found");
 | 
			
		||||
                if (!file_exists(arg_opts.nameserver_path))
 | 
			
		||||
                    queue_init_message("nameserver list not found");
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
@@ -1040,7 +1001,12 @@ static void parse_args(int argc, char *argv[])
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    if (config_err) {
 | 
			
		||||
    if (config_err == -1) {
 | 
			
		||||
        DATA_FILE = strdup(DATANAME);
 | 
			
		||||
        BLOCK_FILE = strdup(BLOCKNAME);
 | 
			
		||||
 | 
			
		||||
        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 {
 | 
			
		||||
        DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
 | 
			
		||||
        BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
        strcat(DATA_FILE, CONFIGDIR);
 | 
			
		||||
@@ -1085,43 +1097,27 @@ static int init_default_data_files(void)
 | 
			
		||||
        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);
 | 
			
		||||
    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)
 | 
			
		||||
#ifdef X11
 | 
			
		||||
void DnD_callback(const char* asdv, DropType dt)
 | 
			
		||||
void DnD_callback(const char *asdv, DropType dt)
 | 
			
		||||
{
 | 
			
		||||
    // if (dt != DT_plain)
 | 
			
		||||
    //     return;
 | 
			
		||||
 | 
			
		||||
    // pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    // line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv);
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
    // pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
}
 | 
			
		||||
#endif /* X11 */
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
int main(int argc, char **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. */
 | 
			
		||||
    umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
 | 
			
		||||
 | 
			
		||||
    int config_err = init_default_data_files();
 | 
			
		||||
    bool datafile_exists = file_exists(DATA_FILE);
 | 
			
		||||
    init_default_data_files();
 | 
			
		||||
 | 
			
		||||
    if (datafile_exists)
 | 
			
		||||
        last_bootstrap_time = get_unix_time();
 | 
			
		||||
    bool datafile_exists = file_exists(DATA_FILE);
 | 
			
		||||
 | 
			
		||||
    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)");
 | 
			
		||||
@@ -1157,11 +1151,27 @@ int main(int argc, char *argv[])
 | 
			
		||||
        exit_toxic_err("failed in main", FATALERR_MEMORY);
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    if (init_xtra(DnD_callback) == -1)
 | 
			
		||||
        queue_init_message("X failed to initialize");
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
        exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
    av = init_audio(prompt, m);
 | 
			
		||||
 | 
			
		||||
    /* audio thread */
 | 
			
		||||
    if (pthread_create(&audio_thread.tid, NULL, thread_audio, (void *) av) != 0)
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    set_primary_device(input, user_settings->audio_in_dev);
 | 
			
		||||
    set_primary_device(output, user_settings->audio_out_dev);
 | 
			
		||||
 | 
			
		||||
#elif SOUND_NOTIFY
 | 
			
		||||
 | 
			
		||||
    if ( init_devices() == de_InternalError )
 | 
			
		||||
        queue_init_message("Failed to init audio devices");
 | 
			
		||||
 | 
			
		||||
@@ -1206,21 +1221,18 @@ int main(int argc, char *argv[])
 | 
			
		||||
 | 
			
		||||
    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 */
 | 
			
		||||
    if (init_mplex_away_timer(m) == -1)
 | 
			
		||||
        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);
 | 
			
		||||
    load_groups(m);
 | 
			
		||||
    print_init_messages(prompt);
 | 
			
		||||
    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);
 | 
			
		||||
    execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE);
 | 
			
		||||
 | 
			
		||||
    uint64_t last_save = (uint64_t) time(NULL);
 | 
			
		||||
    uint64_t looptimer = last_save;
 | 
			
		||||
    useconds_t msleepval = 40000;
 | 
			
		||||
    uint64_t loopcount = 0;
 | 
			
		||||
    time_t last_save = get_unix_time();
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
        do_toxic(m, prompt);
 | 
			
		||||
        uint64_t cur_time = get_unix_time();
 | 
			
		||||
        do_toxic(m);
 | 
			
		||||
 | 
			
		||||
        time_t cur_time = get_unix_time();
 | 
			
		||||
 | 
			
		||||
        if (timed_out(last_save, AUTOSAVE_FREQ)) {
 | 
			
		||||
            pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
            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");
 | 
			
		||||
 | 
			
		||||
            pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
            last_save = cur_time;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        msleepval = optimal_msleepval(&looptimer, &loopcount, cur_time, msleepval);
 | 
			
		||||
        usleep(msleepval);
 | 
			
		||||
        usleep(tox_iteration_interval(m) * 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 TIME_STR_SIZE 32
 | 
			
		||||
 | 
			
		||||
#ifndef MAX_PORT_RANGE
 | 
			
		||||
#define MAX_PORT_RANGE 65535
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* ASCII key codes */
 | 
			
		||||
#define T_KEY_ESC        0x1B     /* ESC key */
 | 
			
		||||
#define T_KEY_KILL       0x0B     /* ctrl-k */
 | 
			
		||||
@@ -66,6 +70,7 @@
 | 
			
		||||
#define T_KEY_C_L        0x0C     /* ctrl-l */
 | 
			
		||||
#define T_KEY_C_W        0x17     /* ctrl-w */
 | 
			
		||||
#define T_KEY_C_B        0x02     /* ctrl-b */
 | 
			
		||||
#define T_KEY_C_T        0x14     /* ctrl-t */
 | 
			
		||||
#define T_KEY_TAB        0x09     /* TAB key */
 | 
			
		||||
 | 
			
		||||
#define ONLINE_CHAR "*"
 | 
			
		||||
@@ -84,6 +89,7 @@ typedef enum _FATAL_ERRS {
 | 
			
		||||
    FATALERR_PROXY = -10,           /* Tox network failed to init using a proxy */
 | 
			
		||||
    FATALERR_ENCRYPT = -11,         /* Data file encryption failure */
 | 
			
		||||
    FATALERR_TOX_INIT = -12,        /* Tox instance failed to initialize */
 | 
			
		||||
    FATALERR_CURSES = -13,          /* Unrecoverable Ncurses error */
 | 
			
		||||
} FATAL_ERRS;
 | 
			
		||||
 | 
			
		||||
/* Fixes text color problem on some terminals.
 | 
			
		||||
@@ -101,18 +107,21 @@ int store_data(Tox *m, const char *path);
 | 
			
		||||
/* callbacks */
 | 
			
		||||
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_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_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_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_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void *userdata);
 | 
			
		||||
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, 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 *userdata);
 | 
			
		||||
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
 | 
			
		||||
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, 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_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_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 *userdata);
 | 
			
		||||
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length,
 | 
			
		||||
                          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,
 | 
			
		||||
                        size_t length, 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);
 | 
			
		||||
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_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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -136,13 +136,13 @@ int del_word_buf(ChatContext *ctx)
 | 
			
		||||
    int i = ctx->pos, count = 0;
 | 
			
		||||
 | 
			
		||||
    /* traverse past empty space */
 | 
			
		||||
    while (i > 0 && ctx->line[i-1] == L' ') {
 | 
			
		||||
    while (i > 0 && ctx->line[i - 1] == L' ') {
 | 
			
		||||
        ++count;
 | 
			
		||||
        --i;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* traverse past last entered word */
 | 
			
		||||
    while (i > 0 && ctx->line[i-1] != L' ') {
 | 
			
		||||
    while (i > 0 && ctx->line[i - 1] != L' ') {
 | 
			
		||||
        ++count;
 | 
			
		||||
        --i;
 | 
			
		||||
    }
 | 
			
		||||
@@ -166,19 +166,19 @@ void reset_buf(ChatContext *ctx)
 | 
			
		||||
    ctx->start = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Removes trailing spaces from line. */
 | 
			
		||||
/* Removes trailing spaces and newlines from line. */
 | 
			
		||||
void rm_trailing_spaces_buf(ChatContext *ctx)
 | 
			
		||||
{
 | 
			
		||||
    if (ctx->len <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (ctx->line[ctx->len - 1] != ' ')
 | 
			
		||||
    if (ctx->line[ctx->len - 1] != ' ' && ctx->line[ctx->len - 1] != L'¶')
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = ctx->len - 1; i >= 0; --i) {
 | 
			
		||||
        if (ctx->line[i] != ' ')
 | 
			
		||||
        if (ctx->line[i] != ' ' && ctx->line[i] != L'¶')
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -242,3 +242,21 @@ void fetch_hist_item(ChatContext *ctx, int key_dir)
 | 
			
		||||
    ctx->pos = 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 */
 | 
			
		||||
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 */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 */
 | 
			
		||||
							
								
								
									
										190
									
								
								src/windows.c
									
									
									
									
									
								
							
							
						
						
									
										190
									
								
								src/windows.c
									
									
									
									
									
								
							@@ -151,8 +151,18 @@ void on_friendadded(Tox *m, uint32_t friendnumber, bool sort)
 | 
			
		||||
    store_data(m, DATA_FILE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length,
 | 
			
		||||
                     void *userdata)
 | 
			
		||||
void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length, 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];
 | 
			
		||||
    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) {
 | 
			
		||||
        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,
 | 
			
		||||
                              size_t length, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
    char msg[MAX_STR_SIZE + 1];
 | 
			
		||||
    length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if (windows[i].onGroupPrivateMessage != NULL)
 | 
			
		||||
            windows[i].onGroupPrivateMessage(&windows[i], m, groupnumber, peer_id, msg, length);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_USER_STATUS status, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if (windows[i].onGroupStatusChange != NULL)
 | 
			
		||||
            windows[i].onGroupStatusChange(&windows[i], m, groupnumber, peer_id, status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peer_id, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if (windows[i].onGroupPeerJoin != NULL)
 | 
			
		||||
            windows[i].onGroupPeerJoin(&windows[i], m, groupnumber, peer_id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *partmsg, size_t length,
 | 
			
		||||
                        void *userdata)
 | 
			
		||||
{
 | 
			
		||||
    char msg[MAX_STR_SIZE + 1];
 | 
			
		||||
    length = copy_tox_str(msg, sizeof(msg), (const char *) action, length);
 | 
			
		||||
 | 
			
		||||
    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].onGroupAction != NULL)
 | 
			
		||||
            windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, msg, length);
 | 
			
		||||
        if (windows[i].onGroupPeerExit != NULL)
 | 
			
		||||
            windows[i].onGroupPeerExit(&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 *userdata)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if (windows[i].onGroupInvite != NULL)
 | 
			
		||||
            windows[i].onGroupInvite(&windows[i], m, friendnumber, type, (char *) group_pub_key, length);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if (windows[i].onGroupNamelistChange != NULL)
 | 
			
		||||
            windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t 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];
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if (windows[i].onGroupTitleChange != NULL)
 | 
			
		||||
            windows[i].onGroupTitleChange(&windows[i], m, groupnumber, peernumber, data, length);
 | 
			
		||||
        if (windows[i].onGroupTopicChange != NULL)
 | 
			
		||||
            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);
 | 
			
		||||
    y2 -= 2;
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
@@ -442,10 +550,12 @@ void on_window_resize(void)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
        if (w->chatwin->infobox.active) {
 | 
			
		||||
            delwin(w->chatwin->infobox.win);
 | 
			
		||||
            w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#endif   /* AUDIO */
 | 
			
		||||
 | 
			
		||||
        scrollok(w->chatwin->history, 0);
 | 
			
		||||
@@ -455,19 +565,28 @@ void on_window_resize(void)
 | 
			
		||||
static void draw_window_tab(ToxWindow *toxwin)
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    clrtoeol();
 | 
			
		||||
    printw(" [%s]", toxwin->name);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void draw_bar(void)
 | 
			
		||||
{
 | 
			
		||||
    int y, x;
 | 
			
		||||
 | 
			
		||||
    // save current cursor position
 | 
			
		||||
    getyx(active_window->window, y, x);
 | 
			
		||||
 | 
			
		||||
    attron(COLOR_PAIR(BLUE));
 | 
			
		||||
    mvhline(LINES - 2, 0, '_', COLS);
 | 
			
		||||
    attroff(COLOR_PAIR(BLUE));
 | 
			
		||||
@@ -505,14 +624,16 @@ static void draw_bar(void)
 | 
			
		||||
            attroff(A_BOLD);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // restore cursor position after drawing
 | 
			
		||||
    move(y, x);
 | 
			
		||||
 | 
			
		||||
    refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void draw_active_window(Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow *a = active_window;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    ToxWindow *a = active_window;
 | 
			
		||||
    a->alert = WINDOW_ALERT_NONE;
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
@@ -522,6 +643,7 @@ void draw_active_window(Tox *m)
 | 
			
		||||
 | 
			
		||||
    touchwin(a->window);
 | 
			
		||||
    a->onDraw(a, m);
 | 
			
		||||
    wrefresh(a->window);
 | 
			
		||||
 | 
			
		||||
    /* Handle input */
 | 
			
		||||
    bool ltr;
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ struct cqueue_thread {
 | 
			
		||||
    pthread_t tid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_thread {
 | 
			
		||||
struct av_thread {
 | 
			
		||||
    pthread_t tid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -90,7 +90,7 @@ struct arg_opts {
 | 
			
		||||
    bool encrypt_data;
 | 
			
		||||
    bool unencrypt_data;
 | 
			
		||||
 | 
			
		||||
    char dns_path[MAX_STR_SIZE];
 | 
			
		||||
    char nameserver_path[MAX_STR_SIZE];
 | 
			
		||||
    char config_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(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS);
 | 
			
		||||
    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(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
 | 
			
		||||
    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(*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
 | 
			
		||||
 | 
			
		||||
    void(*onInvite)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onRinging)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onStarting)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onEnding)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onError)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onStart)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onCancel)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onReject)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onEnd)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onRequestTimeout)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onPeerTimeout)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onWriteDevice)(ToxWindow *, Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int);
 | 
			
		||||
    void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onRinging)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onStarting)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onEnding)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onError)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onStart)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onCancel)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onReject)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, 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 */
 | 
			
		||||
 | 
			
		||||
    bool is_call;
 | 
			
		||||
    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 */
 | 
			
		||||
 | 
			
		||||
    int active_box; /* For box notify */
 | 
			
		||||
@@ -201,8 +214,8 @@ struct infobox {
 | 
			
		||||
    bool hide;
 | 
			
		||||
    bool active;
 | 
			
		||||
 | 
			
		||||
    uint64_t lastupdate;
 | 
			
		||||
    uint64_t starttime;
 | 
			
		||||
    time_t lastupdate;
 | 
			
		||||
    time_t starttime;
 | 
			
		||||
    char timestr[TIME_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    WINDOW *win;
 | 
			
		||||
@@ -234,6 +247,7 @@ struct ChatContext {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    uint8_t self_is_typing;
 | 
			
		||||
    uint8_t pastemode; /* whether to translate \r to \n */
 | 
			
		||||
 | 
			
		||||
    WINDOW *history;
 | 
			
		||||
    WINDOW *linewin;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								src/xtra.c
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								src/xtra.c
									
									
									
									
									
								
							@@ -57,8 +57,7 @@ struct _Xtra {
 | 
			
		||||
    Atom expecting_type;
 | 
			
		||||
} Xtra;
 | 
			
		||||
 | 
			
		||||
typedef struct _Property
 | 
			
		||||
{
 | 
			
		||||
typedef struct _Property {
 | 
			
		||||
    unsigned char *data;
 | 
			
		||||
    int            read_format;
 | 
			
		||||
    unsigned long  read_num;
 | 
			
		||||
@@ -97,21 +96,23 @@ Property read_property(Window s, Atom p)
 | 
			
		||||
Atom get_dnd_type(long *a, int l)
 | 
			
		||||
{
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    for (; i < l; i ++) {
 | 
			
		||||
        if (a[i] != XtraNil) return a[i]; /* Get first valid */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return XtraNil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
 | 
			
		||||
    if ((e->data.l[1] & 1)) {
 | 
			
		||||
        // Fetch the list of possible conversions
 | 
			
		||||
        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);
 | 
			
		||||
    } else {
 | 
			
		||||
        // 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 = {
 | 
			
		||||
        .xclient = {
 | 
			
		||||
@@ -143,7 +144,7 @@ static void handle_xdnd_position(XClientMessageEvent* e)
 | 
			
		||||
    XFlush(Xtra.display);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_xdnd_drop(XClientMessageEvent* e)
 | 
			
		||||
static void handle_xdnd_drop(XClientMessageEvent *e)
 | 
			
		||||
{
 | 
			
		||||
    /* Not expecting any type */
 | 
			
		||||
    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 */
 | 
			
		||||
    XEvent ev = {
 | 
			
		||||
@@ -199,12 +200,12 @@ static void handle_xdnd_selection(XSelectionEvent* e)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /* Call callback for every entry */
 | 
			
		||||
    if (Xtra.on_drop && p.read_num)
 | 
			
		||||
    {
 | 
			
		||||
    if (Xtra.on_drop && p.read_num) {
 | 
			
		||||
        char *sptr;
 | 
			
		||||
        char *str = strtok_r((char *) p.data, "\n\r", &sptr);
 | 
			
		||||
 | 
			
		||||
        if (str) Xtra.on_drop(str, dt);
 | 
			
		||||
 | 
			
		||||
        while ((str = strtok_r(NULL, "\n\r", &sptr)))
 | 
			
		||||
            Xtra.on_drop(str, dt);
 | 
			
		||||
    }
 | 
			
		||||
@@ -212,7 +213,7 @@ static void handle_xdnd_selection(XSelectionEvent* e)
 | 
			
		||||
    if (p.data) XFree(p.data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *event_loop(void* p)
 | 
			
		||||
void *event_loop(void *p)
 | 
			
		||||
{
 | 
			
		||||
    /* Handle events like a real nigga */
 | 
			
		||||
 | 
			
		||||
@@ -221,30 +222,27 @@ void *event_loop(void* p)
 | 
			
		||||
    XEvent event;
 | 
			
		||||
    int pending;
 | 
			
		||||
 | 
			
		||||
    while (Xtra.display)
 | 
			
		||||
    {
 | 
			
		||||
    while (Xtra.display) {
 | 
			
		||||
        /* NEEDMOEVENTSFODEMPROGRAMS */
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            usleep(10000);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (event.type == ClientMessage)
 | 
			
		||||
        {
 | 
			
		||||
        if (event.type == ClientMessage) {
 | 
			
		||||
            Atom type = event.xclient.message_type;
 | 
			
		||||
 | 
			
		||||
            if      (type == XdndEnter)         handle_xdnd_enter(&event.xclient);
 | 
			
		||||
            else if (type == XdndPosition)      handle_xdnd_position(&event.xclient);
 | 
			
		||||
            else if (type == XdndDrop)          handle_xdnd_drop(&event.xclient);
 | 
			
		||||
            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*/
 | 
			
		||||
        else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
 | 
			
		||||
 | 
			
		||||
@@ -256,6 +254,7 @@ void *event_loop(void* p)
 | 
			
		||||
     * otherwise HEWUSAGUDBOI happens
 | 
			
		||||
     */
 | 
			
		||||
    if (Xtra.display) XCloseDisplay(Xtra.display);
 | 
			
		||||
 | 
			
		||||
    return (Xtra.display = NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -267,10 +266,16 @@ int init_xtra(drop_callback d)
 | 
			
		||||
    else Xtra.on_drop = d;
 | 
			
		||||
 | 
			
		||||
    XInitThreads();
 | 
			
		||||
 | 
			
		||||
    if ( !(Xtra.display = XOpenDisplay(NULL))) return -1;
 | 
			
		||||
 | 
			
		||||
    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. */
 | 
			
		||||
        XSetWindowAttributes attr  = {0};
 | 
			
		||||
@@ -330,9 +335,10 @@ int init_xtra(drop_callback d)
 | 
			
		||||
                    XA_ATOM,
 | 
			
		||||
                    32,
 | 
			
		||||
                    PropModeReplace,
 | 
			
		||||
                    (unsigned char*)&XdndVersion, 1);
 | 
			
		||||
                    (unsigned char *)&XdndVersion, 1);
 | 
			
		||||
 | 
			
		||||
    pthread_t id;
 | 
			
		||||
 | 
			
		||||
    if (pthread_create(&id, NULL, event_loop, NULL) != 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
@@ -343,7 +349,7 @@ int init_xtra(drop_callback d)
 | 
			
		||||
 | 
			
		||||
void terminate_xtra()
 | 
			
		||||
{
 | 
			
		||||
    if (!Xtra.display) return;
 | 
			
		||||
    if (!Xtra.display || !Xtra.terminal_window) return;
 | 
			
		||||
 | 
			
		||||
    XEvent terminate = {
 | 
			
		||||
        .xclient = {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ typedef enum {
 | 
			
		||||
}
 | 
			
		||||
DropType;
 | 
			
		||||
 | 
			
		||||
typedef void (*drop_callback) (const char*, DropType);
 | 
			
		||||
typedef void (*drop_callback) (const char *, DropType);
 | 
			
		||||
 | 
			
		||||
int               init_xtra(drop_callback d);
 | 
			
		||||
void              terminate_xtra();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user