mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-11-04 15:06:51 +01:00 
			
		
		
		
	Compare commits
	
		
			316 Commits
		
	
	
		
			v0.7.0
			...
			TokTok-mas
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ae94bc593b | ||
| 
						 | 
					81eb58532e | ||
| 
						 | 
					8464ea9a7a | ||
| 
						 | 
					b77bff35a1 | ||
| 
						 | 
					eb964b64c2 | ||
| 
						 | 
					2ff9d29491 | ||
| 
						 | 
					2640919318 | ||
| 
						 | 
					2fcbc4fa1c | ||
| 
						 | 
					4330bf5867 | ||
| 
						 | 
					3f1b7cdd26 | ||
| 
						 | 
					1e985c1456 | ||
| 
						 | 
					61740bda85 | ||
| 
						 | 
					0d8e6d713e | ||
| 
						 | 
					39e4ff8bd6 | ||
| 
						 | 
					0434ac186a | ||
| 
						 | 
					8d9d51640c | ||
| 
						 | 
					c4c0c0d1f4 | ||
| 
						 | 
					3f2826bd66 | ||
| 
						 | 
					7b7ea0e386 | ||
| 
						 | 
					d35a38735b | ||
| 
						 | 
					f0c4906fdc | ||
| 
						 | 
					56ba61e061 | ||
| 
						 | 
					898d89e95a | ||
| 
						 | 
					1fd1e27bdf | ||
| 
						 | 
					8e84ac58d4 | ||
| 
						 | 
					9d65997871 | ||
| 
						 | 
					da2889f3ab | ||
| 
						 | 
					312b38d253 | ||
| 
						 | 
					0554bf0240 | ||
| 
						 | 
					53a7530e8a | ||
| 
						 | 
					41be04a142 | ||
| 
						 | 
					31f36318a2 | ||
| 
						 | 
					f882fdf608 | ||
| 
						 | 
					7e1e410307 | ||
| 
						 | 
					c135c812c2 | ||
| 
						 | 
					6c239193ab | ||
| 
						 | 
					de7db08352 | ||
| 
						 | 
					ba5ded9bc2 | ||
| 
						 | 
					4581dee4fc | ||
| 
						 | 
					d75d6e8b60 | ||
| 
						 | 
					142ce642f0 | ||
| 
						 | 
					7dead5ec96 | ||
| 
						 | 
					ddcf224db2 | ||
| 
						 | 
					daf794c4a2 | ||
| 
						 | 
					dac0124f0f | ||
| 
						 | 
					15b7a30925 | ||
| 
						 | 
					77ab71f26f | ||
| 
						 | 
					68e1ba312d | ||
| 
						 | 
					752fc6d619 | ||
| 
						 | 
					16bcb27ca7 | ||
| 
						 | 
					71d7d355a6 | ||
| 
						 | 
					4188b392cc | ||
| 
						 | 
					811fbfbb1e | ||
| 
						 | 
					32eb7d3040 | ||
| 
						 | 
					42763905d7 | ||
| 
						 | 
					f64300d1d6 | ||
| 
						 | 
					1a723f0e8e | ||
| 
						 | 
					a86884c40e | ||
| 
						 | 
					3f02e119f4 | ||
| 
						 | 
					1bbd50aac7 | ||
| 
						 | 
					e7a0c32a68 | ||
| 
						 | 
					7560bc9547 | ||
| 
						 | 
					2b43340c90 | ||
| 
						 | 
					ff1620c923 | ||
| 
						 | 
					1303053a27 | ||
| 
						 | 
					91f194c821 | ||
| 
						 | 
					478762f76c | ||
| 
						 | 
					4d96d6a753 | ||
| 
						 | 
					3cdcfbf4e5 | ||
| 
						 | 
					4c302da503 | ||
| 
						 | 
					26b5fe8f9d | ||
| 
						 | 
					22d60232fb | ||
| 
						 | 
					e428879beb | ||
| 
						 | 
					3015138a5a | ||
| 
						 | 
					9c06ad608b | ||
| 
						 | 
					015dbd9a96 | ||
| 
						 | 
					a7466c3142 | ||
| 
						 | 
					f012007cc4 | ||
| 
						 | 
					dcf3baf60f | ||
| 
						 | 
					4bda799a4b | ||
| 
						 | 
					bdeae33d48 | ||
| 
						 | 
					47591d5298 | ||
| 
						 | 
					b5ace27a3e | ||
| 
						 | 
					b334622d36 | ||
| 
						 | 
					4bfb344caa | ||
| 
						 | 
					16d96d6faf | ||
| 
						 | 
					0ab2bad226 | ||
| 
						 | 
					68db926f9f | ||
| 
						 | 
					b270c1e8b7 | ||
| 
						 | 
					e7142e49fd | ||
| 
						 | 
					610906d07f | ||
| 
						 | 
					6f72a191ba | ||
| 
						 | 
					dd5fa236ae | ||
| 
						 | 
					51e1ab94b3 | ||
| 
						 | 
					ddc8c53abf | ||
| 
						 | 
					46513017e3 | ||
| 
						 | 
					98cb7f58c0 | ||
| 
						 | 
					206bf407fd | ||
| 
						 | 
					0a8ac4de3b | ||
| 
						 | 
					87d54acad0 | ||
| 
						 | 
					45ff6d8bac | ||
| 
						 | 
					437dd8baeb | ||
| 
						 | 
					b080236ee5 | ||
| 
						 | 
					116bff8cef | ||
| 
						 | 
					ddeca171a0 | ||
| 
						 | 
					127f9462e0 | ||
| 
						 | 
					4b5a9abbd4 | ||
| 
						 | 
					bb2257973e | ||
| 
						 | 
					12b9cd2386 | ||
| 
						 | 
					2cbe8fa880 | ||
| 
						 | 
					2e39bee05a | ||
| 
						 | 
					05eda76643 | ||
| 
						 | 
					f7b73af9a7 | ||
| 
						 | 
					73aaa44d12 | ||
| 
						 | 
					e4abd8b36b | ||
| 
						 | 
					9e3d4f3889 | ||
| 
						 | 
					b7d67c1d86 | ||
| 
						 | 
					c4a11f8dc7 | ||
| 
						 | 
					d18cc8cbc2 | ||
| 
						 | 
					ce6d4861fb | ||
| 
						 | 
					8f0e6026f0 | ||
| 
						 | 
					258736995d | ||
| 
						 | 
					56e03a3f8b | ||
| 
						 | 
					b6c746b5f5 | ||
| 
						 | 
					03673cbced | ||
| 
						 | 
					0fea930c24 | ||
| 
						 | 
					94d22a8853 | ||
| 
						 | 
					63cc23401a | ||
| 
						 | 
					f90a774470 | ||
| 
						 | 
					e7c5fbc873 | ||
| 
						 | 
					d62902ffb3 | ||
| 
						 | 
					bebff3be0e | ||
| 
						 | 
					2be4847b53 | ||
| 
						 | 
					4557614443 | ||
| 
						 | 
					5b30ecf2e4 | ||
| 
						 | 
					2413ad2b59 | ||
| 
						 | 
					52855b805a | ||
| 
						 | 
					20b5e46850 | ||
| 
						 | 
					f2b796940e | ||
| 
						 | 
					a37bf300f9 | ||
| 
						 | 
					cb524dcbc3 | ||
| 
						 | 
					4144b868ce | ||
| 
						 | 
					af11f16bef | ||
| 
						 | 
					1d27a496ef | ||
| 
						 | 
					32bd9dc1a7 | ||
| 
						 | 
					3cd2bc7e3c | ||
| 
						 | 
					43ca840658 | ||
| 
						 | 
					685837357b | ||
| 
						 | 
					812a13b0fb | ||
| 
						 | 
					641fa471d2 | ||
| 
						 | 
					8d5755f2d8 | ||
| 
						 | 
					af510b6666 | ||
| 
						 | 
					46f646afcf | ||
| 
						 | 
					68ce17a57f | ||
| 
						 | 
					a69fad15c1 | ||
| 
						 | 
					7621fe9a62 | ||
| 
						 | 
					f6d9bc3a74 | ||
| 
						 | 
					29aea0b42c | ||
| 
						 | 
					815c29ee31 | ||
| 
						 | 
					3917f664bf | ||
| 
						 | 
					a223329815 | ||
| 
						 | 
					3fec11d5f9 | ||
| 
						 | 
					221edb0012 | ||
| 
						 | 
					2710ab6034 | ||
| 
						 | 
					bc3ffac0ba | ||
| 
						 | 
					29f55c5277 | ||
| 
						 | 
					a290f0f7f8 | ||
| 
						 | 
					5cd196a769 | ||
| 
						 | 
					b14d983a8c | ||
| 
						 | 
					51f1daeec8 | ||
| 
						 | 
					b799c6a8d7 | ||
| 
						 | 
					b9f9546e2b | ||
| 
						 | 
					846bc4613e | ||
| 
						 | 
					a5a1f6015d | ||
| 
						 | 
					fe6a7074ea | ||
| 
						 | 
					db7c9fe426 | ||
| 
						 | 
					b1d8ab102f | ||
| 
						 | 
					0bd5b4ddee | ||
| 
						 | 
					c387df35f8 | ||
| 
						 | 
					351a50c214 | ||
| 
						 | 
					93175314b5 | ||
| 
						 | 
					b905a1a3c5 | ||
| 
						 | 
					c4386b195f | ||
| 
						 | 
					ed1e617380 | ||
| 
						 | 
					1382adb1f6 | ||
| 
						 | 
					ecf1c317b7 | ||
| 
						 | 
					cf0b99f1e5 | ||
| 
						 | 
					3605a296a9 | ||
| 
						 | 
					9375d220f9 | ||
| 
						 | 
					8f94b0a218 | ||
| 
						 | 
					85a0becbf9 | ||
| 
						 | 
					fec36ad9e6 | ||
| 
						 | 
					ecdf6f01d2 | ||
| 
						 | 
					e1bfa30769 | ||
| 
						 | 
					ebcbc7497b | ||
| 
						 | 
					e844ece28b | ||
| 
						 | 
					8508451ba6 | ||
| 
						 | 
					5cc83a7cb5 | ||
| 
						 | 
					febc725763 | ||
| 
						 | 
					f2c116feb3 | ||
| 
						 | 
					52dd60dc86 | ||
| 
						 | 
					80c0500299 | ||
| 
						 | 
					ab490d28b4 | ||
| 
						 | 
					a9f7f85617 | ||
| 
						 | 
					1bfc1ba371 | ||
| 
						 | 
					2ede39369a | ||
| 
						 | 
					922c184195 | ||
| 
						 | 
					56a9571509 | ||
| 
						 | 
					0136f22076 | ||
| 
						 | 
					c4ace288af | ||
| 
						 | 
					6d3fbfee59 | ||
| 
						 | 
					be5e7906da | ||
| 
						 | 
					369f26932e | ||
| 
						 | 
					22ea522baf | ||
| 
						 | 
					4f60d546e6 | ||
| 
						 | 
					76d1eafdc0 | ||
| 
						 | 
					37912f2d88 | ||
| 
						 | 
					09710327c5 | ||
| 
						 | 
					acee4615f8 | ||
| 
						 | 
					5ed26eda9b | ||
| 
						 | 
					6d2b90ac9f | ||
| 
						 | 
					02ea0fac44 | ||
| 
						 | 
					7d3d129624 | ||
| 
						 | 
					b3ed8bc35c | ||
| 
						 | 
					90210daca7 | ||
| 
						 | 
					0e13a1f1bc | ||
| 
						 | 
					09e2690211 | ||
| 
						 | 
					e65e3af274 | ||
| 
						 | 
					c6c60d018e | ||
| 
						 | 
					451d4ced80 | ||
| 
						 | 
					7a7402ff86 | ||
| 
						 | 
					600e013adc | ||
| 
						 | 
					1d71e2eb18 | ||
| 
						 | 
					f858714edd | ||
| 
						 | 
					4df44a7274 | ||
| 
						 | 
					a26ed9d28f | ||
| 
						 | 
					2bd5083b8f | ||
| 
						 | 
					8805f694b9 | ||
| 
						 | 
					71040355fd | ||
| 
						 | 
					6bc5d8c543 | ||
| 
						 | 
					abb39ea6b5 | ||
| 
						 | 
					15846d2b50 | ||
| 
						 | 
					958df9f2e8 | ||
| 
						 | 
					2fd43aebee | ||
| 
						 | 
					34c29745cc | ||
| 
						 | 
					da6fe41d75 | ||
| 
						 | 
					e17fa89d8f | ||
| 
						 | 
					f056f13329 | ||
| 
						 | 
					3515623159 | ||
| 
						 | 
					2194b9e259 | ||
| 
						 | 
					c24e1bd2b8 | ||
| 
						 | 
					38ec96e96a | ||
| 
						 | 
					d2b572ede1 | ||
| 
						 | 
					703d5419a3 | ||
| 
						 | 
					221d761ff4 | ||
| 
						 | 
					151f5f0c51 | ||
| 
						 | 
					4f6c603543 | ||
| 
						 | 
					a009f11c0c | ||
| 
						 | 
					1f8c11a33a | ||
| 
						 | 
					5e20e6b279 | ||
| 
						 | 
					1f02bb2be5 | ||
| 
						 | 
					98154b3cba | ||
| 
						 | 
					379ad9e116 | ||
| 
						 | 
					cb21672600 | ||
| 
						 | 
					4019395f44 | ||
| 
						 | 
					ee084c572c | ||
| 
						 | 
					41a8401ac5 | ||
| 
						 | 
					d8a3f7de4c | ||
| 
						 | 
					c425aa2f27 | ||
| 
						 | 
					94e026d114 | ||
| 
						 | 
					f89638635a | ||
| 
						 | 
					402b86687f | ||
| 
						 | 
					5b1b420ac0 | ||
| 
						 | 
					62ec514f17 | ||
| 
						 | 
					f893dd755f | ||
| 
						 | 
					9aedcf7753 | ||
| 
						 | 
					d3effa26b5 | ||
| 
						 | 
					2ec180789b | ||
| 
						 | 
					9f74d3a3a8 | ||
| 
						 | 
					9fcbc3bde0 | ||
| 
						 | 
					cf16849b37 | ||
| 
						 | 
					32442b6286 | ||
| 
						 | 
					50f227418b | ||
| 
						 | 
					fc06a625a6 | ||
| 
						 | 
					70bd39eb74 | ||
| 
						 | 
					4e0e322e32 | ||
| 
						 | 
					e73ac9b6a4 | ||
| 
						 | 
					bcda6e476e | ||
| 
						 | 
					5b29ce7132 | ||
| 
						 | 
					f43f644451 | ||
| 
						 | 
					d6fdac9739 | ||
| 
						 | 
					c6a2bb8a90 | ||
| 
						 | 
					04576fea7e | ||
| 
						 | 
					e6f839f9ac | ||
| 
						 | 
					eb02424f8a | ||
| 
						 | 
					2e609c46f6 | ||
| 
						 | 
					a474e3bf39 | ||
| 
						 | 
					93835f0455 | ||
| 
						 | 
					ac6d8ff89c | ||
| 
						 | 
					88e74224ed | ||
| 
						 | 
					deccaec40e | ||
| 
						 | 
					4419be36e8 | ||
| 
						 | 
					b34b51e8c1 | ||
| 
						 | 
					74416b4b58 | ||
| 
						 | 
					675712cea0 | ||
| 
						 | 
					36feebfe8d | ||
| 
						 | 
					3fe9abd84d | ||
| 
						 | 
					fd6432c727 | ||
| 
						 | 
					1feffcc2f0 | ||
| 
						 | 
					6bba3cb9e2 | ||
| 
						 | 
					3cb6db3d60 | ||
| 
						 | 
					77238eeadf | ||
| 
						 | 
					88270827a9 | ||
| 
						 | 
					aade65bfe1 | ||
| 
						 | 
					b24c5d8cf8 | ||
| 
						 | 
					9f0feb7223 | 
							
								
								
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
/.github/	@TokTok/admins
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
---
 | 
			
		||||
github: [JFreegman]
 | 
			
		||||
							
								
								
									
										17
									
								
								.github/settings.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.github/settings.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
---
 | 
			
		||||
_extends: .github
 | 
			
		||||
 | 
			
		||||
repository:
 | 
			
		||||
  name: toxic
 | 
			
		||||
  description: An ncurses-based Tox client
 | 
			
		||||
  topics: tox, console, chat
 | 
			
		||||
 | 
			
		||||
branches:
 | 
			
		||||
  - name: "master"
 | 
			
		||||
    protection:
 | 
			
		||||
      required_status_checks:
 | 
			
		||||
        contexts:
 | 
			
		||||
          - Codacy/PR Quality Review
 | 
			
		||||
          - CodeFactor
 | 
			
		||||
          - Travis CI - Pull Request
 | 
			
		||||
          - code-review/reviewable
 | 
			
		||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -16,3 +16,4 @@ stamp-h1
 | 
			
		||||
build/toxic
 | 
			
		||||
build/*.o
 | 
			
		||||
build/*.d
 | 
			
		||||
apidoc/python/build
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.restyled.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.restyled.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
---
 | 
			
		||||
restylers:
 | 
			
		||||
  - astyle:
 | 
			
		||||
      arguments: ["--options=astylerc"]
 | 
			
		||||
							
								
								
									
										60
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
---
 | 
			
		||||
language: python
 | 
			
		||||
python: nightly
 | 
			
		||||
dist: xenial
 | 
			
		||||
os: linux
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  include:
 | 
			
		||||
    - env: JOB=linux
 | 
			
		||||
 | 
			
		||||
      addons:
 | 
			
		||||
        apt:
 | 
			
		||||
          packages:
 | 
			
		||||
            - libalut-dev
 | 
			
		||||
            - libconfig-dev
 | 
			
		||||
            - libnotify-dev
 | 
			
		||||
            - libopenal-dev
 | 
			
		||||
            - libopus-dev
 | 
			
		||||
            - libqrencode-dev
 | 
			
		||||
            - libvpx-dev
 | 
			
		||||
 | 
			
		||||
      cache:
 | 
			
		||||
        directories:
 | 
			
		||||
          - $HOME/cache
 | 
			
		||||
 | 
			
		||||
      install:
 | 
			
		||||
        # Where to find libraries.
 | 
			
		||||
        - export LD_LIBRARY_PATH=$HOME/cache/usr/lib
 | 
			
		||||
        - export PKG_CONFIG_PATH=$HOME/cache/usr/lib/pkgconfig
 | 
			
		||||
        # c-sodium
 | 
			
		||||
        - git clone --depth=1 --branch=stable https://github.com/jedisct1/libsodium ../libsodium
 | 
			
		||||
        - test -f $HOME/cache/usr/lib/libsodium.so || (cd ../libsodium && ./configure --prefix=$HOME/cache/usr && make install -j$(nproc))
 | 
			
		||||
        # c-toxcore
 | 
			
		||||
        - git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
 | 
			
		||||
        - test -f $HOME/cache/usr/lib/libtoxcore.so || (cd ../c-toxcore && cmake -B_build -H. -DCMAKE_INSTALL_PREFIX:PATH=$HOME/cache/usr && make -C_build install -j$(nproc))
 | 
			
		||||
 | 
			
		||||
      script:
 | 
			
		||||
        - make ENABLE_PYTHON=1 -j$(nproc)
 | 
			
		||||
 | 
			
		||||
    - env: JOB=macos
 | 
			
		||||
      os: macos
 | 
			
		||||
      language: c
 | 
			
		||||
 | 
			
		||||
      cache:
 | 
			
		||||
        directories:
 | 
			
		||||
          - $HOME/cache
 | 
			
		||||
 | 
			
		||||
      install:
 | 
			
		||||
        - brew install
 | 
			
		||||
          freealut
 | 
			
		||||
          libconfig
 | 
			
		||||
          libqrencode
 | 
			
		||||
          libsodium
 | 
			
		||||
          openal-soft
 | 
			
		||||
        - export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.21.0/lib"
 | 
			
		||||
        - git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
 | 
			
		||||
        - test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc))
 | 
			
		||||
 | 
			
		||||
      script:
 | 
			
		||||
        - make ENABLE_PYTHON=1 DISABLE_DESKTOP_NOTIFY=1 DISABLE_X11=1 DISABLE_AV=1 DISABLE_SOUND_NOTIFY=1 -j$(nproc)
 | 
			
		||||
							
								
								
									
										41
									
								
								BUILD.bazel
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								BUILD.bazel
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
load("@rules_cc//cc:defs.bzl", "cc_binary")
 | 
			
		||||
load("//tools/project:build_defs.bzl", "project")
 | 
			
		||||
 | 
			
		||||
project()
 | 
			
		||||
 | 
			
		||||
cc_binary(
 | 
			
		||||
    name = "toxic",
 | 
			
		||||
    srcs = glob(
 | 
			
		||||
        [
 | 
			
		||||
            "src/*.c",
 | 
			
		||||
            "src/*.h",
 | 
			
		||||
        ],
 | 
			
		||||
        exclude = ["src/video*"],
 | 
			
		||||
    ) + select({
 | 
			
		||||
        "//tools/config:linux": glob(["src/video*"]),
 | 
			
		||||
        "//tools/config:osx": [],
 | 
			
		||||
    }),
 | 
			
		||||
    copts = [
 | 
			
		||||
        "-std=gnu99",
 | 
			
		||||
        "-DAUDIO",
 | 
			
		||||
        "-DPACKAGE_DATADIR='\"data\"'",
 | 
			
		||||
        "-DPYTHON",
 | 
			
		||||
        "-DQRCODE",
 | 
			
		||||
    ] + select({
 | 
			
		||||
        "//tools/config:linux": ["-DVIDEO"],
 | 
			
		||||
        "//tools/config:osx": [],
 | 
			
		||||
    }),
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//c-toxcore",
 | 
			
		||||
        "@curl",
 | 
			
		||||
        "@libconfig",
 | 
			
		||||
        "@libqrencode",
 | 
			
		||||
        "@libvpx",
 | 
			
		||||
        "@ncurses",
 | 
			
		||||
        "@openal",
 | 
			
		||||
        "@python3//:python",
 | 
			
		||||
    ] + select({
 | 
			
		||||
        "//tools/config:linux": ["@x11"],
 | 
			
		||||
        "//tools/config:osx": [],
 | 
			
		||||
    }),
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										503
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										503
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,503 @@
 | 
			
		||||
# Change Log
 | 
			
		||||
 | 
			
		||||
## [Unreleased](https://github.com/JFreegman/toxic/tree/HEAD)
 | 
			
		||||
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.7.0...HEAD)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- How can I copy everything from one computer to another? [\#391](https://github.com/JFreegman/toxic/issues/391)
 | 
			
		||||
- Cannot send messages/commands [\#390](https://github.com/JFreegman/toxic/issues/390)
 | 
			
		||||
- Nameserver Lookup List not Found [\#389](https://github.com/JFreegman/toxic/issues/389)
 | 
			
		||||
- ERROR: toxini file 'tox.ini' not found [\#388](https://github.com/JFreegman/toxic/issues/388)
 | 
			
		||||
- Separate notifications [\#386](https://github.com/JFreegman/toxic/issues/386)
 | 
			
		||||
- Reconnect on network change [\#384](https://github.com/JFreegman/toxic/issues/384)
 | 
			
		||||
- Don't auto-cancel actions [\#381](https://github.com/JFreegman/toxic/issues/381)
 | 
			
		||||
- How to export your profile? [\#377](https://github.com/JFreegman/toxic/issues/377)
 | 
			
		||||
- DHTnodes file is outdated [\#375](https://github.com/JFreegman/toxic/issues/375)
 | 
			
		||||
- Toxic fails to initialize if ~/.config directory doesn't exist [\#372](https://github.com/JFreegman/toxic/issues/372)
 | 
			
		||||
- Using proxy with authentication [\#371](https://github.com/JFreegman/toxic/issues/371)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Add multiline support [\#387](https://github.com/JFreegman/toxic/pull/387) ([mphe](https://github.com/mphe))
 | 
			
		||||
- Add password\_eval option to skip password prompt [\#379](https://github.com/JFreegman/toxic/pull/379) ([FreakyPenguin](https://github.com/FreakyPenguin))
 | 
			
		||||
- sleep use tox\_iteration\_interval [\#374](https://github.com/JFreegman/toxic/pull/374) ([quininer](https://github.com/quininer))
 | 
			
		||||
- Fix \#372 - can't start with missing ~/.config [\#373](https://github.com/JFreegman/toxic/pull/373) ([wedge-jarrad](https://github.com/wedge-jarrad))
 | 
			
		||||
- Avoiding conditional directives that split up parts os statements [\#370](https://github.com/JFreegman/toxic/pull/370) ([RomeroMalaquias](https://github.com/RomeroMalaquias))
 | 
			
		||||
- update doc: DATA\_FILE is now `toxic\_profile.tox` [\#369](https://github.com/JFreegman/toxic/pull/369) ([nil0x42](https://github.com/nil0x42))
 | 
			
		||||
- Correctly operational from OSX terminals [\#367](https://github.com/JFreegman/toxic/pull/367) ([landswellsong](https://github.com/landswellsong))
 | 
			
		||||
 | 
			
		||||
## [v0.7.0](https://github.com/JFreegman/toxic/tree/v0.7.0) (2015-11-12)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.1...v0.7.0)
 | 
			
		||||
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- /myid doesn't show qrcode [\#326](https://github.com/JFreegman/toxic/issues/326)
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- Installation failed on ubuntu 12.04, package missing [\#279](https://github.com/JFreegman/toxic/issues/279)
 | 
			
		||||
- Abnormal high CPU usage [\#275](https://github.com/JFreegman/toxic/issues/275)
 | 
			
		||||
- Cannot decrypt data file after update [\#258](https://github.com/JFreegman/toxic/issues/258)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Compiling video\_device.c on FreeBSD [\#364](https://github.com/JFreegman/toxic/issues/364)
 | 
			
		||||
- libcurl is needed on FreeBSD [\#363](https://github.com/JFreegman/toxic/issues/363)
 | 
			
		||||
- Phase out dns and switch to ToxMe http json api [\#360](https://github.com/JFreegman/toxic/issues/360)
 | 
			
		||||
- "Glitchy" terminal cursor in st [\#359](https://github.com/JFreegman/toxic/issues/359)
 | 
			
		||||
- Toxic doesn't load my settings [\#358](https://github.com/JFreegman/toxic/issues/358)
 | 
			
		||||
- Does Toxic support proxy? [\#355](https://github.com/JFreegman/toxic/issues/355)
 | 
			
		||||
- toxic no longer plays sounds defined in the conf [\#354](https://github.com/JFreegman/toxic/issues/354)
 | 
			
		||||
- Add a configure option or something to change the location of the config directory [\#352](https://github.com/JFreegman/toxic/issues/352)
 | 
			
		||||
- Remove/Replace links to libtoxcore.so [\#349](https://github.com/JFreegman/toxic/issues/349)
 | 
			
		||||
- "No pending friend requests." while"Friend request has already been sent." [\#348](https://github.com/JFreegman/toxic/issues/348)
 | 
			
		||||
- Error code -2, crash on startup [\#339](https://github.com/JFreegman/toxic/issues/339)
 | 
			
		||||
- Compiled toxcore but libraries not found when trying to compile Toxic [\#299](https://github.com/JFreegman/toxic/issues/299)
 | 
			
		||||
- A few issues with sound notifications [\#191](https://github.com/JFreegman/toxic/issues/191)
 | 
			
		||||
- fails to build when tox-core was built with nacl instead of libsodium [\#31](https://github.com/JFreegman/toxic/issues/31)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Fix spelling mistake BOARDER -\> BORDER [\#362](https://github.com/JFreegman/toxic/pull/362) ([subliun](https://github.com/subliun))
 | 
			
		||||
- Fix compile for DragonFlyBSD [\#351](https://github.com/JFreegman/toxic/pull/351) ([mneumann](https://github.com/mneumann))
 | 
			
		||||
 | 
			
		||||
## [v0.6.1](https://github.com/JFreegman/toxic/tree/v0.6.1) (2015-08-28)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.0...v0.6.1)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- \[Invalid UTF-8\] [\#344](https://github.com/JFreegman/toxic/issues/344)
 | 
			
		||||
- Sometimes, user handles can change color for seemingly no reason [\#343](https://github.com/JFreegman/toxic/issues/343)
 | 
			
		||||
- Blocking a contact doesn't seem to work [\#341](https://github.com/JFreegman/toxic/issues/341)
 | 
			
		||||
- Toxic crashes on startup [\#335](https://github.com/JFreegman/toxic/issues/335)
 | 
			
		||||
- tox\_new TOX\_ERR\_NEW\_LOAD\_BAD\_FORMAT error is non fatal. [\#333](https://github.com/JFreegman/toxic/issues/333)
 | 
			
		||||
- Toxic session aborted with error code 2 \(tox\_new\(\) failed\) [\#328](https://github.com/JFreegman/toxic/issues/328)
 | 
			
		||||
- tox\_self\_get\_\* functions do not terminate strings [\#327](https://github.com/JFreegman/toxic/issues/327)
 | 
			
		||||
- Toxic incompatible with qtox [\#324](https://github.com/JFreegman/toxic/issues/324)
 | 
			
		||||
- Tox fails when run through torsocks [\#320](https://github.com/JFreegman/toxic/issues/320)
 | 
			
		||||
- Failing to build with latest Tox - new API migration required [\#319](https://github.com/JFreegman/toxic/issues/319)
 | 
			
		||||
- Avoid non-posix option in sed. [\#307](https://github.com/JFreegman/toxic/issues/307)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- fix a broken link [\#350](https://github.com/JFreegman/toxic/pull/350) ([vinegret](https://github.com/vinegret))
 | 
			
		||||
- Makefile: allow overriding pkg-config [\#346](https://github.com/JFreegman/toxic/pull/346) ([ony](https://github.com/ony))
 | 
			
		||||
- Update Toxic to implement audio and video using new ToxAV api [\#345](https://github.com/JFreegman/toxic/pull/345) ([cnhenry](https://github.com/cnhenry))
 | 
			
		||||
- travis.yml: update dependencies [\#340](https://github.com/JFreegman/toxic/pull/340) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Add localization system \(gettext\) [\#337](https://github.com/JFreegman/toxic/pull/337) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Makefile: try to fix Tox/toxic\#307 [\#323](https://github.com/JFreegman/toxic/pull/323) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Makefile: add uninstall target [\#322](https://github.com/JFreegman/toxic/pull/322) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
 | 
			
		||||
## [v0.6.0](https://github.com/JFreegman/toxic/tree/v0.6.0) (2015-03-28)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.2...v0.6.0)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Please do not force push to tox/toxic master branch. [\#311](https://github.com/JFreegman/toxic/issues/311)
 | 
			
		||||
- Import tox id [\#295](https://github.com/JFreegman/toxic/issues/295)
 | 
			
		||||
- openalut [\#287](https://github.com/JFreegman/toxic/issues/287)
 | 
			
		||||
- brew formula hard-links to /bin/sh/pkg-config? \(OS X\) [\#286](https://github.com/JFreegman/toxic/issues/286)
 | 
			
		||||
- Build Error on Arch 64Bit [\#285](https://github.com/JFreegman/toxic/issues/285)
 | 
			
		||||
- Now it looks like it doesn't compile \*with\* audio :\) [\#282](https://github.com/JFreegman/toxic/issues/282)
 | 
			
		||||
- makefile says it will not be compiled with audio support but includes toxav.h anyway. [\#281](https://github.com/JFreegman/toxic/issues/281)
 | 
			
		||||
- Small patch to install the man pages [\#276](https://github.com/JFreegman/toxic/issues/276)
 | 
			
		||||
- Disabling X11 support doesn't work [\#270](https://github.com/JFreegman/toxic/issues/270)
 | 
			
		||||
- Support arrow keys [\#265](https://github.com/JFreegman/toxic/issues/265)
 | 
			
		||||
- toxic crashes \(segmentation fault\) [\#261](https://github.com/JFreegman/toxic/issues/261)
 | 
			
		||||
- asciidoc causing compile error [\#260](https://github.com/JFreegman/toxic/issues/260)
 | 
			
		||||
- これはセグフォールトですか [\#259](https://github.com/JFreegman/toxic/issues/259)
 | 
			
		||||
- Verify ~/.config/tox permissions on startup [\#245](https://github.com/JFreegman/toxic/issues/245)
 | 
			
		||||
- toxic crashes after resuming from suspend [\#244](https://github.com/JFreegman/toxic/issues/244)
 | 
			
		||||
- Toxic does not compile on osx 10.9.3 [\#145](https://github.com/JFreegman/toxic/issues/145)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- README.md: fix typo [\#318](https://github.com/JFreegman/toxic/pull/318) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Makefile: be less aggressive when cleaning [\#316](https://github.com/JFreegman/toxic/pull/316) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Move makefile into root directory [\#315](https://github.com/JFreegman/toxic/pull/315) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Fixing couple leaking file descriptors [\#314](https://github.com/JFreegman/toxic/pull/314) ([al42and](https://github.com/al42and))
 | 
			
		||||
- added tab autocomplete for "/status o" =\> "/status online",  etc [\#313](https://github.com/JFreegman/toxic/pull/313) ([hardlyeven](https://github.com/hardlyeven))
 | 
			
		||||
- Some cosmetics changes [\#310](https://github.com/JFreegman/toxic/pull/310) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Openbsd [\#308](https://github.com/JFreegman/toxic/pull/308) ([henriqueleng](https://github.com/henriqueleng))
 | 
			
		||||
- Add support for custom timestamps in chat and logs. [\#303](https://github.com/JFreegman/toxic/pull/303) ([louipc](https://github.com/louipc))
 | 
			
		||||
- README.md: update download section [\#302](https://github.com/JFreegman/toxic/pull/302) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Add INSTALL.md [\#301](https://github.com/JFreegman/toxic/pull/301) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- travis.yml: use latest libsodium stable [\#298](https://github.com/JFreegman/toxic/pull/298) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Travis should build with Libsodium stable, fix clang [\#297](https://github.com/JFreegman/toxic/pull/297) ([urras](https://github.com/urras))
 | 
			
		||||
- Interface [\#296](https://github.com/JFreegman/toxic/pull/296) ([louipc](https://github.com/louipc))
 | 
			
		||||
- Correct filename comment from main.c to toxic.c [\#293](https://github.com/JFreegman/toxic/pull/293) ([Spagy](https://github.com/Spagy))
 | 
			
		||||
- Update for toxcore API break [\#292](https://github.com/JFreegman/toxic/pull/292) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Fix some edge cases when obtaining paths [\#291](https://github.com/JFreegman/toxic/pull/291) ([dantok](https://github.com/dantok))
 | 
			
		||||
- Update DHT nodes again [\#290](https://github.com/JFreegman/toxic/pull/290) ([urras](https://github.com/urras))
 | 
			
		||||
- Update DHT node list [\#289](https://github.com/JFreegman/toxic/pull/289) ([urras](https://github.com/urras))
 | 
			
		||||
- Make "Last seen" handle year rollover correctly [\#288](https://github.com/JFreegman/toxic/pull/288) ([flussence](https://github.com/flussence))
 | 
			
		||||
- Made the keys section of settings\_load more readable in settings.c [\#284](https://github.com/JFreegman/toxic/pull/284) ([jpoler](https://github.com/jpoler))
 | 
			
		||||
- Destroy AL context before closing dhndl [\#283](https://github.com/JFreegman/toxic/pull/283) ([stal888](https://github.com/stal888))
 | 
			
		||||
- Darwin Build [\#280](https://github.com/JFreegman/toxic/pull/280) ([DomT4](https://github.com/DomT4))
 | 
			
		||||
- Fix Tox/toxic\#276 [\#278](https://github.com/JFreegman/toxic/pull/278) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Makefile: revert back to mkdir [\#274](https://github.com/JFreegman/toxic/pull/274) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Makefile: add toxic.desktop to install target [\#273](https://github.com/JFreegman/toxic/pull/273) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Toxic.conf.exmaple: fix sound namefile [\#271](https://github.com/JFreegman/toxic/pull/271) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Version: fix revision calculation [\#269](https://github.com/JFreegman/toxic/pull/269) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- fix doc building, dataencrypt api and minor ui tweak [\#267](https://github.com/JFreegman/toxic/pull/267) ([louipc](https://github.com/louipc))
 | 
			
		||||
- Change action messages indicator [\#264](https://github.com/JFreegman/toxic/pull/264) ([zetok](https://github.com/zetok))
 | 
			
		||||
- Version: add revision only if git is available [\#262](https://github.com/JFreegman/toxic/pull/262) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
 | 
			
		||||
## [v0.5.2](https://github.com/JFreegman/toxic/tree/v0.5.2) (2014-09-29)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.1...v0.5.2)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Failed to read log file [\#254](https://github.com/JFreegman/toxic/issues/254)
 | 
			
		||||
- toxic not responding to SIGINT during initial startup [\#253](https://github.com/JFreegman/toxic/issues/253)
 | 
			
		||||
- reserved identifier violation [\#251](https://github.com/JFreegman/toxic/issues/251)
 | 
			
		||||
- Fix signal handler [\#250](https://github.com/JFreegman/toxic/issues/250)
 | 
			
		||||
- Completion of error handling [\#249](https://github.com/JFreegman/toxic/issues/249)
 | 
			
		||||
- How to decline file sends? [\#247](https://github.com/JFreegman/toxic/issues/247)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Fix "error: unknown type name 'off\_t'" [\#255](https://github.com/JFreegman/toxic/pull/255) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- rm -rf -\> rm -f [\#252](https://github.com/JFreegman/toxic/pull/252) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Update screenshot [\#246](https://github.com/JFreegman/toxic/pull/246) ([urras](https://github.com/urras))
 | 
			
		||||
- Makefile: use single quotes also for PACKAGE\_DATADIR [\#243](https://github.com/JFreegman/toxic/pull/243) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
 | 
			
		||||
## [v0.5.1](https://github.com/JFreegman/toxic/tree/v0.5.1) (2014-09-19)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.0...v0.5.1)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Support for faux offline messaging [\#233](https://github.com/JFreegman/toxic/issues/233)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Usage help: add missing comma [\#242](https://github.com/JFreegman/toxic/pull/242) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Fix some 'clang --analyze' warnings [\#240](https://github.com/JFreegman/toxic/pull/240) ([s3erios](https://github.com/s3erios))
 | 
			
		||||
- Addition to Tox/toxic\#235 [\#238](https://github.com/JFreegman/toxic/pull/238) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Some code simplification [\#236](https://github.com/JFreegman/toxic/pull/236) ([s3erios](https://github.com/s3erios))
 | 
			
		||||
- Add X11 option [\#235](https://github.com/JFreegman/toxic/pull/235) ([s3erios](https://github.com/s3erios))
 | 
			
		||||
 | 
			
		||||
## [v0.5.0](https://github.com/JFreegman/toxic/tree/v0.5.0) (2014-09-01)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.7...v0.5.0)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- 7edcf6cb45e6917f41bd82e3435e3a898a032b47 segfaults when supplied with a config file [\#232](https://github.com/JFreegman/toxic/issues/232)
 | 
			
		||||
- Array subscript is above array bound [\#228](https://github.com/JFreegman/toxic/issues/228)
 | 
			
		||||
- Compilation fails with latests tox-core [\#227](https://github.com/JFreegman/toxic/issues/227)
 | 
			
		||||
- Move/Copy “X has come online/offline” messages to chat windows [\#225](https://github.com/JFreegman/toxic/issues/225)
 | 
			
		||||
- MANDIR set for Linux [\#222](https://github.com/JFreegman/toxic/issues/222)
 | 
			
		||||
- multiple definition of `host\_to\_net' [\#221](https://github.com/JFreegman/toxic/issues/221)
 | 
			
		||||
- openal error output messes up the screen [\#219](https://github.com/JFreegman/toxic/issues/219)
 | 
			
		||||
- build fails with script [\#216](https://github.com/JFreegman/toxic/issues/216)
 | 
			
		||||
- UTF-8 Support [\#171](https://github.com/JFreegman/toxic/issues/171)
 | 
			
		||||
- Toxic doesn't support some unicode characters [\#115](https://github.com/JFreegman/toxic/issues/115)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Cosmetic fixes [\#234](https://github.com/JFreegman/toxic/pull/234) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Reworked manpage build system [\#231](https://github.com/JFreegman/toxic/pull/231) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Manpage [\#230](https://github.com/JFreegman/toxic/pull/230) ([louipc](https://github.com/louipc))
 | 
			
		||||
- toxic.conf.example: better formatting [\#229](https://github.com/JFreegman/toxic/pull/229) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Fix Tox/toxic\#222 and reorganize cfg dir [\#226](https://github.com/JFreegman/toxic/pull/226) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Add debug flag and update man page. [\#223](https://github.com/JFreegman/toxic/pull/223) ([louipc](https://github.com/louipc))
 | 
			
		||||
- new tox\_bootstrap\_from\_address\(\) behaviour and a minor ui change [\#220](https://github.com/JFreegman/toxic/pull/220) ([louipc](https://github.com/louipc))
 | 
			
		||||
- toxic.conf.5: Remove default config from man page [\#218](https://github.com/JFreegman/toxic/pull/218) ([louipc](https://github.com/louipc))
 | 
			
		||||
 | 
			
		||||
## [v0.4.7](https://github.com/JFreegman/toxic/tree/v0.4.7) (2014-08-05)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.6...v0.4.7)
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- Segfault on openSUSE 13.1 [\#106](https://github.com/JFreegman/toxic/issues/106)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- cancel callback doesn't work [\#214](https://github.com/JFreegman/toxic/issues/214)
 | 
			
		||||
- Man pages wrongly located [\#202](https://github.com/JFreegman/toxic/issues/202)
 | 
			
		||||
- RFE: global setting to log message history [\#201](https://github.com/JFreegman/toxic/issues/201)
 | 
			
		||||
- Small typo in menu item [\#197](https://github.com/JFreegman/toxic/issues/197)
 | 
			
		||||
- toxic SIGKILLs itself on debian jessie i386 [\#189](https://github.com/JFreegman/toxic/issues/189)
 | 
			
		||||
- Toxic segfaults [\#144](https://github.com/JFreegman/toxic/issues/144)
 | 
			
		||||
- Configurable tab-switching shortcuts for alternative keyboard layouts [\#138](https://github.com/JFreegman/toxic/issues/138)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Fix ringing sounds [\#215](https://github.com/JFreegman/toxic/pull/215) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Add missing includes [\#213](https://github.com/JFreegman/toxic/pull/213) ([doughdemon](https://github.com/doughdemon))
 | 
			
		||||
- Fix bug [\#211](https://github.com/JFreegman/toxic/pull/211) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Fresh pack of backdoors [\#210](https://github.com/JFreegman/toxic/pull/210) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Makefile: refactoring and adding desktop notifications support [\#208](https://github.com/JFreegman/toxic/pull/208) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Update toxic.conf manpage [\#207](https://github.com/JFreegman/toxic/pull/207) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Configurable keybindings [\#206](https://github.com/JFreegman/toxic/pull/206) ([gracchus163](https://github.com/gracchus163))
 | 
			
		||||
- Lowered volume of sounds [\#205](https://github.com/JFreegman/toxic/pull/205) ([loadedice](https://github.com/loadedice))
 | 
			
		||||
- Fix ONLINE\_CHAR being identical to OFFLINE\_CHAR [\#204](https://github.com/JFreegman/toxic/pull/204) ([zetok](https://github.com/zetok))
 | 
			
		||||
- Put man pages in right place by default \(\#202\) [\#203](https://github.com/JFreegman/toxic/pull/203) ([zetok](https://github.com/zetok))
 | 
			
		||||
- Popup notifications & core adjustments [\#200](https://github.com/JFreegman/toxic/pull/200) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Fixed sounds not playing [\#199](https://github.com/JFreegman/toxic/pull/199) ([ghost](https://github.com/ghost))
 | 
			
		||||
- README.md: add precompiled binaries [\#198](https://github.com/JFreegman/toxic/pull/198) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
 | 
			
		||||
## [v0.4.6](https://github.com/JFreegman/toxic/tree/v0.4.6) (2014-07-23)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.5...v0.4.6)
 | 
			
		||||
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- "Officially Deprecated" build for 32-bit? [\#192](https://github.com/JFreegman/toxic/issues/192)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Please create me a wiki account [\#196](https://github.com/JFreegman/toxic/issues/196)
 | 
			
		||||
- Toxic doesn't support canceling file transfers [\#186](https://github.com/JFreegman/toxic/issues/186)
 | 
			
		||||
- hashes of binaries? [\#185](https://github.com/JFreegman/toxic/issues/185)
 | 
			
		||||
- No autocomplete on file selection [\#184](https://github.com/JFreegman/toxic/issues/184)
 | 
			
		||||
- valgrind [\#178](https://github.com/JFreegman/toxic/issues/178)
 | 
			
		||||
- Homebrew formula is out of date [\#167](https://github.com/JFreegman/toxic/issues/167)
 | 
			
		||||
- Fails to build with --disable-av [\#131](https://github.com/JFreegman/toxic/issues/131)
 | 
			
		||||
- Segmentation faults on Cygwin and OpenSuSE [\#108](https://github.com/JFreegman/toxic/issues/108)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Add hardcoded path for sound notifications [\#195](https://github.com/JFreegman/toxic/pull/195) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Makefile: little refactoring [\#193](https://github.com/JFreegman/toxic/pull/193) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Fixed some build errors [\#190](https://github.com/JFreegman/toxic/pull/190) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Makefile fix [\#188](https://github.com/JFreegman/toxic/pull/188) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Added sound notifications, libconfig support, and more... [\#187](https://github.com/JFreegman/toxic/pull/187) ([ghost](https://github.com/ghost))
 | 
			
		||||
 | 
			
		||||
## [v0.4.5](https://github.com/JFreegman/toxic/tree/v0.4.5) (2014-07-14)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.1...v0.4.5)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- building on freebsd [\#177](https://github.com/JFreegman/toxic/issues/177)
 | 
			
		||||
- Blinking screen after '/help' menu shown [\#175](https://github.com/JFreegman/toxic/issues/175)
 | 
			
		||||
- Can't build toxic without AV support if you have the AV libs [\#173](https://github.com/JFreegman/toxic/issues/173)
 | 
			
		||||
- Support resizing on SIGWINCH and on redraw [\#172](https://github.com/JFreegman/toxic/issues/172)
 | 
			
		||||
- Broken backspace [\#163](https://github.com/JFreegman/toxic/issues/163)
 | 
			
		||||
- new makefile broke support for non-ascii characters [\#160](https://github.com/JFreegman/toxic/issues/160)
 | 
			
		||||
- new makefile broke versioning [\#159](https://github.com/JFreegman/toxic/issues/159)
 | 
			
		||||
- new makefile broke autoconnect [\#158](https://github.com/JFreegman/toxic/issues/158)
 | 
			
		||||
- Compilation error [\#143](https://github.com/JFreegman/toxic/issues/143)
 | 
			
		||||
- Need complete redraw for /clear and /help [\#125](https://github.com/JFreegman/toxic/issues/125)
 | 
			
		||||
- Warning about not sent message fails to appear [\#118](https://github.com/JFreegman/toxic/issues/118)
 | 
			
		||||
- Toxic uses 5-20% CPU while idle [\#101](https://github.com/JFreegman/toxic/issues/101)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Fixes problems with upstream changes [\#183](https://github.com/JFreegman/toxic/pull/183) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Use long int instead uint64\_t [\#181](https://github.com/JFreegman/toxic/pull/181) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Forgot about help [\#180](https://github.com/JFreegman/toxic/pull/180) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Add option to disable audio support [\#179](https://github.com/JFreegman/toxic/pull/179) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Make closing window end call [\#174](https://github.com/JFreegman/toxic/pull/174) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Manpage fix [\#170](https://github.com/JFreegman/toxic/pull/170) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Add help target and toxic.conf manpage [\#169](https://github.com/JFreegman/toxic/pull/169) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Fixed setting buffer to half of the size [\#165](https://github.com/JFreegman/toxic/pull/165) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Add manpage [\#164](https://github.com/JFreegman/toxic/pull/164) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Try to fix autoconnect [\#161](https://github.com/JFreegman/toxic/pull/161) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Wide characters support [\#157](https://github.com/JFreegman/toxic/pull/157) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Polishing README.md [\#155](https://github.com/JFreegman/toxic/pull/155) ([theGeekPirate](https://github.com/theGeekPirate))
 | 
			
		||||
- README.md: add build status [\#153](https://github.com/JFreegman/toxic/pull/153) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Update readme instructions [\#152](https://github.com/JFreegman/toxic/pull/152) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Forgot to set index in some callbacks [\#151](https://github.com/JFreegman/toxic/pull/151) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Reverse call\_idx and enable running call when devices fail to load [\#150](https://github.com/JFreegman/toxic/pull/150) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Remove autotools dependency [\#149](https://github.com/JFreegman/toxic/pull/149) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Cast localtime [\#147](https://github.com/JFreegman/toxic/pull/147) ([Ansa89](https://github.com/Ansa89))
 | 
			
		||||
- Changed code a bit and added new features [\#146](https://github.com/JFreegman/toxic/pull/146) ([ghost](https://github.com/ghost))
 | 
			
		||||
 | 
			
		||||
## [0.4.1](https://github.com/JFreegman/toxic/tree/0.4.1) (2014-06-19)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.0...0.4.1)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Toxic does not complie with audio on OSX [\#140](https://github.com/JFreegman/toxic/issues/140)
 | 
			
		||||
- compiling error [\#139](https://github.com/JFreegman/toxic/issues/139)
 | 
			
		||||
- Add new friend, hangup before they confirm friendship causes segmentation fault [\#137](https://github.com/JFreegman/toxic/issues/137)
 | 
			
		||||
- build fail [\#124](https://github.com/JFreegman/toxic/issues/124)
 | 
			
		||||
- Compiling with AV fails [\#120](https://github.com/JFreegman/toxic/issues/120)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Add libresolv [\#142](https://github.com/JFreegman/toxic/pull/142) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- Search for OpenAL framework on OSX [\#141](https://github.com/JFreegman/toxic/pull/141) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
 | 
			
		||||
## [0.4.0](https://github.com/JFreegman/toxic/tree/0.4.0) (2014-06-01)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0.1...0.4.0)
 | 
			
		||||
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- Are there any keybinding to scroll chat/groupchat view up and down? [\#74](https://github.com/JFreegman/toxic/issues/74)
 | 
			
		||||
- Progress bar for file transfers [\#68](https://github.com/JFreegman/toxic/issues/68)
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- Toxic does not support certain characters [\#84](https://github.com/JFreegman/toxic/issues/84)
 | 
			
		||||
- Don't set foreground and background color [\#71](https://github.com/JFreegman/toxic/issues/71)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Toxic misbehaves and is killed [\#136](https://github.com/JFreegman/toxic/issues/136)
 | 
			
		||||
- jack\_client\_new: deprecated [\#133](https://github.com/JFreegman/toxic/issues/133)
 | 
			
		||||
- build error on os x 10.9 [\#129](https://github.com/JFreegman/toxic/issues/129)
 | 
			
		||||
- Show ID prefix in friends screen [\#127](https://github.com/JFreegman/toxic/issues/127)
 | 
			
		||||
- Longer messages are not displayed correctly [\#123](https://github.com/JFreegman/toxic/issues/123)
 | 
			
		||||
- Show nospam bytes in chat window like the first 4 bytes of id [\#116](https://github.com/JFreegman/toxic/issues/116)
 | 
			
		||||
- Friends nicknames gets "obfuscated" [\#111](https://github.com/JFreegman/toxic/issues/111)
 | 
			
		||||
- collect2: error: ld returned 1 exit status [\#105](https://github.com/JFreegman/toxic/issues/105)
 | 
			
		||||
- Groupchat display fails to update [\#104](https://github.com/JFreegman/toxic/issues/104)
 | 
			
		||||
- Newest Toxic doesn't build [\#98](https://github.com/JFreegman/toxic/issues/98)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Update README.md [\#134](https://github.com/JFreegman/toxic/pull/134) ([zetok](https://github.com/zetok))
 | 
			
		||||
- Update audio\_call.c [\#132](https://github.com/JFreegman/toxic/pull/132) ([Impyy](https://github.com/Impyy))
 | 
			
		||||
- Not done yet. [\#130](https://github.com/JFreegman/toxic/pull/130) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Fix file sender null terminator. [\#128](https://github.com/JFreegman/toxic/pull/128) ([aitjcize](https://github.com/aitjcize))
 | 
			
		||||
- Drop typedef redeclarations [\#122](https://github.com/JFreegman/toxic/pull/122) ([czarkoff](https://github.com/czarkoff))
 | 
			
		||||
- Include "pthread.h" [\#121](https://github.com/JFreegman/toxic/pull/121) ([czarkoff](https://github.com/czarkoff))
 | 
			
		||||
- Wow [\#119](https://github.com/JFreegman/toxic/pull/119) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Use default terminal fg/bg colors when we can. [\#117](https://github.com/JFreegman/toxic/pull/117) ([ooesili](https://github.com/ooesili))
 | 
			
		||||
- Fixed support for wide characters [\#113](https://github.com/JFreegman/toxic/pull/113) ([graboy](https://github.com/graboy))
 | 
			
		||||
- Mention av [\#110](https://github.com/JFreegman/toxic/pull/110) ([stqism](https://github.com/stqism))
 | 
			
		||||
- allow history scrolling [\#109](https://github.com/JFreegman/toxic/pull/109) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Only those who appreciate small things [\#107](https://github.com/JFreegman/toxic/pull/107) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Open devices when call starts instead of keeping them opened all the time [\#103](https://github.com/JFreegman/toxic/pull/103) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Incorrectly handled error check for widechars [\#102](https://github.com/JFreegman/toxic/pull/102) ([graboy](https://github.com/graboy))
 | 
			
		||||
- Fix toxic build when toxav is not available [\#100](https://github.com/JFreegman/toxic/pull/100) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- Add checks for pthreads to the build system [\#99](https://github.com/JFreegman/toxic/pull/99) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- Fixes and stuff... [\#97](https://github.com/JFreegman/toxic/pull/97) ([ghost](https://github.com/ghost))
 | 
			
		||||
 | 
			
		||||
## [0.3.0.1](https://github.com/JFreegman/toxic/tree/0.3.0.1) (2014-03-12)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0...0.3.0.1)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- SPELLING IS FOR FOOLS [\#94](https://github.com/JFreegman/toxic/pull/94) ([lehitoskin](https://github.com/lehitoskin))
 | 
			
		||||
 | 
			
		||||
## [0.3.0](https://github.com/JFreegman/toxic/tree/0.3.0) (2014-03-12)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.7...0.3.0)
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- SIGSEVG upon friend hanging up [\#89](https://github.com/JFreegman/toxic/issues/89)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Fixed segfault [\#92](https://github.com/JFreegman/toxic/pull/92) ([ghost](https://github.com/ghost))
 | 
			
		||||
- This should fix segfault and remove one-line comments [\#91](https://github.com/JFreegman/toxic/pull/91) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Fixed another clang issue with bools that broek file sending. [\#90](https://github.com/JFreegman/toxic/pull/90) ([Jman012](https://github.com/Jman012))
 | 
			
		||||
- Toxic audio support [\#88](https://github.com/JFreegman/toxic/pull/88) ([ghost](https://github.com/ghost))
 | 
			
		||||
- Fixed clang error, disabling the execute module. [\#87](https://github.com/JFreegman/toxic/pull/87) ([Jman012](https://github.com/Jman012))
 | 
			
		||||
- Issue \#84 fixed [\#86](https://github.com/JFreegman/toxic/pull/86) ([thevar1able](https://github.com/thevar1able))
 | 
			
		||||
- Fixing fall-back from IPv6 to IPv4 [\#85](https://github.com/JFreegman/toxic/pull/85) ([micrictor](https://github.com/micrictor))
 | 
			
		||||
 | 
			
		||||
## [0.2.7](https://github.com/JFreegman/toxic/tree/0.2.7) (2014-03-01)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6.1...0.2.7)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Toxic segfault when window is closed [\#81](https://github.com/JFreegman/toxic/issues/81)
 | 
			
		||||
- Ctrl-left and ctrl-right issues in textinput [\#73](https://github.com/JFreegman/toxic/issues/73)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- down arrow returns empty string if at end of history [\#82](https://github.com/JFreegman/toxic/pull/82) ([kl4ng](https://github.com/kl4ng))
 | 
			
		||||
- Fallback to loading /usr/share/toxic/DHTservers. [\#80](https://github.com/JFreegman/toxic/pull/80) ([viric](https://github.com/viric))
 | 
			
		||||
 | 
			
		||||
## [0.2.6.1](https://github.com/JFreegman/toxic/tree/0.2.6.1) (2014-02-23)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6...0.2.6.1)
 | 
			
		||||
 | 
			
		||||
## [0.2.6](https://github.com/JFreegman/toxic/tree/0.2.6) (2014-02-23)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.5...0.2.6)
 | 
			
		||||
 | 
			
		||||
## [0.2.5](https://github.com/JFreegman/toxic/tree/0.2.5) (2014-02-22)
 | 
			
		||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/prealpha_win32_r8...0.2.5)
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- Back space leaves ć character [\#44](https://github.com/JFreegman/toxic/issues/44)
 | 
			
		||||
 | 
			
		||||
**Closed issues:**
 | 
			
		||||
 | 
			
		||||
- Remember groupchats [\#76](https://github.com/JFreegman/toxic/issues/76)
 | 
			
		||||
- Segfault [\#75](https://github.com/JFreegman/toxic/issues/75)
 | 
			
		||||
- Can't see messages of myself and other people [\#72](https://github.com/JFreegman/toxic/issues/72)
 | 
			
		||||
- binary blob in source [\#66](https://github.com/JFreegman/toxic/issues/66)
 | 
			
		||||
- symbol lookup error [\#54](https://github.com/JFreegman/toxic/issues/54)
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- ncurses libraries README note  [\#78](https://github.com/JFreegman/toxic/pull/78) ([kl4ng](https://github.com/kl4ng))
 | 
			
		||||
- umask such that stored files are u+rw only [\#77](https://github.com/JFreegman/toxic/pull/77) ([alevy](https://github.com/alevy))
 | 
			
		||||
- Fix groupchat cursor movement. [\#63](https://github.com/JFreegman/toxic/pull/63) ([aitjcize](https://github.com/aitjcize))
 | 
			
		||||
- Fix wchar cursor movement. [\#62](https://github.com/JFreegman/toxic/pull/62) ([aitjcize](https://github.com/aitjcize))
 | 
			
		||||
- api update [\#61](https://github.com/JFreegman/toxic/pull/61) ([naxuroqa](https://github.com/naxuroqa))
 | 
			
		||||
- Add option to switch off ipv6. [\#60](https://github.com/JFreegman/toxic/pull/60) ([aitjcize](https://github.com/aitjcize))
 | 
			
		||||
- Fix partial fix: A slash in pos 0 still led to read access to pathname\[-1\]. [\#59](https://github.com/JFreegman/toxic/pull/59) ([FullName](https://github.com/FullName))
 | 
			
		||||
- Fix corresponding API name changes in toxcore. [\#58](https://github.com/JFreegman/toxic/pull/58) ([aitjcize](https://github.com/aitjcize))
 | 
			
		||||
- Fix API ret code changes of ToxCore [\#57](https://github.com/JFreegman/toxic/pull/57) ([aitjcize](https://github.com/aitjcize))
 | 
			
		||||
 | 
			
		||||
## [prealpha_win32_r8](https://github.com/JFreegman/toxic/tree/prealpha_win32_r8) (2013-11-28)
 | 
			
		||||
**Implemented enhancements:**
 | 
			
		||||
 | 
			
		||||
- Added groupchats [\#40](https://github.com/JFreegman/toxic/pull/40) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Adapted to ipv6-enabled tox [\#38](https://github.com/JFreegman/toxic/pull/38) ([FullName](https://github.com/FullName))
 | 
			
		||||
- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName))
 | 
			
		||||
- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- if tox\_new\(\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName))
 | 
			
		||||
- truncate friends' notes if they're too long [\#30](https://github.com/JFreegman/toxic/pull/30) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Added status bar to prompt, made it beep/blink on friend request, and bug fixes [\#29](https://github.com/JFreegman/toxic/pull/29) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Added a statusbar to chat windows and removed spammy messages [\#28](https://github.com/JFreegman/toxic/pull/28) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- implemented status and connectionstatus callbacks [\#26](https://github.com/JFreegman/toxic/pull/26) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Show offline friends names and some cosmetic changes [\#25](https://github.com/JFreegman/toxic/pull/25) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- refactor command argument parsing [\#23](https://github.com/JFreegman/toxic/pull/23) ([lukechampine](https://github.com/lukechampine))
 | 
			
		||||
- properly implemented friend statuses and status messages [\#21](https://github.com/JFreegman/toxic/pull/21) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- implemented friend deletion [\#15](https://github.com/JFreegman/toxic/pull/15) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- Add check for setlocale\(\) [\#10](https://github.com/JFreegman/toxic/pull/10) ([manuel-arguelles](https://github.com/manuel-arguelles))
 | 
			
		||||
- Update build system [\#7](https://github.com/JFreegman/toxic/pull/7) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- Added travis integration [\#6](https://github.com/JFreegman/toxic/pull/6) ([stqism](https://github.com/stqism))
 | 
			
		||||
- Use new public api [\#5](https://github.com/JFreegman/toxic/pull/5) ([fhahn](https://github.com/fhahn))
 | 
			
		||||
- Add widechar checks [\#2](https://github.com/JFreegman/toxic/pull/2) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
 | 
			
		||||
**Fixed bugs:**
 | 
			
		||||
 | 
			
		||||
- Let windows.c actually get the tox \*m. [\#41](https://github.com/JFreegman/toxic/pull/41) ([Jman012](https://github.com/Jman012))
 | 
			
		||||
- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName))
 | 
			
		||||
- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Merged pr6 [\#34](https://github.com/JFreegman/toxic/pull/34) ([stqism](https://github.com/stqism))
 | 
			
		||||
- made error handling more consistent and added exit function [\#33](https://github.com/JFreegman/toxic/pull/33) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- if tox\\_new\\(\\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName))
 | 
			
		||||
- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- fix buffer overflows and format issues [\#20](https://github.com/JFreegman/toxic/pull/20) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Fix blocking while waiting for key [\#17](https://github.com/JFreegman/toxic/pull/17) ([manuel-arguelles](https://github.com/manuel-arguelles))
 | 
			
		||||
- fixed "free\(\): invalid pointer" when XDG\_CONFIG\_HOME is set [\#16](https://github.com/JFreegman/toxic/pull/16) ([gs93](https://github.com/gs93))
 | 
			
		||||
- Make sure toxic compiles on MinGW/Win32 again [\#14](https://github.com/JFreegman/toxic/pull/14) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- Fix for the "bad character" when doing backspace in chat window [\#12](https://github.com/JFreegman/toxic/pull/12) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- Fix configure script for ncurses without ncursesw [\#9](https://github.com/JFreegman/toxic/pull/9) ([manuel-arguelles](https://github.com/manuel-arguelles))
 | 
			
		||||
- Fix configure script for mingw32 [\#8](https://github.com/JFreegman/toxic/pull/8) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
- warning: comparison of integers of different signs: 'int' and 'unsigned long' [\#3](https://github.com/JFreegman/toxic/pull/3) ([1100110](https://github.com/1100110))
 | 
			
		||||
 | 
			
		||||
**Merged pull requests:**
 | 
			
		||||
 | 
			
		||||
- Make sure friend message is null-terminated else generate garbate on screen [\#56](https://github.com/JFreegman/toxic/pull/56) ([aitjcize](https://github.com/aitjcize))
 | 
			
		||||
- Fix trailing slashes which leads to segfault. [\#55](https://github.com/JFreegman/toxic/pull/55) ([aitjcize](https://github.com/aitjcize))
 | 
			
		||||
- fix cflags [\#53](https://github.com/JFreegman/toxic/pull/53) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Fix 93ab16c [\#52](https://github.com/JFreegman/toxic/pull/52) ([urras](https://github.com/urras))
 | 
			
		||||
- Offer solution for "error while loading shared libraries: libtoxcore.so.... [\#51](https://github.com/JFreegman/toxic/pull/51) ([urras](https://github.com/urras))
 | 
			
		||||
- Implemented file transfers [\#50](https://github.com/JFreegman/toxic/pull/50) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Fix check for toxcore by linking sodium in the correct place [\#47](https://github.com/JFreegman/toxic/pull/47) ([devurandom](https://github.com/devurandom))
 | 
			
		||||
- Changed order of servers [\#46](https://github.com/JFreegman/toxic/pull/46) ([grimd34th](https://github.com/grimd34th))
 | 
			
		||||
- set friendnames properly and some fixes [\#45](https://github.com/JFreegman/toxic/pull/45) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- moved misc helper functions to separate file and removed redundant includes [\#43](https://github.com/JFreegman/toxic/pull/43) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Refactored prompt command parser to work with all window types and moved command stuff to separate files [\#42](https://github.com/JFreegman/toxic/pull/42) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Ipv6.init connection [\#39](https://github.com/JFreegman/toxic/pull/39) ([FullName](https://github.com/FullName))
 | 
			
		||||
- Remove DHT window [\#13](https://github.com/JFreegman/toxic/pull/13) ([JFreegman](https://github.com/JFreegman))
 | 
			
		||||
- Update README.md [\#4](https://github.com/JFreegman/toxic/pull/4) ([notadecent](https://github.com/notadecent))
 | 
			
		||||
- Toxic standalone [\#1](https://github.com/JFreegman/toxic/pull/1) ([jin-eld](https://github.com/jin-eld))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
 | 
			
		||||
							
								
								
									
										70
									
								
								INSTALL.md
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								INSTALL.md
									
									
									
									
									
								
							@@ -1,70 +1,70 @@
 | 
			
		||||
# Installation
 | 
			
		||||
* [Dependencies](#deps)
 | 
			
		||||
  * [OS X Notes](#deps_osx)
 | 
			
		||||
* [Dependencies](#dependencies)
 | 
			
		||||
  * [OS X Notes](#os-x-notes)
 | 
			
		||||
* [Compiling](#compiling)
 | 
			
		||||
  * [Documentation](#docs)
 | 
			
		||||
  * [Documentation](#documentation)
 | 
			
		||||
* [Notes](#notes)
 | 
			
		||||
  * [Compilation variables](#comp_vars)
 | 
			
		||||
  * [Packaging](#packaging)
 | 
			
		||||
  * [Compilation variables](#compilation-variables)
 | 
			
		||||
  * [Environment variables](#environment-variables)
 | 
			
		||||
 | 
			
		||||
<a name="deps" />
 | 
			
		||||
## Dependencies
 | 
			
		||||
| Name                                                 | Needed by                  | Debian package      |
 | 
			
		||||
|------------------------------------------------------|----------------------------|---------------------|
 | 
			
		||||
| [Tox Core](https://github.com/irungentoo/toxcore)    | BASE                       | *None*              |
 | 
			
		||||
| [Tox Core](https://github.com/toktok/c-toxcore)      | BASE                       | *None*              |
 | 
			
		||||
| [NCurses](https://www.gnu.org/software/ncurses)      | BASE                       | libncursesw5-dev    |
 | 
			
		||||
| [LibConfig](http://www.hyperrealm.com/libconfig)     | BASE                       | libconfig-dev       |
 | 
			
		||||
| [GNUmake](https://www.gnu.org/software/make)         | BASE                       | make                |
 | 
			
		||||
| [libcurl](http://curl.haxx.se/)                      | BASE                       | libcurl4-openssl-dev|
 | 
			
		||||
| [libqrencode](https://fukuchi.org/works/qrencode/)   | BASE                       | libqrencode-dev     |
 | 
			
		||||
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO                      | *None*              |
 | 
			
		||||
| [libqrencode](https://fukuchi.org/works/qrencode/)   | QRCODE                     | libqrencode-dev     |
 | 
			
		||||
| [OpenAL](http://openal.org)                          | AUDIO, SOUND NOTIFICATIONS | libopenal-dev       |
 | 
			
		||||
| [OpenALUT](http://openal.org)                        | SOUND NOTIFICATIONS        | libalut-dev         |
 | 
			
		||||
| [LibNotify](https://developer.gnome.org/libnotify)   | DESKTOP NOTIFICATIONS      | libnotify-dev       |
 | 
			
		||||
| [Python 3](http://www.python.org/)                   | PYTHON                     | python3-dev         |
 | 
			
		||||
| [AsciiDoc](http://asciidoc.org/index.html)           | DOCUMENTATION<sup>1</sup>  | asciidoc            |
 | 
			
		||||
<sup>1</sup>: see [Documentation](#docs)
 | 
			
		||||
 | 
			
		||||
<a name="deps_osx" />
 | 
			
		||||
<sup>1</sup>: see [Documentation](#documentation)
 | 
			
		||||
 | 
			
		||||
#### OS X Notes
 | 
			
		||||
Using [Homebrew](http://brew.sh):
 | 
			
		||||
```
 | 
			
		||||
brew install openal-soft freealut libconfig
 | 
			
		||||
brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
 | 
			
		||||
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb
 | 
			
		||||
brew install curl qrencode openal-soft freealut libconfig libpng
 | 
			
		||||
brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
 | 
			
		||||
brew install libnotify
 | 
			
		||||
export PKG_CONFIG_PATH=/usr/local/opt/openal-soft/lib/pkgconfig
 | 
			
		||||
make
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can omit `libnotify` if you intend to build without desktop notifications enabled.
 | 
			
		||||
 | 
			
		||||
<a name="Compiling">
 | 
			
		||||
## Compiling
 | 
			
		||||
```
 | 
			
		||||
make PREFIX="/where/to/install"
 | 
			
		||||
sudo make install PREFIX="/where/to/install"
 | 
			
		||||
make
 | 
			
		||||
sudo make install
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<a name="docs" />
 | 
			
		||||
#### Documentation
 | 
			
		||||
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
 | 
			
		||||
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
 | 
			
		||||
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
 | 
			
		||||
**Note for developers**: asciidoc files and generated manpages will need to be committed together.<br />
 | 
			
		||||
**Note for everyone**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
 | 
			
		||||
 | 
			
		||||
<a name="notes" />
 | 
			
		||||
## Notes
 | 
			
		||||
 | 
			
		||||
<a name="comp_vars" />
 | 
			
		||||
#### Compilation variables
 | 
			
		||||
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
 | 
			
		||||
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
 | 
			
		||||
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
 | 
			
		||||
  * `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking)
 | 
			
		||||
  * `DISABLE_AV=1` → build toxic without audio call support
 | 
			
		||||
  * `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
 | 
			
		||||
  * `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
 | 
			
		||||
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables
 | 
			
		||||
* Default compile options can be overridden by using special variables:
 | 
			
		||||
  * `DISABLE_X11=1` → Disable X11 support (needed for focus tracking)
 | 
			
		||||
  * `DISABLE_AV=1` → Disable audio call support
 | 
			
		||||
  * `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support
 | 
			
		||||
  * `DISABLE_QRCODE` → Disable QR exporting support
 | 
			
		||||
  * `DISABLE_QRPNG` → Disable support for exporting QR as PNG
 | 
			
		||||
  * `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
 | 
			
		||||
  * `ENABLE_PYTHON=1` → Build toxic with Python scripting support
 | 
			
		||||
  * `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations
 | 
			
		||||
  * `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled
 | 
			
		||||
 | 
			
		||||
<a name="packaging" />
 | 
			
		||||
#### Packaging
 | 
			
		||||
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
 | 
			
		||||
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
 | 
			
		||||
  * `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
 | 
			
		||||
  * `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
 | 
			
		||||
* `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
 | 
			
		||||
 | 
			
		||||
#### Environment variables
 | 
			
		||||
* You can use the `CFLAGS` and `LDFLAGS` environment variables to add specific flags to the Makefile
 | 
			
		||||
* The `PREFIX` environment variable specifies a base install directory for binaries and data files. This is interchangeable with the `DESTDIR` variable, and is generally used by systems that have the `PREFIX` environment variable set by default.<br />
 | 
			
		||||
**Note**: `sudo` does not preserve user environment variables by default on some systems. See the `sudoers` manual for more information.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										53
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								Makefile
									
									
									
									
									
								
							@@ -3,39 +3,52 @@ CFG_DIR = $(BASE_DIR)/cfg
 | 
			
		||||
 | 
			
		||||
-include $(CFG_DIR)/global_vars.mk
 | 
			
		||||
 | 
			
		||||
LIBS = libtoxcore ncursesw libconfig libqrencode
 | 
			
		||||
LIBS = toxcore ncursesw libconfig libcurl
 | 
			
		||||
 | 
			
		||||
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all
 | 
			
		||||
CFLAGS ?= -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wno-missing-braces
 | 
			
		||||
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
 | 
			
		||||
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
 | 
			
		||||
CFLAGS += $(USER_CFLAGS)
 | 
			
		||||
LDFLAGS = $(USER_LDFLAGS)
 | 
			
		||||
CFLAGS += ${USER_CFLAGS}
 | 
			
		||||
LDFLAGS ?=
 | 
			
		||||
LDFLAGS += ${USER_LDFLAGS}
 | 
			
		||||
 | 
			
		||||
OBJ = chat.o chat_commands.o configdir.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 name_lookup.o qr_code.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 conference_commands.o conference.o help.o input.o
 | 
			
		||||
OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
 | 
			
		||||
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
 | 
			
		||||
 | 
			
		||||
# Check if debug build is enabled
 | 
			
		||||
RELEASE := $(shell if [ -z "$(ENABLE_RELEASE)" ] || [ "$(ENABLE_RELEASE)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
 | 
			
		||||
ifneq ($(RELEASE), enabled)
 | 
			
		||||
	CFLAGS += -O0 -g -DDEBUG
 | 
			
		||||
	LDFLAGS += -O0
 | 
			
		||||
else
 | 
			
		||||
	CFLAGS += -O2 -flto
 | 
			
		||||
	LDFLAGS += -O2 -flto
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if LLVM Address Sanitizer is enabled
 | 
			
		||||
ENABLE_ASAN := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
 | 
			
		||||
ifneq ($(ENABLE_ASAN), disabled)
 | 
			
		||||
	CFLAGS += -fsanitize=address -fno-omit-frame-pointer
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check on wich system we are running
 | 
			
		||||
UNAME_S = $(shell uname -s)
 | 
			
		||||
ifeq ($(UNAME_S), Linux)
 | 
			
		||||
    -include $(CFG_DIR)/systems/Linux.mk
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(UNAME_S), FreeBSD)
 | 
			
		||||
    -include $(CFG_DIR)/systems/FreeBSD.mk
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(UNAME_S), DragonFly)
 | 
			
		||||
    -include $(CFG_DIR)/systems/FreeBSD.mk
 | 
			
		||||
LDFLAGS += -ldl -lrt
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(UNAME_S), OpenBSD)
 | 
			
		||||
    -include $(CFG_DIR)/systems/FreeBSD.mk
 | 
			
		||||
LIBS := $(filter-out ncursesw, $(LIBS))
 | 
			
		||||
LDFLAGS += -lncursesw
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(UNAME_S), NetBSD)
 | 
			
		||||
LIBS := $(filter-out ncursesw, $(LIBS))
 | 
			
		||||
LDFLAGS += -lncursesw
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(UNAME_S), Darwin)
 | 
			
		||||
    -include $(CFG_DIR)/systems/Darwin.mk
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(UNAME_S), Solaris)
 | 
			
		||||
    -include $(CFG_DIR)/systems/Solaris.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check on which platform we are running
 | 
			
		||||
UNAME_M = $(shell uname -m)
 | 
			
		||||
@@ -72,7 +85,7 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
 | 
			
		||||
	fi
 | 
			
		||||
	@echo "  CC    $(@:$(BUILD_DIR)/%=%)"
 | 
			
		||||
	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c
 | 
			
		||||
	@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d
 | 
			
		||||
	@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c >$(BUILD_DIR)/$*.d
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,12 @@
 | 
			
		||||
       src="https://scan.coverity.com/projects/4975/badge.svg"/>
 | 
			
		||||
</a>
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client.
 | 
			
		||||
 | 
			
		||||
[](https://i.imgur.com/san99Z2.png)
 | 
			
		||||
[](https://i.imgur.com/TwYA8L0.png)
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
 | 
			
		||||
[Compile it yourself](/INSTALL.md)
 | 
			
		||||
[See the install instructions](/INSTALL.md)
 | 
			
		||||
 | 
			
		||||
## Settings
 | 
			
		||||
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								apidoc/python/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								apidoc/python/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
# Minimal makefile for Sphinx documentation
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# You can set these variables from the command line.
 | 
			
		||||
SPHINXOPTS    =
 | 
			
		||||
SPHINXBUILD   = sphinx-build
 | 
			
		||||
SPHINXPROJ    = toxic_api
 | 
			
		||||
SOURCEDIR     = source
 | 
			
		||||
BUILDDIR      = build
 | 
			
		||||
 | 
			
		||||
# Put it first so that "make" without argument is like "make help".
 | 
			
		||||
help:
 | 
			
		||||
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 | 
			
		||||
 | 
			
		||||
.PHONY: help Makefile
 | 
			
		||||
 | 
			
		||||
# Catch-all target: route all unknown targets to Sphinx using the new
 | 
			
		||||
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
 | 
			
		||||
%: Makefile
 | 
			
		||||
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 | 
			
		||||
							
								
								
									
										157
									
								
								apidoc/python/source/conf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								apidoc/python/source/conf.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,157 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# toxic_api documentation build configuration file, created by
 | 
			
		||||
# sphinx-quickstart on Tue May 16 08:58:24 2017.
 | 
			
		||||
#
 | 
			
		||||
# This file is execfile()d with the current directory set to its
 | 
			
		||||
# containing dir.
 | 
			
		||||
#
 | 
			
		||||
# Note that not all possible configuration values are present in this
 | 
			
		||||
# autogenerated file.
 | 
			
		||||
#
 | 
			
		||||
# All configuration values have a default; values that are commented out
 | 
			
		||||
# serve to show the default.
 | 
			
		||||
 | 
			
		||||
# If extensions (or modules to document with autodoc) are in another directory,
 | 
			
		||||
# add these directories to sys.path here. If the directory is relative to the
 | 
			
		||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
 | 
			
		||||
#
 | 
			
		||||
# import os
 | 
			
		||||
# import sys
 | 
			
		||||
# sys.path.insert(0, os.path.abspath('.'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- General configuration ------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# If your documentation needs a minimal Sphinx version, state it here.
 | 
			
		||||
#
 | 
			
		||||
# needs_sphinx = '1.0'
 | 
			
		||||
 | 
			
		||||
# Add any Sphinx extension module names here, as strings. They can be
 | 
			
		||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 | 
			
		||||
# ones.
 | 
			
		||||
extensions = []
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain templates here, relative to this directory.
 | 
			
		||||
templates_path = ['_templates']
 | 
			
		||||
 | 
			
		||||
# The suffix(es) of source filenames.
 | 
			
		||||
# You can specify multiple suffix as a list of string:
 | 
			
		||||
#
 | 
			
		||||
# source_suffix = ['.rst', '.md']
 | 
			
		||||
source_suffix = '.rst'
 | 
			
		||||
 | 
			
		||||
# The master toctree document.
 | 
			
		||||
master_doc = 'index'
 | 
			
		||||
 | 
			
		||||
# General information about the project.
 | 
			
		||||
project = 'toxic_api'
 | 
			
		||||
copyright = '2017, Jakob Kreuze'
 | 
			
		||||
author = 'Jakob Kreuze'
 | 
			
		||||
 | 
			
		||||
# The version info for the project you're documenting, acts as replacement for
 | 
			
		||||
# |version| and |release|, also used in various other places throughout the
 | 
			
		||||
# built documents.
 | 
			
		||||
#
 | 
			
		||||
# The short X.Y version.
 | 
			
		||||
version = '0.10.0'
 | 
			
		||||
# The full version, including alpha/beta/rc tags.
 | 
			
		||||
release = '0.10.0'
 | 
			
		||||
 | 
			
		||||
# The language for content autogenerated by Sphinx. Refer to documentation
 | 
			
		||||
# for a list of supported languages.
 | 
			
		||||
#
 | 
			
		||||
# This is also used if you do content translation via gettext catalogs.
 | 
			
		||||
# Usually you set "language" from the command line for these cases.
 | 
			
		||||
language = None
 | 
			
		||||
 | 
			
		||||
# List of patterns, relative to source directory, that match files and
 | 
			
		||||
# directories to ignore when looking for source files.
 | 
			
		||||
# This patterns also effect to html_static_path and html_extra_path
 | 
			
		||||
exclude_patterns = []
 | 
			
		||||
 | 
			
		||||
# The name of the Pygments (syntax highlighting) style to use.
 | 
			
		||||
pygments_style = 'sphinx'
 | 
			
		||||
 | 
			
		||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
 | 
			
		||||
todo_include_todos = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for HTML output ----------------------------------------------
 | 
			
		||||
 | 
			
		||||
# The theme to use for HTML and HTML Help pages.  See the documentation for
 | 
			
		||||
# a list of builtin themes.
 | 
			
		||||
#
 | 
			
		||||
html_theme = 'alabaster'
 | 
			
		||||
 | 
			
		||||
# Theme options are theme-specific and customize the look and feel of a theme
 | 
			
		||||
# further.  For a list of options available for each theme, see the
 | 
			
		||||
# documentation.
 | 
			
		||||
#
 | 
			
		||||
# html_theme_options = {}
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain custom static files (such as style sheets) here,
 | 
			
		||||
# relative to this directory. They are copied after the builtin static files,
 | 
			
		||||
# so a file named "default.css" will overwrite the builtin "default.css".
 | 
			
		||||
html_static_path = ['_static']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for HTMLHelp output ------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Output file base name for HTML help builder.
 | 
			
		||||
htmlhelp_basename = 'toxic_apidoc'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for LaTeX output ---------------------------------------------
 | 
			
		||||
 | 
			
		||||
latex_elements = {
 | 
			
		||||
    # The paper size ('letterpaper' or 'a4paper').
 | 
			
		||||
    #
 | 
			
		||||
    # 'papersize': 'letterpaper',
 | 
			
		||||
 | 
			
		||||
    # The font size ('10pt', '11pt' or '12pt').
 | 
			
		||||
    #
 | 
			
		||||
    # 'pointsize': '10pt',
 | 
			
		||||
 | 
			
		||||
    # Additional stuff for the LaTeX preamble.
 | 
			
		||||
    #
 | 
			
		||||
    # 'preamble': '',
 | 
			
		||||
 | 
			
		||||
    # Latex figure (float) alignment
 | 
			
		||||
    #
 | 
			
		||||
    # 'figure_align': 'htbp',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Grouping the document tree into LaTeX files. List of tuples
 | 
			
		||||
# (source start file, target name, title,
 | 
			
		||||
#  author, documentclass [howto, manual, or own class]).
 | 
			
		||||
latex_documents = [
 | 
			
		||||
    (master_doc, 'toxic_api.tex', 'toxic\\_api Documentation',
 | 
			
		||||
     'Jakob Kreuze', 'manual'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for manual page output ---------------------------------------
 | 
			
		||||
 | 
			
		||||
# One entry per manual page. List of tuples
 | 
			
		||||
# (source start file, name, description, authors, manual section).
 | 
			
		||||
man_pages = [
 | 
			
		||||
    (master_doc, 'toxic_api', 'toxic_api Documentation',
 | 
			
		||||
     [author], 1)
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for Texinfo output -------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Grouping the document tree into Texinfo files. List of tuples
 | 
			
		||||
# (source start file, target name, title, author,
 | 
			
		||||
#  dir menu entry, description, category)
 | 
			
		||||
texinfo_documents = [
 | 
			
		||||
    (master_doc, 'toxic_api', 'toxic_api Documentation',
 | 
			
		||||
     author, 'toxic_api', 'One line description of project.',
 | 
			
		||||
     'Miscellaneous'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								apidoc/python/source/examples.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								apidoc/python/source/examples.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
============
 | 
			
		||||
API Examples
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
Fortune
 | 
			
		||||
=======
 | 
			
		||||
.. literalinclude:: fortune.py
 | 
			
		||||
   :language: python
 | 
			
		||||
							
								
								
									
										37
									
								
								apidoc/python/source/fortune.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								apidoc/python/source/fortune.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
import toxic_api
 | 
			
		||||
import random
 | 
			
		||||
 | 
			
		||||
FORTUNES = [
 | 
			
		||||
    "A bug in the code is worth two in the documentation.",
 | 
			
		||||
    "A bug in the hand is better than one as yet undetected.",
 | 
			
		||||
    "\"A debugged program is one for which you have not yet found the "
 | 
			
		||||
    "conditions that make it fail.\" -- Jerry Ogdin"
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
def send_fortune(args):
 | 
			
		||||
    """Callback function that sends the contact of the current window a
 | 
			
		||||
    given number of random fortunes.
 | 
			
		||||
    """
 | 
			
		||||
    if len(args) != 1:
 | 
			
		||||
        toxic_api.display("Only one argument allowed!")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        count = int(args[0])
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        toxic_api.display("Argument must be a number!")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    if count < 0 or count > 20:
 | 
			
		||||
        toxic_api.display("Argument is too large!")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    name = toxic_api.get_nick()
 | 
			
		||||
 | 
			
		||||
    toxic_api.send("%s has decided to send you %d fortunes:" % (name, count))
 | 
			
		||||
    for _ in range(count):
 | 
			
		||||
        toxic_api.send(random.choice(FORTUNES))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
toxic_api.register("/fortune", "Send a fortune to the contact of the current "
 | 
			
		||||
                   "window", send_fortune)
 | 
			
		||||
							
								
								
									
										9
									
								
								apidoc/python/source/index.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								apidoc/python/source/index.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
Toxic Scripting Interface Documentation
 | 
			
		||||
=======================================
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 2
 | 
			
		||||
 | 
			
		||||
   intro
 | 
			
		||||
   reference
 | 
			
		||||
   examples
 | 
			
		||||
							
								
								
									
										12
									
								
								apidoc/python/source/intro.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								apidoc/python/source/intro.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
=========================
 | 
			
		||||
Toxic Scripting Interface
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
A Python scripting interface to `Toxic <https://github.com/JFreegman/toxic>`_.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Getting Started
 | 
			
		||||
===============
 | 
			
		||||
Toxic is compiled with Python support by default. To access the scripting interface, simply import "toxic_api" in your script.
 | 
			
		||||
 | 
			
		||||
Scripts can be run by issuing "/run <path>" from toxic, or placing them in the "autorun_path" from your toxic configuration file.
 | 
			
		||||
							
								
								
									
										73
									
								
								apidoc/python/source/reference.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								apidoc/python/source/reference.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
=============
 | 
			
		||||
API Reference
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
Messages
 | 
			
		||||
========
 | 
			
		||||
.. function:: display(msg)
 | 
			
		||||
 | 
			
		||||
   Display a message to the user through the current window.
 | 
			
		||||
 | 
			
		||||
   :param msg: The message to display.
 | 
			
		||||
   :type msg: string
 | 
			
		||||
   :rtype: none
 | 
			
		||||
 | 
			
		||||
.. function:: send(msg)
 | 
			
		||||
 | 
			
		||||
   Send a message to the user specified by the currently open conversation.
 | 
			
		||||
 | 
			
		||||
   :param msg: The message to display.
 | 
			
		||||
   :type msg: string
 | 
			
		||||
   :rtype: none
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
State
 | 
			
		||||
=====
 | 
			
		||||
.. function:: get_nick()
 | 
			
		||||
 | 
			
		||||
   Return the user's current nickname.
 | 
			
		||||
 | 
			
		||||
   :rtype: string
 | 
			
		||||
 | 
			
		||||
.. function:: get_status()
 | 
			
		||||
 | 
			
		||||
   Return a string representing the user's current status. Can be either "online", "away", or "busy".
 | 
			
		||||
 | 
			
		||||
   :rtype: string
 | 
			
		||||
 | 
			
		||||
.. function:: get_status_message()
 | 
			
		||||
 | 
			
		||||
   Return the user's current status message.
 | 
			
		||||
 | 
			
		||||
   :rtype: string
 | 
			
		||||
 | 
			
		||||
.. function:: get_all_friends()
 | 
			
		||||
 | 
			
		||||
   Return a list of all the user's friends.
 | 
			
		||||
 | 
			
		||||
   :rtype: list of (string, string) tuples containing the nickname followed by their public key
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Commands
 | 
			
		||||
========
 | 
			
		||||
.. function:: execute(command, class)
 | 
			
		||||
 | 
			
		||||
   Executes the given command. The API exports three constants for the class parameter; GLOBAL_COMMAND, CHAT_COMMAND, and GROUPCHAT_COMMAND.
 | 
			
		||||
 | 
			
		||||
   :param command: The command to execute.
 | 
			
		||||
   :type command: string
 | 
			
		||||
   :param class: The class of the command.
 | 
			
		||||
   :type class: int
 | 
			
		||||
   :rtype: none
 | 
			
		||||
 | 
			
		||||
.. function:: register(command, help, callback)
 | 
			
		||||
 | 
			
		||||
   Register a callback to be executed whenever command is run. The callback function will be called with one argument, a list of arguments from when the user calls the command.
 | 
			
		||||
 | 
			
		||||
   :param command: The command to listen for.
 | 
			
		||||
   :type command: string
 | 
			
		||||
   :param help: A description of the command to be shown in the help menu.
 | 
			
		||||
   :type help: string
 | 
			
		||||
   :param callback: The function to be called.
 | 
			
		||||
   :type callback: callable
 | 
			
		||||
   :rtype: none
 | 
			
		||||
							
								
								
									
										26
									
								
								astylerc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								astylerc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
# Bracket Style Options
 | 
			
		||||
--style=kr
 | 
			
		||||
 | 
			
		||||
# Tab Options
 | 
			
		||||
--indent=spaces=4
 | 
			
		||||
 | 
			
		||||
# Indentation Options
 | 
			
		||||
--indent-switches
 | 
			
		||||
 | 
			
		||||
# Padding Options
 | 
			
		||||
--pad-header
 | 
			
		||||
--break-blocks
 | 
			
		||||
--pad-oper
 | 
			
		||||
--unpad-paren
 | 
			
		||||
--align-pointer=name
 | 
			
		||||
--align-reference=name
 | 
			
		||||
 | 
			
		||||
# Formatting Options
 | 
			
		||||
--add-brackets
 | 
			
		||||
--convert-tabs
 | 
			
		||||
--max-code-length=120
 | 
			
		||||
 | 
			
		||||
# Other Options
 | 
			
		||||
--preserve-date
 | 
			
		||||
--formatted
 | 
			
		||||
--lineend=linux
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
# Variables for audio call support
 | 
			
		||||
AUDIO_LIBS = libtoxav openal
 | 
			
		||||
AUDIO_LIBS = openal
 | 
			
		||||
AUDIO_CFLAGS = -DAUDIO
 | 
			
		||||
ifneq (, $(findstring audio_device.o, $(OBJ)))
 | 
			
		||||
    AUDIO_OBJ = audio_call.o
 | 
			
		||||
@@ -8,13 +8,14 @@ else
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we can build audio support
 | 
			
		||||
CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
 | 
			
		||||
CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
 | 
			
		||||
ifneq ($(CHECK_AUDIO_LIBS), error)
 | 
			
		||||
    LIBS += $(AUDIO_LIBS)
 | 
			
		||||
    LDFLAGS += -lm
 | 
			
		||||
    CFLAGS += $(AUDIO_CFLAGS)
 | 
			
		||||
    OBJ += $(AUDIO_OBJ)
 | 
			
		||||
else ifneq ($(MAKECMDGOALS), clean)
 | 
			
		||||
    MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    MISSING_AUDIO_LIBS := $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    $(warning WARNING -- Toxic will be compiled without audio support)
 | 
			
		||||
    $(warning WARNING -- You need these libraries for audio support)
 | 
			
		||||
    $(warning WARNING -- $(MISSING_AUDIO_LIBS))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,19 @@
 | 
			
		||||
CHECKS_DIR = $(CFG_DIR)/checks
 | 
			
		||||
 | 
			
		||||
# Check if we want build X11 support
 | 
			
		||||
X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
X11 := $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(X11), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/x11.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build audio support
 | 
			
		||||
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
AUDIO := $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(AUDIO), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/audio.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build video support
 | 
			
		||||
VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
VIDEO := $(shell if [ -z "$(DISABLE_VI)" ] || [ "$(DISABLE_VI)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(X11), disabled)
 | 
			
		||||
ifneq ($(AUDIO), disabled)
 | 
			
		||||
ifneq ($(VIDEO), disabled)
 | 
			
		||||
@@ -23,24 +23,42 @@ endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build sound notifications support
 | 
			
		||||
SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
SND_NOTIFY := $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(SND_NOTIFY), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/sound_notifications.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build desktop notifications support
 | 
			
		||||
DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
DESK_NOTIFY := $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(DESK_NOTIFY), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/desktop_notifications.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build QR export support
 | 
			
		||||
QR_CODE := $(shell if [ -z "$(DISABLE_QRCODE)" ] || [ "$(DISABLE_QRCODE)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(QR_CODE), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/qr.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build QR exported as PNG support
 | 
			
		||||
QR_PNG := $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(QR_PNG), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/qr_png.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build Python scripting support
 | 
			
		||||
PYTHON := $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
 | 
			
		||||
ifneq ($(PYTHON), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/python.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we can build Toxic
 | 
			
		||||
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
 | 
			
		||||
CHECK_LIBS := $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
 | 
			
		||||
ifneq ($(CHECK_LIBS), error)
 | 
			
		||||
    CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS))
 | 
			
		||||
    LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS))
 | 
			
		||||
else ifneq ($(MAKECMDGOALS), clean)
 | 
			
		||||
    MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    MISSING_LIBS := $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    $(warning ERROR -- Cannot compile Toxic)
 | 
			
		||||
    $(warning ERROR -- You need these libraries)
 | 
			
		||||
    $(warning ERROR -- $(MISSING_LIBS))
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,12 @@ DESK_NOTIFY_LIBS = libnotify
 | 
			
		||||
DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY
 | 
			
		||||
 | 
			
		||||
# Check if we can build desktop notifications support
 | 
			
		||||
CHECK_DESK_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
 | 
			
		||||
CHECK_DESK_NOTIFY_LIBS := $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
 | 
			
		||||
ifneq ($(CHECK_DESK_NOTIFY_LIBS), error)
 | 
			
		||||
    LIBS += $(DESK_NOTIFY_LIBS)
 | 
			
		||||
    CFLAGS += $(DESK_NOTIFY_CFLAGS)
 | 
			
		||||
else ifneq ($(MAKECMDGOALS), clean)
 | 
			
		||||
    MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    MISSING_DESK_NOTIFY_LIBS := $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    $(warning WARNING -- Toxic will be compiled without desktop notifications support)
 | 
			
		||||
    $(warning WARNING -- You need these libraries for desktop notifications support)
 | 
			
		||||
    $(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								cfg/checks/python.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/python.mk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
# Variables for Python scripting support
 | 
			
		||||
PYTHON3_LIBS = python3
 | 
			
		||||
PYTHON_CFLAGS = -DPYTHON
 | 
			
		||||
PYTHON_OBJ = api.o python_api.o
 | 
			
		||||
 | 
			
		||||
# Check if we can build Python scripting support
 | 
			
		||||
CHECK_PYTHON3_LIBS = $(shell $(PKG_CONFIG) --exists $(PYTHON3_LIBS) || echo -n "error")
 | 
			
		||||
ifneq ($(CHECK_PYTHON3_LIBS), error)
 | 
			
		||||
    LDFLAGS += $(shell python3-config --ldflags)
 | 
			
		||||
    CFLAGS += $(PYTHON_CFLAGS) $(shell python3-config --includes)
 | 
			
		||||
    OBJ += $(PYTHON_OBJ)
 | 
			
		||||
else ifneq ($(MAKECMDGOALS), clean)
 | 
			
		||||
    $(warning WARNING -- Toxic will be compiled without Python scripting support)
 | 
			
		||||
    $(warning WARNING -- You need python3 installed for Python scripting support)
 | 
			
		||||
endif
 | 
			
		||||
							
								
								
									
										15
									
								
								cfg/checks/qr.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/qr.mk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
# Variables for QR export support
 | 
			
		||||
QR_LIBS = libqrencode
 | 
			
		||||
QR_CFLAGS = -DQRCODE
 | 
			
		||||
 | 
			
		||||
# Check if we can build QR export support
 | 
			
		||||
CHECK_QR_LIBS = $(shell pkg-config --exists $(QR_LIBS) || echo -n "error")
 | 
			
		||||
ifneq ($(CHECK_QR_LIBS), error)
 | 
			
		||||
    LIBS += $(QR_LIBS)
 | 
			
		||||
    CFLAGS += $(QR_CFLAGS)
 | 
			
		||||
else ifneq ($(MAKECMDGOALS), clean)
 | 
			
		||||
    MISSING_QR_LIBS = $(shell for lib in $(QR_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    $(warning WARNING -- Toxic will be compiled without QR export support)
 | 
			
		||||
    $(warning WARNING -- You need these libraries for QR export support)
 | 
			
		||||
    $(warning WARNING -- $(MISSING_QR_LIBS))
 | 
			
		||||
endif
 | 
			
		||||
							
								
								
									
										15
									
								
								cfg/checks/qr_png.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/qr_png.mk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
# Variables for QR exported as PNG support
 | 
			
		||||
PNG_LIBS = libpng
 | 
			
		||||
PNG_CFLAGS = -DQRPNG
 | 
			
		||||
 | 
			
		||||
# Check if we can build QR exported as PNG support
 | 
			
		||||
CHECK_PNG_LIBS = $(shell pkg-config --exists $(PNG_LIBS) || echo -n "error")
 | 
			
		||||
ifneq ($(CHECK_PNG_LIBS), error)
 | 
			
		||||
    LIBS += $(PNG_LIBS)
 | 
			
		||||
    CFLAGS += $(PNG_CFLAGS)
 | 
			
		||||
else ifneq ($(MAKECMDGOALS), clean)
 | 
			
		||||
    MISSING_PNG_LIBS = $(shell for lib in $(PNG_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    $(warning WARNING -- Toxic will be compiled without QR exported as PNG support)
 | 
			
		||||
    $(warning WARNING -- You need these libraries for QR exported as PNG support)
 | 
			
		||||
    $(warning WARNING -- $(MISSING_PNG_LIBS))
 | 
			
		||||
endif
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
# Variables for video call support
 | 
			
		||||
VIDEO_LIBS = libtoxav vpx x11
 | 
			
		||||
VIDEO_LIBS = openal vpx x11
 | 
			
		||||
VIDEO_CFLAGS = -DVIDEO
 | 
			
		||||
ifneq (, $(findstring video_device.o, $(OBJ)))
 | 
			
		||||
    VIDEO_OBJ = video_call.o
 | 
			
		||||
@@ -8,13 +8,13 @@ else
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we can build video support
 | 
			
		||||
CHECK_VIDEO_LIBS = $(shell pkg-config --exists $(VIDEO_LIBS) || echo -n "error")
 | 
			
		||||
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)
 | 
			
		||||
    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))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
# Variables for X11 support
 | 
			
		||||
X11_LIBS = x11
 | 
			
		||||
X11_CFLAGS = -DX11
 | 
			
		||||
X11_OBJ = xtra.o
 | 
			
		||||
X11_OBJ = x11focus.o
 | 
			
		||||
 | 
			
		||||
# Check if we can build X11 support
 | 
			
		||||
CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
# Version
 | 
			
		||||
TOXIC_VERSION = 0.7.0
 | 
			
		||||
TOXIC_VERSION = 0.10.0
 | 
			
		||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
 | 
			
		||||
ifneq (, $(findstring error, $(REV)))
 | 
			
		||||
    VERSION = $(TOXIC_VERSION)
 | 
			
		||||
@@ -16,17 +16,17 @@ MISC_DIR = $(BASE_DIR)/misc
 | 
			
		||||
 | 
			
		||||
# Project files
 | 
			
		||||
MANFILES = toxic.1 toxic.conf.5
 | 
			
		||||
DATAFILES = DHTnodes nameservers 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
 | 
			
		||||
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
 | 
			
		||||
 | 
			
		||||
# Install directories
 | 
			
		||||
PREFIX = /usr/local
 | 
			
		||||
PREFIX ?= /usr/local
 | 
			
		||||
BINDIR = $(PREFIX)/bin
 | 
			
		||||
DATADIR = $(PREFIX)/share/toxic
 | 
			
		||||
MANDIR = $(PREFIX)/share/man
 | 
			
		||||
MANDIR ?= $(PREFIX)/share/man
 | 
			
		||||
APPDIR = $(PREFIX)/share/applications
 | 
			
		||||
 | 
			
		||||
# Platform tools
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ 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 -lcurl -lconfig -ltoxencryptsave -g
 | 
			
		||||
LDFLAGS += -lncurses -lalut -ltoxcore -lcurl -lconfig -lqrencode -lpng -lopenal -g
 | 
			
		||||
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
 | 
			
		||||
 | 
			
		||||
OSX_LIBRARIES = -lobjc -lresolv
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
# Specials options for freebsd systems
 | 
			
		||||
LIBS := $(filter-out ncursesw, $(LIBS))
 | 
			
		||||
LDFLAGS += -lncursesw -lcurl
 | 
			
		||||
MANDIR = $(PREFIX)/man
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
# Specials options for linux systems
 | 
			
		||||
CFLAGS +=
 | 
			
		||||
LDFLAGS += -ldl -lrt -lcurl
 | 
			
		||||
MANDIR = $(PREFIX)/share/man
 | 
			
		||||
@@ -14,9 +14,24 @@ 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_QRCODE:         Set to \"1\" to force building without QR export support"
 | 
			
		||||
	@echo "  DISABLE_QRPNG:          Set to \"1\" to force building without QR exported as PNG support"
 | 
			
		||||
	@echo "  ENABLE_PYTHON:          Set to \"1\" to enable building with Python scripting support"
 | 
			
		||||
	@echo "  ENABLE_RELEASE:         Set to \"1\" to build without debug symbols and with full compiler optimizations"
 | 
			
		||||
	@echo "  ENABLE_ASAN:            Set to \"1\" to build with LLVM address sanitizer enabled.
 | 
			
		||||
	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS"
 | 
			
		||||
	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS"
 | 
			
		||||
	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
 | 
			
		||||
	@echo "  DESTDIR:                Specify a directory where to store installed files (mainly for packaging purpose)"
 | 
			
		||||
	@echo "  MANDIR:                 Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "-- Environment Variables --"
 | 
			
		||||
	@echo "  CFLAGS:                 Add custom flags to default CFLAGS"
 | 
			
		||||
	@echo "  LDFLAGS:                Add custom flags to default LDFLAGS"
 | 
			
		||||
	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS"
 | 
			
		||||
	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS"
 | 
			
		||||
	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
 | 
			
		||||
	@echo "  DESTDIR:                Specify a directory where to store installed files (mainly for packaging purpose)"
 | 
			
		||||
	@echo "  MANDIR:                 Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
 | 
			
		||||
 | 
			
		||||
.PHONY: help
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ install: $(BUILD_DIR)/toxic
 | 
			
		||||
		if [ ! -e "$(DOC_DIR)/$$f" ]; then \
 | 
			
		||||
			continue ;\
 | 
			
		||||
		fi ;\
 | 
			
		||||
		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
 | 
			
		||||
		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
 | 
			
		||||
		file=$$section/$$f ;\
 | 
			
		||||
		mkdir -p $$section ;\
 | 
			
		||||
		install -m 0644 $(DOC_DIR)/$$f $$file ;\
 | 
			
		||||
@@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic
 | 
			
		||||
		mv temp_file $$file ;\
 | 
			
		||||
		sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
 | 
			
		||||
		mv temp_file $$file ;\
 | 
			
		||||
		gzip -f -9 $$file ;\
 | 
			
		||||
		gzip -f -n -9 $$file ;\
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
.PHONY: install
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ uninstall:
 | 
			
		||||
	
 | 
			
		||||
	@echo "Removing man pages"
 | 
			
		||||
	@for f in $(MANFILES) ; do \
 | 
			
		||||
		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
 | 
			
		||||
		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
 | 
			
		||||
		file=$$section/$$f ;\
 | 
			
		||||
		rm -f $$file $$file.gz ;\
 | 
			
		||||
	done
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								doc/toxic.1
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								doc/toxic.1
									
									
									
									
									
								
							@@ -1,13 +1,13 @@
 | 
			
		||||
'\" t
 | 
			
		||||
.\"     Title: toxic
 | 
			
		||||
.\"    Author: [see the "AUTHORS" section]
 | 
			
		||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
 | 
			
		||||
.\"      Date: 2015-07-08
 | 
			
		||||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
 | 
			
		||||
.\"      Date: 2020-05-04
 | 
			
		||||
.\"    Manual: Toxic Manual
 | 
			
		||||
.\"    Source: toxic __VERSION__
 | 
			
		||||
.\"  Language: English
 | 
			
		||||
.\"
 | 
			
		||||
.TH "TOXIC" "1" "2015\-07\-08" "toxic __VERSION__" "Toxic Manual"
 | 
			
		||||
.TH "TOXIC" "1" "2020\-05\-04" "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
 | 
			
		||||
@@ -78,12 +78,17 @@ instead of
 | 
			
		||||
Show help message
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\-l, \-\-logging
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable toxcore logging to stderr
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\-n, \-\-nodes nodes\-file
 | 
			
		||||
.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
 | 
			
		||||
@@ -122,12 +127,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 +151,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 +164,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,16 @@ 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
 | 
			
		||||
 | 
			
		||||
-l, --logging::
 | 
			
		||||
    Enable toxcore logging to stderr
 | 
			
		||||
 | 
			
		||||
-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
 | 
			
		||||
@@ -68,10 +70,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 +86,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 +103,6 @@ SEE ALSO
 | 
			
		||||
 | 
			
		||||
LINKS
 | 
			
		||||
-----
 | 
			
		||||
Project page: <https://github.com/Tox/toxic>
 | 
			
		||||
Project page: <https://github.com/JFreegman/toxic>
 | 
			
		||||
 | 
			
		||||
IRC channel: chat.freenode.net#tox
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										126
									
								
								doc/toxic.conf.5
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								doc/toxic.conf.5
									
									
									
									
									
								
							@@ -1,13 +1,13 @@
 | 
			
		||||
'\" t
 | 
			
		||||
.\"     Title: toxic.conf
 | 
			
		||||
.\"    Author: [see the "AUTHORS" section]
 | 
			
		||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
 | 
			
		||||
.\"      Date: 2015-03-28
 | 
			
		||||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
 | 
			
		||||
.\"      Date: 2020-11-18
 | 
			
		||||
.\"    Manual: Toxic Manual
 | 
			
		||||
.\"    Source: toxic __VERSION__
 | 
			
		||||
.\"  Language: English
 | 
			
		||||
.\"
 | 
			
		||||
.TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
 | 
			
		||||
.TH "TOXIC\&.CONF" "5" "2020\-11\-18" "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
 | 
			
		||||
@@ -93,6 +93,26 @@ Enable or disable terminal alerts on events\&. true or false
 | 
			
		||||
Select between native terminal colors and toxic color theme\&. true or false
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBcolor_bar_bg\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
set background color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBcolor_bar_fg\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
set foreground (text) color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBcolor_bar_accent\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
set foreground accent color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBcolor_bar_notify\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
set foreground notify (and typing) color in chat status bar\&. (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBautolog\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable or disable autologging\&. true or false
 | 
			
		||||
@@ -113,11 +133,31 @@ 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\&. (integer; 0 to disable)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBautosave_freq\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
How often in seconds to auto\-save the Tox data file\&. (integer; 0 to disable)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBhistory_size\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBnotification_timeout\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBline_join\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
 | 
			
		||||
@@ -146,6 +186,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
 | 
			
		||||
@@ -165,9 +237,24 @@ Audio output device\&. Integer value\&. Number corresponds to
 | 
			
		||||
/lsdev out
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBVAD_treshold\fR
 | 
			
		||||
\fBVAD_threshold\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0
 | 
			
		||||
Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBconference_audio_channels\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBchat_audio_channels\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBpush_to_talk\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false
 | 
			
		||||
.RE
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
@@ -185,10 +272,20 @@ Default path for downloads\&. String value\&. Absolute path for downloaded files
 | 
			
		||||
Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBautorun_path\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Path for any scripts that should be run on startup
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBchatlogs_path\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBpassword_eval\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Replace password prompt by running this command and using its output as the password\&.
 | 
			
		||||
.RE
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBsounds\fR
 | 
			
		||||
@@ -289,20 +386,15 @@ Key combination to scroll half page down\&.
 | 
			
		||||
Key combination to scroll to page bottom\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBpeer_list_up\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Key combination to scroll contacts list up\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBpeer_list_down\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Key combination to scroll contacts list down\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBtoggle_peerlist\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Toggle the peer list on and off\&.
 | 
			
		||||
.RE
 | 
			
		||||
.PP
 | 
			
		||||
\fBtoggle_paste_mode\fR
 | 
			
		||||
.RS 4
 | 
			
		||||
Toggle treating linebreaks as enter key press\&.
 | 
			
		||||
.RE
 | 
			
		||||
.RE
 | 
			
		||||
.SH "FILES"
 | 
			
		||||
.PP
 | 
			
		||||
@@ -320,7 +412,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,11 +55,23 @@ 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
 | 
			
		||||
 | 
			
		||||
    *color_bar_bg*;;
 | 
			
		||||
        set background color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
 | 
			
		||||
    *color_bar_fg*;;
 | 
			
		||||
        set foreground (text) color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
 | 
			
		||||
    *color_bar_accent*;;
 | 
			
		||||
        set foreground accent color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
 | 
			
		||||
    *color_bar_notify*;;
 | 
			
		||||
        set foreground notify (and typing) color in chat status bar. (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
 | 
			
		||||
    *autolog*;;
 | 
			
		||||
        Enable or disable autologging. true or false
 | 
			
		||||
 | 
			
		||||
@@ -72,9 +84,21 @@ 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. (integer; 0 to disable)
 | 
			
		||||
 | 
			
		||||
    *autosave_freq*;;
 | 
			
		||||
        How often in seconds to auto-save the Tox data file. (integer; 0 to disable)
 | 
			
		||||
 | 
			
		||||
    *history_size*;;
 | 
			
		||||
        Maximum lines for chat window history. Integer value. (for example: 700)
 | 
			
		||||
 | 
			
		||||
    *notification_timeout*;;
 | 
			
		||||
        Time in milliseconds to display a notification. Integer value. (for example: 3000)
 | 
			
		||||
 | 
			
		||||
    *line_join*;;
 | 
			
		||||
        Indicator for when someone connects or joins a group.
 | 
			
		||||
        Three characters max for line_ settings.
 | 
			
		||||
@@ -97,6 +121,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.
 | 
			
		||||
 | 
			
		||||
@@ -106,9 +147,18 @@ OPTIONS
 | 
			
		||||
    *output_device*;;
 | 
			
		||||
        Audio output device. Integer value. Number corresponds to `/lsdev out`
 | 
			
		||||
 | 
			
		||||
    *VAD_treshold*;;
 | 
			
		||||
        Voice Activity Detection treshold.  Float value. Recommended values are
 | 
			
		||||
        around 40.0
 | 
			
		||||
    *VAD_threshold*;;
 | 
			
		||||
        Voice Activity Detection threshold.  Float value. Recommended values are
 | 
			
		||||
        1.0-40.0
 | 
			
		||||
 | 
			
		||||
    *conference_audio_channels*;;
 | 
			
		||||
        Number of channels for conference audio broadcast. Integer value. 1 (mono) or 2 (stereo)
 | 
			
		||||
 | 
			
		||||
    *chat_audio_channels*;;
 | 
			
		||||
        Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo)
 | 
			
		||||
 | 
			
		||||
    *push_to_talk*;;
 | 
			
		||||
        Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false
 | 
			
		||||
 | 
			
		||||
*tox*::
 | 
			
		||||
    Configuration related to paths.
 | 
			
		||||
@@ -120,9 +170,16 @@ OPTIONS
 | 
			
		||||
    *avatar_path*;;
 | 
			
		||||
        Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
 | 
			
		||||
 | 
			
		||||
    *autorun_path*;;
 | 
			
		||||
        Path for any scripts that should be run on startup
 | 
			
		||||
 | 
			
		||||
    *chatlogs_path*;;
 | 
			
		||||
        Default path for chatlogs. String value. Absolute path for chatlog files.
 | 
			
		||||
 | 
			
		||||
    *password_eval*;;
 | 
			
		||||
        Replace password prompt by running this command and using its output as
 | 
			
		||||
	the password.
 | 
			
		||||
 | 
			
		||||
*sounds*::
 | 
			
		||||
    Configuration related to notification sounds.
 | 
			
		||||
    Special value "silent" can be used to disable a specific notification. +
 | 
			
		||||
@@ -185,15 +242,12 @@ OPTIONS
 | 
			
		||||
    *page_bottom*;;
 | 
			
		||||
        Key combination to scroll to page bottom.
 | 
			
		||||
 | 
			
		||||
    *peer_list_up*;;
 | 
			
		||||
        Key combination to scroll contacts list up.
 | 
			
		||||
 | 
			
		||||
    *peer_list_down*;;
 | 
			
		||||
        Key combination to scroll contacts list down.
 | 
			
		||||
 | 
			
		||||
    *toggle_peerlist*;;
 | 
			
		||||
        Toggle the peer list on and off.
 | 
			
		||||
 | 
			
		||||
    *toggle_paste_mode*;;
 | 
			
		||||
        Toggle treating linebreaks as enter key press.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FILES
 | 
			
		||||
-----
 | 
			
		||||
@@ -211,7 +265,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,17 +0,0 @@
 | 
			
		||||
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
 | 
			
		||||
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
 | 
			
		||||
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
 | 
			
		||||
195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
 | 
			
		||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
 | 
			
		||||
46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A
 | 
			
		||||
178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B
 | 
			
		||||
130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F
 | 
			
		||||
104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57
 | 
			
		||||
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
 | 
			
		||||
185.25.116.107 33445 DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43
 | 
			
		||||
192.99.168.140 33445 6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F
 | 
			
		||||
@@ -1,2 +1 @@
 | 
			
		||||
toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,20 +5,44 @@ 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;
 | 
			
		||||
 | 
			
		||||
  // set background color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
  color_bar_bg="blue";
 | 
			
		||||
 | 
			
		||||
  // set foreground (text) color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
  color_bar_fg="white";
 | 
			
		||||
 | 
			
		||||
  // set foreground accent color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
  color_bar_accent="cyan";
 | 
			
		||||
 | 
			
		||||
  // set foreground notify (and typing) color in chat status bar (black, white, red, green, blue, cyan, yellow, magenta)
 | 
			
		||||
  color_bar_notify="yellow";
 | 
			
		||||
 | 
			
		||||
  // true to enable autologging, false to disable
 | 
			
		||||
  autolog=false;
 | 
			
		||||
 | 
			
		||||
  // 24 or 12 hour time
 | 
			
		||||
  time_format=24;
 | 
			
		||||
 | 
			
		||||
  // timestamp format string according to date/strftime format. Overrides time_format setting
 | 
			
		||||
  timestamp_format="%H:%M:%S";
 | 
			
		||||
  // Timestamp format string according to date/strftime format. Overrides time_format setting
 | 
			
		||||
  timestamp_format="%H:%M";
 | 
			
		||||
 | 
			
		||||
  // true to show you when others are typing a message in 1-on-1 chats
 | 
			
		||||
  show_typing_other=true;
 | 
			
		||||
@@ -29,20 +53,32 @@ 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;
 | 
			
		||||
 | 
			
		||||
  // How often in seconds to auto-save the Tox data file. (0 to disable periodic auto-saves)
 | 
			
		||||
  autosave_freq=600;
 | 
			
		||||
 | 
			
		||||
  // maximum lines for chat window history
 | 
			
		||||
  history_size=700;
 | 
			
		||||
 | 
			
		||||
  // Indicator for display when someone connects or joins a group.
 | 
			
		||||
  // time in milliseconds to display a notification
 | 
			
		||||
  notification_timeout=6000;
 | 
			
		||||
 | 
			
		||||
  // Indicator for display when someone connects or joins a group
 | 
			
		||||
  line_join="-->";
 | 
			
		||||
 | 
			
		||||
  // Indicator for display when someone disconnects or leaves a group.
 | 
			
		||||
  // Indicator for display when someone disconnects or leaves a group
 | 
			
		||||
  line_quit="<--";
 | 
			
		||||
 | 
			
		||||
  // Indicator for alert messages.
 | 
			
		||||
  line_alert="-!-";
 | 
			
		||||
 | 
			
		||||
  // Indicator for normal messages.
 | 
			
		||||
  line_normal="---";
 | 
			
		||||
  line_normal="-";
 | 
			
		||||
 | 
			
		||||
  // true to change status based on screen/tmux attach/detach, false to disable
 | 
			
		||||
  mplex_away=true;
 | 
			
		||||
@@ -58,8 +94,17 @@ audio = {
 | 
			
		||||
  // preferred audio output device; numbers correspond to /lsdev out
 | 
			
		||||
  output_device=0;
 | 
			
		||||
 | 
			
		||||
  // default VAD treshold; float (recommended values are around 40)
 | 
			
		||||
  VAD_treshold=40.0;
 | 
			
		||||
  // default VAD threshold; float (recommended values are 1.0-40.0)
 | 
			
		||||
  VAD_threshold=5.0;
 | 
			
		||||
 | 
			
		||||
  // Number of channels to use for conference audio broadcasts; 1 for mono, 2 for stereo.
 | 
			
		||||
  conference_audio_channels=1;
 | 
			
		||||
 | 
			
		||||
  // Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo.
 | 
			
		||||
  chat_audio_channels=2;
 | 
			
		||||
 | 
			
		||||
  // toggle conference push-to-talk
 | 
			
		||||
  push_to_talk=false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
tox = {
 | 
			
		||||
@@ -69,6 +114,9 @@ tox = {
 | 
			
		||||
  // Path for your avatar (file must be a .png and cannot exceed 64 KiB)
 | 
			
		||||
  // avatar_path="/home/USERNAME/Pictures/youravatar.png";
 | 
			
		||||
 | 
			
		||||
  // Path for scripts that should be run on startup
 | 
			
		||||
  // autorun_path="/home/USERNAME/toxic_scripts/";
 | 
			
		||||
 | 
			
		||||
  // Path for chatlogs
 | 
			
		||||
  // chatlogs_path="/home/USERNAME/toxic_chatlogs/";
 | 
			
		||||
};
 | 
			
		||||
@@ -86,7 +134,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";
 | 
			
		||||
@@ -95,8 +143,6 @@ keys = {
 | 
			
		||||
  half_page_up="Ctrl+F";
 | 
			
		||||
  half_page_down="Ctrl+V";
 | 
			
		||||
  page_bottom="Ctrl+H";
 | 
			
		||||
  peer_list_up="Ctrl+[";
 | 
			
		||||
  peer_list_down="Ctrl+]";
 | 
			
		||||
  toggle_peerlist="Ctrl+b";
 | 
			
		||||
  toggle_peerlist="Ctrl+B";
 | 
			
		||||
  toggle_paste_mode="Ctrl+T";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										217
									
								
								src/api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/api.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
			
		||||
/*  api.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include <tox/tox.h>
 | 
			
		||||
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "message_queue.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "toxic_strings.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
#include "python_api.h"
 | 
			
		||||
 | 
			
		||||
Tox              *user_tox;
 | 
			
		||||
static WINDOW    *cur_window;
 | 
			
		||||
static ToxWindow *self_window;
 | 
			
		||||
 | 
			
		||||
extern FriendsList Friends;
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
 | 
			
		||||
void api_display(const char *const msg)
 | 
			
		||||
{
 | 
			
		||||
    if (msg == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self_window = get_active_window();
 | 
			
		||||
    line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FriendsList api_get_friendslist(void)
 | 
			
		||||
{
 | 
			
		||||
    return Friends;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *api_get_nick(void)
 | 
			
		||||
{
 | 
			
		||||
    size_t   len  = tox_self_get_name_size(user_tox);
 | 
			
		||||
    uint8_t *name = malloc(len + 1);
 | 
			
		||||
 | 
			
		||||
    if (name == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tox_self_get_name(user_tox, name);
 | 
			
		||||
    name[len] = '\0';
 | 
			
		||||
    return (char *) name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Tox_User_Status api_get_status(void)
 | 
			
		||||
{
 | 
			
		||||
    return tox_self_get_status(user_tox);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *api_get_status_message(void)
 | 
			
		||||
{
 | 
			
		||||
    size_t   len    = tox_self_get_status_message_size(user_tox);
 | 
			
		||||
    uint8_t *status = malloc(len + 1);
 | 
			
		||||
 | 
			
		||||
    if (status == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tox_self_get_status_message(user_tox, status);
 | 
			
		||||
    status[len] = '\0';
 | 
			
		||||
    return (char *) status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void api_send(const char *msg)
 | 
			
		||||
{
 | 
			
		||||
    if (msg == NULL || self_window->chatwin->cqueue == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char *name = api_get_nick();
 | 
			
		||||
 | 
			
		||||
    if (name == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self_window = get_active_window();
 | 
			
		||||
 | 
			
		||||
    strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line));
 | 
			
		||||
    add_line_to_hist(self_window->chatwin);
 | 
			
		||||
    int id = line_info_add(self_window, true, name, NULL, OUT_MSG, 0, 0, "%s", msg);
 | 
			
		||||
    cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id);
 | 
			
		||||
    free(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void api_execute(const char *input, int mode)
 | 
			
		||||
{
 | 
			
		||||
    self_window = get_active_window();
 | 
			
		||||
    execute(cur_window, self_window, user_tox, input, mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    return do_python_command(num_args, args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int num_registered_handlers(void)
 | 
			
		||||
{
 | 
			
		||||
    return python_num_registered_handlers();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int help_max_width(void)
 | 
			
		||||
{
 | 
			
		||||
    return python_help_max_width();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void draw_handler_help(WINDOW *win)
 | 
			
		||||
{
 | 
			
		||||
    python_draw_handler_help(win);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    FILE       *fp;
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    cur_window  = window;
 | 
			
		||||
    self_window = self;
 | 
			
		||||
 | 
			
		||||
    if (argc != 1) {
 | 
			
		||||
        if (argc < 1) {
 | 
			
		||||
            error_str = "Path must be specified.";
 | 
			
		||||
        } else {
 | 
			
		||||
            error_str = "Only one argument allowed.";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fp = fopen(argv[1], "r");
 | 
			
		||||
 | 
			
		||||
    if (fp == NULL) {
 | 
			
		||||
        error_str = "Path does not exist.";
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    run_python(fp, argv[1]);
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void invoke_autoruns(WINDOW *window, ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    char abspath_buf[PATH_MAX + 256];
 | 
			
		||||
    char err_buf[PATH_MAX + 128];
 | 
			
		||||
 | 
			
		||||
    if (user_settings->autorun_path[0] == '\0') {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DIR *d = opendir(user_settings->autorun_path);
 | 
			
		||||
 | 
			
		||||
    if (d == NULL) {
 | 
			
		||||
        snprintf(err_buf, sizeof(err_buf), "Autorun path does not exist: %s", user_settings->autorun_path);
 | 
			
		||||
        api_display(err_buf);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct dirent *dir = NULL;
 | 
			
		||||
 | 
			
		||||
    cur_window  = window;
 | 
			
		||||
 | 
			
		||||
    self_window = self;
 | 
			
		||||
 | 
			
		||||
    while ((dir = readdir(d)) != NULL) {
 | 
			
		||||
        size_t path_len = strlen(dir->d_name);
 | 
			
		||||
 | 
			
		||||
        if (!strcmp(dir->d_name + path_len - 3, ".py")) {
 | 
			
		||||
            snprintf(abspath_buf, sizeof(abspath_buf), "%s%s", user_settings->autorun_path, dir->d_name);
 | 
			
		||||
            FILE *fp = fopen(abspath_buf, "r");
 | 
			
		||||
 | 
			
		||||
            if (fp == NULL) {
 | 
			
		||||
                snprintf(err_buf, sizeof(err_buf), "Invalid path: %s", abspath_buf);
 | 
			
		||||
                api_display(err_buf);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            run_python(fp, abspath_buf);
 | 
			
		||||
            fclose(fp);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closedir(d);
 | 
			
		||||
}
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
							
								
								
									
										43
									
								
								src/api.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/api.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/*  api.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef API_H
 | 
			
		||||
#define API_H
 | 
			
		||||
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
void api_display(const char *const msg);
 | 
			
		||||
FriendsList api_get_friendslist(void);
 | 
			
		||||
char *api_get_nick(void);
 | 
			
		||||
Tox_User_Status api_get_status(void);
 | 
			
		||||
char *api_get_status_message(void);
 | 
			
		||||
void api_send(const char *msg);
 | 
			
		||||
void api_execute(const char *input, int mode);
 | 
			
		||||
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]);
 | 
			
		||||
int num_registered_handlers(void);
 | 
			
		||||
int help_max_width(void);
 | 
			
		||||
void draw_handler_help(WINDOW *win);
 | 
			
		||||
void invoke_autoruns(WINDOW *w, ToxWindow *self);
 | 
			
		||||
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
 | 
			
		||||
#endif /* API_H */
 | 
			
		||||
							
								
								
									
										1053
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										1053
									
								
								src/audio_call.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -27,9 +27,7 @@
 | 
			
		||||
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_CALLS 10
 | 
			
		||||
 | 
			
		||||
typedef enum _AudioError {
 | 
			
		||||
typedef enum AudioError {
 | 
			
		||||
    ae_None = 0,
 | 
			
		||||
    ae_StartingCaptureDevice = 1 << 0,
 | 
			
		||||
    ae_StartingOutputDevice = 1 << 1,
 | 
			
		||||
@@ -37,7 +35,7 @@ typedef enum _AudioError {
 | 
			
		||||
} AudioError;
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
typedef enum _VideoError {
 | 
			
		||||
typedef enum VideoError {
 | 
			
		||||
    ve_None = 0,
 | 
			
		||||
    ve_StartingCaptureDevice = 1 << 0,
 | 
			
		||||
    ve_StartingOutputDevice = 1 << 1,
 | 
			
		||||
@@ -46,14 +44,27 @@ typedef enum _VideoError {
 | 
			
		||||
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
/* Status transitions:
 | 
			
		||||
 * None -> Pending (call invitation made or received);
 | 
			
		||||
 * Pending -> None (invitation rejected or failed);
 | 
			
		||||
 * Pending -> Active (call starts);
 | 
			
		||||
 * Active -> None (call ends).
 | 
			
		||||
 */
 | 
			
		||||
typedef enum CallStatus {
 | 
			
		||||
    cs_None = 0,
 | 
			
		||||
    cs_Pending,
 | 
			
		||||
    cs_Active
 | 
			
		||||
} CallStatus;
 | 
			
		||||
 | 
			
		||||
typedef struct Call {
 | 
			
		||||
    pthread_t ttid; /* Transmission thread id */
 | 
			
		||||
    bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
 | 
			
		||||
    uint32_t in_idx, out_idx; /* Audio Index */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    uint32_t vin_idx, vout_idx; /* Video Index */
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    pthread_mutex_t mutex;
 | 
			
		||||
    CallStatus status;
 | 
			
		||||
    uint32_t state; /* ToxAV call state, valid when `status == cs_Active` */
 | 
			
		||||
    uint32_t in_idx, out_idx; /* Audio device index, or -1 if not open */
 | 
			
		||||
    uint32_t audio_bit_rate; /* Bit rate for sending audio */
 | 
			
		||||
 | 
			
		||||
    uint32_t vin_idx, vout_idx; /* Video device index, or -1 if not open */
 | 
			
		||||
    uint32_t video_width, video_height;
 | 
			
		||||
    uint32_t video_bit_rate; /* Bit rate for sending video; 0 for no video */
 | 
			
		||||
} Call;
 | 
			
		||||
 | 
			
		||||
struct CallControl {
 | 
			
		||||
@@ -65,29 +76,34 @@ struct CallControl {
 | 
			
		||||
    ToxAV *av;
 | 
			
		||||
    ToxWindow *prompt;
 | 
			
		||||
 | 
			
		||||
    Call calls[MAX_CALLS];
 | 
			
		||||
    uint32_t call_state;
 | 
			
		||||
    bool pending_call;
 | 
			
		||||
    Call *calls;
 | 
			
		||||
    uint32_t max_calls;
 | 
			
		||||
 | 
			
		||||
    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 default_audio_bit_rate;
 | 
			
		||||
 | 
			
		||||
    uint32_t video_bit_rate;
 | 
			
		||||
    int32_t video_frame_duration;
 | 
			
		||||
    uint32_t default_video_width, default_video_height;
 | 
			
		||||
    uint32_t default_video_bit_rate;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} CallControl;
 | 
			
		||||
 | 
			
		||||
struct CallControl CallControl;
 | 
			
		||||
extern struct CallControl CallControl;
 | 
			
		||||
 | 
			
		||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
 | 
			
		||||
ToxAV *init_audio(ToxWindow *self, Tox *tox);
 | 
			
		||||
void terminate_audio();
 | 
			
		||||
int start_transmission(ToxWindow *self, Call *call);
 | 
			
		||||
int stop_transmission(Call *call, uint32_t friend_number);
 | 
			
		||||
void terminate_audio(void);
 | 
			
		||||
 | 
			
		||||
bool init_call(Call *call);
 | 
			
		||||
 | 
			
		||||
void place_call(ToxWindow *self);
 | 
			
		||||
void stop_current_call(ToxWindow *self);
 | 
			
		||||
 | 
			
		||||
void init_friend_AV(uint32_t index);
 | 
			
		||||
void del_friend_AV(uint32_t index);
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO_CALL_H */
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -30,8 +30,9 @@
 | 
			
		||||
#define AUDIO_DEVICE_H
 | 
			
		||||
 | 
			
		||||
#define OPENAL_BUFS 5
 | 
			
		||||
#define MAX_OPENAL_DEVICES 32
 | 
			
		||||
#define MAX_DEVICES 32
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
typedef enum DeviceType {
 | 
			
		||||
@@ -52,40 +53,45 @@ 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);
 | 
			
		||||
#else
 | 
			
		||||
DeviceError init_devices();
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
DeviceError init_devices(void);
 | 
			
		||||
 | 
			
		||||
DeviceError terminate_devices();
 | 
			
		||||
 | 
			
		||||
/* Callback handles ready data from INPUT device */
 | 
			
		||||
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
 | 
			
		||||
void* get_device_callback_data(uint32_t device_idx);
 | 
			
		||||
void get_al_device_names(void);
 | 
			
		||||
DeviceError terminate_devices(void);
 | 
			
		||||
 | 
			
		||||
/* toggle device mute */
 | 
			
		||||
DeviceError device_mute(DeviceType type, uint32_t device_idx);
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
 | 
			
		||||
#endif
 | 
			
		||||
bool device_is_muted(DeviceType type, uint32_t device_idx);
 | 
			
		||||
 | 
			
		||||
DeviceError device_set_VAD_threshold(uint32_t device_idx, float value);
 | 
			
		||||
 | 
			
		||||
float device_get_VAD_threshold(uint32_t device_idx);
 | 
			
		||||
 | 
			
		||||
DeviceError set_source_position(uint32_t device_idx, float x, float y, float z);
 | 
			
		||||
 | 
			
		||||
DeviceError set_al_device(DeviceType type, int32_t selection);
 | 
			
		||||
 | 
			
		||||
DeviceError set_primary_device(DeviceType type, int32_t selection);
 | 
			
		||||
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, 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_input_device(uint32_t *device_idx,
 | 
			
		||||
                              DataHandleCallback cb, void *cb_data, bool enable_VAD,
 | 
			
		||||
                              uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
 | 
			
		||||
DeviceError open_output_device(uint32_t *device_idx,
 | 
			
		||||
                               uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
 | 
			
		||||
 | 
			
		||||
/* Stop device */
 | 
			
		||||
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);
 | 
			
		||||
/* Write data to output device */
 | 
			
		||||
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
 | 
			
		||||
                      uint32_t sample_rate);
 | 
			
		||||
 | 
			
		||||
void print_devices(ToxWindow* self, DeviceType type);
 | 
			
		||||
void get_primary_device_name(DeviceType type, char *buf, int size);
 | 
			
		||||
/* return current input volume as float in range 0.0-100.0 */
 | 
			
		||||
float get_input_volume(void);
 | 
			
		||||
 | 
			
		||||
void print_al_devices(ToxWindow *self, DeviceType type);
 | 
			
		||||
 | 
			
		||||
DeviceError selection_valid(DeviceType type, int32_t selection);
 | 
			
		||||
#endif /* AUDIO_DEVICE_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -20,110 +20,120 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#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>
 | 
			
		||||
#endif /* ifdef __APPLE__ */
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#endif /* __APPLE__ */
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "configdir.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size)
 | 
			
		||||
static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matches)
 | 
			
		||||
{
 | 
			
		||||
    if (m)
 | 
			
		||||
    if (m) {
 | 
			
		||||
        execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *L = (char *) list;
 | 
			
		||||
    int i;
 | 
			
		||||
    for (size_t i = 0; i < n_matches; ++i) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", list[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < n_items; ++i)
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]);
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");   /* formatting */
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* puts match in match buffer. if more than one match, add first n chars that are identical.
 | 
			
		||||
   e.g. if matches contains: [foo, foobar, foe] we put fo in 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, const char **matches, size_t n_items,
 | 
			
		||||
                            size_t max_size)
 | 
			
		||||
{
 | 
			
		||||
    if (n == 1) {
 | 
			
		||||
        strcpy(match, matches[0]);
 | 
			
		||||
        return;
 | 
			
		||||
    UNUSED_VAR(self);
 | 
			
		||||
 | 
			
		||||
    if (n_items == 1) {
 | 
			
		||||
        return snprintf(match, match_sz, "%s", matches[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_STR_SIZE; ++i) {
 | 
			
		||||
    for (size_t i = 0; i < max_size; ++i) {
 | 
			
		||||
        char ch1 = matches[0][i];
 | 
			
		||||
        int j;
 | 
			
		||||
 | 
			
		||||
        for (j = 0; j < n; ++j) {
 | 
			
		||||
        for (size_t j = 0; j < n_items; ++j) {
 | 
			
		||||
            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,
 | 
			
		||||
   then fills line with the complete word. e.g. "Hello jo" would complete the line
 | 
			
		||||
   with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
 | 
			
		||||
 | 
			
		||||
   list is a pointer to the list of strings being compared, n_items is the number of items
 | 
			
		||||
   in the list, and size is the size of each item in the list.
 | 
			
		||||
 | 
			
		||||
   Returns the difference between the old len and new len of line on success, -1 if error */
 | 
			
		||||
int complete_line(ToxWindow *self, const void *list, int n_items, int size)
 | 
			
		||||
/*
 | 
			
		||||
 * Looks for all instances in list that begin with the last entered word in line according to pos,
 | 
			
		||||
 * then fills line with the complete word. e.g. "Hello jo" would complete the line
 | 
			
		||||
 * with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
 | 
			
		||||
 *
 | 
			
		||||
 * `list` is a pointer to `n_items` strings. Each string in the list must be <= MAX_STR_SIZE.
 | 
			
		||||
 *
 | 
			
		||||
 * dir_search should be true if the line being completed is a file path.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the difference between the old len and new len of line on success.
 | 
			
		||||
 * Returns -1 on error.
 | 
			
		||||
 *
 | 
			
		||||
 * Note: This function should not be called directly. Use complete_line() and complete_path() instead.
 | 
			
		||||
 */
 | 
			
		||||
static int complete_line_helper(ToxWindow *self, const char **list, const size_t n_items, bool dir_search)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
 | 
			
		||||
    if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ctx->len >= MAX_STR_SIZE) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *L = (char *) list;
 | 
			
		||||
    const char *endchrs = " ";
 | 
			
		||||
    char ubuf[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    /* work with multibyte string copy of buf for simplicity */
 | 
			
		||||
    if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
 | 
			
		||||
    if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    /* TODO: generalize this */
 | 
			
		||||
    bool dir_search =    !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
 | 
			
		||||
                      || !strncmp(ubuf, "/avatar", strlen("/avatar"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* isolate substring from space behind pos to pos */
 | 
			
		||||
    char tmp[MAX_STR_SIZE];
 | 
			
		||||
    snprintf(tmp, sizeof(tmp), "%s", ubuf);
 | 
			
		||||
    tmp[ctx->pos] = '\0';
 | 
			
		||||
    memcpy(tmp, ubuf, ctx->pos);
 | 
			
		||||
    tmp[ctx->pos] = 0;
 | 
			
		||||
 | 
			
		||||
    const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
 | 
			
		||||
    const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' ');
 | 
			
		||||
    char *sub = calloc(1, strlen(ubuf) + 1);
 | 
			
		||||
 | 
			
		||||
    if (sub == NULL)
 | 
			
		||||
        exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
 | 
			
		||||
    if (sub == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in complete_line_helper", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!s && !dir_search) {
 | 
			
		||||
        strcpy(sub, tmp);
 | 
			
		||||
 | 
			
		||||
        if (sub[0] != '/')
 | 
			
		||||
        if (sub[0] != '/') {
 | 
			
		||||
            endchrs = ": ";
 | 
			
		||||
        }
 | 
			
		||||
    } else if (s) {
 | 
			
		||||
        strcpy(sub, &s[1]);
 | 
			
		||||
 | 
			
		||||
@@ -131,47 +141,60 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
 | 
			
		||||
            int sub_len = strlen(sub);
 | 
			
		||||
            int si = char_rfind(sub, '/', sub_len);
 | 
			
		||||
 | 
			
		||||
            if (si || *sub == '/')
 | 
			
		||||
            if (si || *sub == '/') {
 | 
			
		||||
                memmove(sub, &sub[si + 1], sub_len - si);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!sub[0]) {
 | 
			
		||||
    if (!sub[0] && !(dir_search && n_items == 1)) {
 | 
			
		||||
        free(sub);
 | 
			
		||||
        return -1;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int s_len = strlen(sub);
 | 
			
		||||
    int n_matches = 0;
 | 
			
		||||
    char matches[n_items][MAX_STR_SIZE];
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    size_t n_matches = 0;
 | 
			
		||||
 | 
			
		||||
    char **matches = (char **) malloc_ptr_array(n_items, MAX_STR_SIZE);
 | 
			
		||||
 | 
			
		||||
    if (matches == NULL) {
 | 
			
		||||
        free(sub);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* put all list matches in matches array */
 | 
			
		||||
    for (i = 0; i < n_items; ++i) {
 | 
			
		||||
        char str[MAX_CMDNAME_SIZE + 1];
 | 
			
		||||
        snprintf(str, sizeof(str), "%s", &L[i * size]);
 | 
			
		||||
 | 
			
		||||
        if (strncasecmp(str, sub, s_len) == 0)
 | 
			
		||||
            strcpy(matches[n_matches++], str);
 | 
			
		||||
    for (size_t i = 0; i < n_items; ++i) {
 | 
			
		||||
        if (strncasecmp(list[i], sub, s_len) == 0) {
 | 
			
		||||
            snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(sub);
 | 
			
		||||
 | 
			
		||||
    if (!n_matches)
 | 
			
		||||
    if (!n_matches) {
 | 
			
		||||
        free_ptr_array((void **) matches);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!dir_search && n_matches > 1)
 | 
			
		||||
        print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
 | 
			
		||||
    if (!dir_search && n_matches > 1) {
 | 
			
		||||
        print_ac_matches(self, NULL, matches, n_matches);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char match[MAX_STR_SIZE];
 | 
			
		||||
    get_str_match(self, match, matches, n_matches);
 | 
			
		||||
    size_t match_len = strlen(match);
 | 
			
		||||
    size_t match_len = get_str_match(self, match, sizeof(match), (const char **) matches, n_matches, MAX_STR_SIZE);
 | 
			
		||||
 | 
			
		||||
    free_ptr_array((void **) matches);
 | 
			
		||||
 | 
			
		||||
    if (match_len == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dir_search) {
 | 
			
		||||
        if (n_matches == 1)
 | 
			
		||||
            endchrs = char_rfind(match, '.', match_len) ? "\"" : "/";
 | 
			
		||||
        else
 | 
			
		||||
        if (n_matches == 1) {
 | 
			
		||||
            endchrs = char_rfind(match, '.', match_len) ? "" : "/";
 | 
			
		||||
        } else {
 | 
			
		||||
            endchrs = "";
 | 
			
		||||
        }
 | 
			
		||||
    } else if (n_matches > 1) {
 | 
			
		||||
        endchrs = "";
 | 
			
		||||
    }
 | 
			
		||||
@@ -181,24 +204,52 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
 | 
			
		||||
    int strt = ctx->pos - s_len;
 | 
			
		||||
    int diff = match_len - s_len + n_endchrs;
 | 
			
		||||
 | 
			
		||||
    if (ctx->len + diff >= MAX_STR_SIZE)
 | 
			
		||||
    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))
 | 
			
		||||
    if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    strcpy(&ubuf[strt], match);
 | 
			
		||||
 | 
			
		||||
    /* If path points to a file with no extension don't append a forward slash */
 | 
			
		||||
    if (dir_search && *endchrs == '/') {
 | 
			
		||||
        const char *path_start = strchr(ubuf + 1, '/');
 | 
			
		||||
 | 
			
		||||
        if (!path_start) {
 | 
			
		||||
            path_start = strchr(ubuf + 1, ' ');
 | 
			
		||||
 | 
			
		||||
            if (!path_start) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (strlen(path_start) < 2) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ++path_start;
 | 
			
		||||
 | 
			
		||||
        if (file_type(path_start) == FILE_TYPE_REGULAR) {
 | 
			
		||||
            endchrs = "";
 | 
			
		||||
            diff -= n_endchrs;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    strcpy(&ubuf[strt + match_len], endchrs);
 | 
			
		||||
    strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
 | 
			
		||||
 | 
			
		||||
    /* convert to widechar and copy back to original buf */
 | 
			
		||||
    wchar_t newbuf[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1)
 | 
			
		||||
    if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wcscpy(ctx->line, newbuf);
 | 
			
		||||
 | 
			
		||||
@@ -208,54 +259,82 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
 | 
			
		||||
    return diff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
 | 
			
		||||
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
 | 
			
		||||
int complete_line(ToxWindow *self, const char **list, size_t n_items)
 | 
			
		||||
{
 | 
			
		||||
    return complete_line_helper(self, list, n_items, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int complete_path(ToxWindow *self, const char **list, const size_t n_items)
 | 
			
		||||
{
 | 
			
		||||
    return complete_line_helper(self, list, n_items, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Transforms a tab complete starting with the shorthand "~" into the full home directory. */
 | 
			
		||||
static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    char homedir[MAX_STR_SIZE] = {0};
 | 
			
		||||
    get_home_dir(homedir, sizeof(homedir));
 | 
			
		||||
 | 
			
		||||
    char newline[MAX_STR_SIZE];
 | 
			
		||||
    snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
 | 
			
		||||
    snprintf(path, pathsize, "%s", &newline[cmdlen]);
 | 
			
		||||
    char newline[MAX_STR_SIZE + 1];
 | 
			
		||||
    snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1);
 | 
			
		||||
    snprintf(path, pathsize, "%s", &newline[cmdlen - 1]);
 | 
			
		||||
 | 
			
		||||
    wchar_t wline[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
 | 
			
		||||
    if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int newlen = wcslen(wline);
 | 
			
		||||
 | 
			
		||||
    if (ctx->len + newlen >= MAX_STR_SIZE)
 | 
			
		||||
    if (ctx->len + newlen >= MAX_STR_SIZE) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wmemcpy(ctx->line, wline, newlen + 1);
 | 
			
		||||
    ctx->pos = newlen;
 | 
			
		||||
    ctx->len = ctx->pos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*  attempts to match /command "<incomplete-dir>" line to matching directories.
 | 
			
		||||
/*
 | 
			
		||||
 * Return true if the first `p_len` chars in `s` are equal to `p` and `s` is a valid directory name.
 | 
			
		||||
 */
 | 
			
		||||
static bool is_partial_match(const char *s, const char *p, size_t p_len)
 | 
			
		||||
{
 | 
			
		||||
    if (s == NULL || p == NULL) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if only one match, auto-complete line.
 | 
			
		||||
    return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
 | 
			
		||||
#define MAX_DIRS 512
 | 
			
		||||
    return strncmp(s, p, p_len) == 0 && strcmp(".", s) != 0 && strcmp("..", s) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
 | 
			
		||||
 * If there is only one match the line is auto-completed.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the diff between old len and new len of ctx->line on success.
 | 
			
		||||
 * Returns -1 if no matches or more than one match.
 | 
			
		||||
 */
 | 
			
		||||
#define MAX_DIRS 75
 | 
			
		||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
 | 
			
		||||
{
 | 
			
		||||
    char b_path[MAX_STR_SIZE];
 | 
			
		||||
    char b_name[MAX_STR_SIZE];
 | 
			
		||||
    char b_path[MAX_STR_SIZE + 1];
 | 
			
		||||
    char b_name[MAX_STR_SIZE + 1];
 | 
			
		||||
    char b_cmd[MAX_STR_SIZE];
 | 
			
		||||
    const wchar_t *tmpline = &line[wcslen(cmd) + 2];   /* start after "/command \"" */
 | 
			
		||||
    const wchar_t *tmpline = &line[wcslen(cmd) + 1];   /* start after "/command " */
 | 
			
		||||
 | 
			
		||||
    if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
 | 
			
		||||
    if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path) - 1) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1)
 | 
			
		||||
    if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (b_path[0] == '~')
 | 
			
		||||
        complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
 | 
			
		||||
    if (b_path[0] == '~') {
 | 
			
		||||
        complete_home_dir(self, b_path, sizeof(b_path) - 1, b_cmd, strlen(b_cmd) + 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int si = char_rfind(b_path, '/', strlen(b_path));
 | 
			
		||||
 | 
			
		||||
@@ -263,40 +342,52 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
 | 
			
		||||
        b_path[0] = '.';
 | 
			
		||||
        b_path[1] = '\0';
 | 
			
		||||
    } else if (!si && b_path[0] != '/') {    /* look for matches in pwd */
 | 
			
		||||
        char tmp[MAX_STR_SIZE];
 | 
			
		||||
        snprintf(tmp, sizeof(tmp), ".%s", b_path);
 | 
			
		||||
        snprintf(b_path, sizeof(b_path), "%s", tmp);
 | 
			
		||||
        memmove(b_path + 1, b_path, sizeof(b_path) - 1);
 | 
			
		||||
        b_path[0] = '.';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
 | 
			
		||||
    b_path[si + 1] = '\0';
 | 
			
		||||
    int b_name_len = strlen(b_name);
 | 
			
		||||
    size_t b_name_len = strlen(b_name);
 | 
			
		||||
    DIR *dp = opendir(b_path);
 | 
			
		||||
 | 
			
		||||
    if (dp == NULL)
 | 
			
		||||
    if (dp == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char **dirnames = (char **) malloc_ptr_array(MAX_DIRS, NAME_MAX + 1);
 | 
			
		||||
 | 
			
		||||
    if (dirnames == NULL) {
 | 
			
		||||
        closedir(dp);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char dirnames[MAX_DIRS][NAME_MAX];
 | 
			
		||||
    struct dirent *entry;
 | 
			
		||||
 | 
			
		||||
    int dircount = 0;
 | 
			
		||||
 | 
			
		||||
    while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
 | 
			
		||||
        if (strncmp(entry->d_name, b_name, b_name_len) == 0
 | 
			
		||||
                                && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
 | 
			
		||||
            snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
 | 
			
		||||
        if (is_partial_match(entry->d_name, b_name, b_name_len)) {
 | 
			
		||||
            snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name);
 | 
			
		||||
            ++dircount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closedir(dp);
 | 
			
		||||
 | 
			
		||||
    if (dircount == 0)
 | 
			
		||||
    if (dircount == 0) {
 | 
			
		||||
        free_ptr_array((void **) dirnames);
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if (dircount > 1) {
 | 
			
		||||
        qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
 | 
			
		||||
        print_matches(self, m, dirnames, dircount, NAME_MAX);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return complete_line(self, dirnames, dircount, NAME_MAX);
 | 
			
		||||
    if (dircount > 1) {
 | 
			
		||||
        qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper);
 | 
			
		||||
        print_ac_matches(self, m, dirnames, dircount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ret = complete_path(self, (const char **) dirnames, dircount);
 | 
			
		||||
 | 
			
		||||
    free_ptr_array((void **) dirnames);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,20 +23,30 @@
 | 
			
		||||
#ifndef AUTOCOMPLETE_H
 | 
			
		||||
#define AUTOCOMPLETE_H
 | 
			
		||||
 | 
			
		||||
/* looks for all instances in list that begin with the last entered word in line according to pos,
 | 
			
		||||
   then fills line with the complete word. e.g. "Hello jo" would complete the line
 | 
			
		||||
   with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
   list is a pointer to the list of strings being compared, n_items is the number of items
 | 
			
		||||
   in the list, and size is the size of each item in the list.
 | 
			
		||||
/*
 | 
			
		||||
 * Looks for all instances in list that begin with the last entered word in line according to pos,
 | 
			
		||||
 * then fills line with the complete word. e.g. "Hello jo" would complete the line
 | 
			
		||||
 * with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
 | 
			
		||||
 *
 | 
			
		||||
* `list` is a pointer to `n_items` strings.
 | 
			
		||||
 *
 | 
			
		||||
 * dir_search should be true if the line being completed is a file path.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the difference between the old len and new len of line on success.
 | 
			
		||||
 * Returns -1 on error.
 | 
			
		||||
 *
 | 
			
		||||
 * Note: This function should not be called directly. Use complete_line() and complete_path() instead.
 | 
			
		||||
 */
 | 
			
		||||
int complete_line(ToxWindow *self, const char **list, size_t n_items);
 | 
			
		||||
 | 
			
		||||
   Returns the difference between the old len and new len of line on success, -1 if error */
 | 
			
		||||
int complete_line(ToxWindow *self, const void *list, int n_items, int size);
 | 
			
		||||
 | 
			
		||||
/*  attempts to match /command "<incomplete-dir>" line to matching directories.
 | 
			
		||||
 | 
			
		||||
    if only one match, auto-complete line.
 | 
			
		||||
    return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
 | 
			
		||||
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
 | 
			
		||||
 * If there is only one match the line is auto-completed.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the diff between old len and new len of ctx->line on success.
 | 
			
		||||
 * Returns -1 if no matches or more than one match.
 | 
			
		||||
 */
 | 
			
		||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
 | 
			
		||||
 | 
			
		||||
#endif  /* #define AUTOCOMPLETE_H */
 | 
			
		||||
#endif /* AUTOCOMPLETE_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										117
									
								
								src/avatars.c
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								src/avatars.c
									
									
									
									
									
								
							@@ -20,14 +20,14 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "avatars.h"
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "avatars.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
 | 
			
		||||
extern FriendsList Friends;
 | 
			
		||||
 | 
			
		||||
@@ -39,39 +39,76 @@ static struct Avatar {
 | 
			
		||||
    off_t size;
 | 
			
		||||
} Avatar;
 | 
			
		||||
 | 
			
		||||
/* Compares the first size bytes of fp to signature.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 if they are the same
 | 
			
		||||
 * Returns 1 if they differ
 | 
			
		||||
 * Returns -1 on error.
 | 
			
		||||
 *
 | 
			
		||||
 * On success this function will seek back to the beginning of fp.
 | 
			
		||||
 */
 | 
			
		||||
static int check_file_signature(const unsigned char *signature, size_t size, FILE *fp)
 | 
			
		||||
{
 | 
			
		||||
    char *buf = malloc(size);
 | 
			
		||||
 | 
			
		||||
    if (buf == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fread(buf, size, 1, fp) != 1) {
 | 
			
		||||
        free(buf);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ret = memcmp(signature, buf, size);
 | 
			
		||||
 | 
			
		||||
    free(buf);
 | 
			
		||||
 | 
			
		||||
    if (fseek(fp, 0L, SEEK_SET) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret == 0 ? 0 : 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void avatar_clear(void)
 | 
			
		||||
{
 | 
			
		||||
    memset(&Avatar, 0, sizeof(struct Avatar));
 | 
			
		||||
    Avatar = (struct Avatar) {
 | 
			
		||||
        0
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Sends avatar to friendnum.
 | 
			
		||||
/* Sends avatar to friendnumber.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int avatar_send(Tox *m, uint32_t friendnum)
 | 
			
		||||
int avatar_send(Tox *m, uint32_t friendnumber)
 | 
			
		||||
{
 | 
			
		||||
    TOX_ERR_FILE_SEND err;
 | 
			
		||||
    uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
 | 
			
		||||
    Tox_Err_File_Send err;
 | 
			
		||||
    uint32_t filenumber = tox_file_send(m, friendnumber, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
 | 
			
		||||
                                        NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
 | 
			
		||||
    if (Avatar.size == 0)
 | 
			
		||||
 | 
			
		||||
    if (Avatar.size == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_FILE_SEND_OK) {
 | 
			
		||||
        fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
 | 
			
		||||
        fprintf(stderr, "tox_file_send failed for friendnumber %u (error %d)\n", friendnumber, err);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
 | 
			
		||||
    struct FileTransfer *ft = new_file_transfer(NULL, friendnumber, filenumber, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
 | 
			
		||||
 | 
			
		||||
    if (!ft)
 | 
			
		||||
    if (!ft) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ft->file = fopen(Avatar.path, "r");
 | 
			
		||||
 | 
			
		||||
    if (ft->file == NULL)
 | 
			
		||||
    if (ft->file == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
 | 
			
		||||
    ft->file_size = Avatar.size;
 | 
			
		||||
@@ -82,12 +119,11 @@ int avatar_send(Tox *m, uint32_t friendnum)
 | 
			
		||||
/* Sends avatar to all friends */
 | 
			
		||||
static void avatar_send_all(Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < Friends.max_idx; ++i) {
 | 
			
		||||
        if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
 | 
			
		||||
    for (size_t i = 0; i < Friends.max_idx; ++i) {
 | 
			
		||||
        if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) {
 | 
			
		||||
            avatar_send(m, Friends.list[i].num);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Sets avatar to path and sends it to all online contacts.
 | 
			
		||||
@@ -97,15 +133,17 @@ static void avatar_send_all(Tox *m)
 | 
			
		||||
 */
 | 
			
		||||
int avatar_set(Tox *m, const char *path, size_t path_len)
 | 
			
		||||
{
 | 
			
		||||
    if (path_len == 0 || path_len >= sizeof(Avatar.path))
 | 
			
		||||
    if (path_len == 0 || path_len >= sizeof(Avatar.path)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FILE *fp = fopen(path, "rb");
 | 
			
		||||
 | 
			
		||||
    if (fp == NULL)
 | 
			
		||||
    if (fp == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
 | 
			
		||||
    unsigned char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
 | 
			
		||||
 | 
			
		||||
    if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
@@ -116,8 +154,9 @@ int avatar_set(Tox *m, const char *path, size_t path_len)
 | 
			
		||||
 | 
			
		||||
    off_t size = file_size(path);
 | 
			
		||||
 | 
			
		||||
    if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
 | 
			
		||||
    if (size == 0 || size > MAX_AVATAR_FILE_SIZE) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get_file_name(Avatar.name, sizeof(Avatar.name), path);
 | 
			
		||||
    Avatar.name_len = strlen(Avatar.name);
 | 
			
		||||
@@ -141,7 +180,14 @@ void avatar_unset(Tox *m)
 | 
			
		||||
    avatar_send_all(m);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
 | 
			
		||||
void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status)
 | 
			
		||||
{
 | 
			
		||||
    if (connection_status == TOX_CONNECTION_NONE) {
 | 
			
		||||
        kill_avatar_file_transfers_friend(m, friendnumber);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control)
 | 
			
		||||
{
 | 
			
		||||
    switch (control) {
 | 
			
		||||
        case TOX_FILE_CONTROL_RESUME:
 | 
			
		||||
@@ -150,6 +196,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:
 | 
			
		||||
@@ -164,8 +211,9 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co
 | 
			
		||||
 | 
			
		||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
 | 
			
		||||
{
 | 
			
		||||
    if (ft->state != FILE_TRANSFER_STARTED)
 | 
			
		||||
    if (ft->state != FILE_TRANSFER_STARTED) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (length == 0) {
 | 
			
		||||
        close_file_transfer(NULL, m, ft, -1, NULL, silent);
 | 
			
		||||
@@ -186,20 +234,29 @@ void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position,
 | 
			
		||||
        ft->position = position;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t send_data[length];
 | 
			
		||||
    size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
 | 
			
		||||
    uint8_t *send_data = malloc(length);
 | 
			
		||||
 | 
			
		||||
    if (send_length != length) {
 | 
			
		||||
    if (send_data == NULL) {
 | 
			
		||||
        close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_FILE_SEND_CHUNK err;
 | 
			
		||||
    tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
 | 
			
		||||
    size_t send_length = fread(send_data, 1, length, ft->file);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
 | 
			
		||||
    if (send_length != length) {
 | 
			
		||||
        close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
 | 
			
		||||
        free(send_data);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Tox_Err_File_Send_Chunk err;
 | 
			
		||||
    tox_file_send_chunk(m, ft->friendnumber, ft->filenumber, position, send_data, send_length, &err);
 | 
			
		||||
 | 
			
		||||
    free(send_data);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_FILE_SEND_CHUNK_OK) {
 | 
			
		||||
        fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ft->position += send_length;
 | 
			
		||||
    ft->last_keep_alive = get_unix_time();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,8 @@
 | 
			
		||||
#ifndef AVATARS_H
 | 
			
		||||
#define AVATARS_H
 | 
			
		||||
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_AVATAR_FILE_SIZE 65536
 | 
			
		||||
 | 
			
		||||
/* Sends avatar to friendnum.
 | 
			
		||||
@@ -47,6 +49,7 @@ int avatar_set(Tox *m, const char *path, size_t length);
 | 
			
		||||
void avatar_unset(Tox *m);
 | 
			
		||||
 | 
			
		||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
 | 
			
		||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
 | 
			
		||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control);
 | 
			
		||||
void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status);
 | 
			
		||||
 | 
			
		||||
#endif /* AVATARS_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										635
									
								
								src/bootstrap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										635
									
								
								src/bootstrap.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,635 @@
 | 
			
		||||
/*  bootstrap.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
#include <tox/tox.h>
 | 
			
		||||
 | 
			
		||||
#include "configdir.h"
 | 
			
		||||
#include "curl_util.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "prompt.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
extern struct arg_opts arg_opts;
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
extern struct Winthread Winthread;
 | 
			
		||||
 | 
			
		||||
/* URL that we get the JSON encoded nodes list from. */
 | 
			
		||||
#define NODES_LIST_URL "https://nodes.tox.chat/json"
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_NODES_FILENAME "DHTnodes.json"
 | 
			
		||||
 | 
			
		||||
/* Time to wait between bootstrap attempts */
 | 
			
		||||
#define TRY_BOOTSTRAP_INTERVAL 5
 | 
			
		||||
 | 
			
		||||
/* Number of nodes to bootstrap to per try */
 | 
			
		||||
#define NUM_BOOTSTRAP_NODES 5
 | 
			
		||||
 | 
			
		||||
/* Number of seconds since last successful ping before we consider a node offline */
 | 
			
		||||
#define NODE_OFFLINE_TIMOUT (60*60*24*2)
 | 
			
		||||
 | 
			
		||||
#define IP_MAX_SIZE 45
 | 
			
		||||
#define IP_MIN_SIZE 7
 | 
			
		||||
#define PORT_MAX_SIZE 5
 | 
			
		||||
 | 
			
		||||
#define LAST_SCAN_JSON_KEY "\"last_scan\":"
 | 
			
		||||
#define LAST_SCAN_JSON_KEY_LEN (sizeof(LAST_SCAN_JSON_KEY) - 1)
 | 
			
		||||
 | 
			
		||||
#define IPV4_JSON_KEY "\"ipv4\":\""
 | 
			
		||||
#define IPV4_JSON_KEY_LEN (sizeof(IPV4_JSON_KEY) - 1)
 | 
			
		||||
 | 
			
		||||
#define IPV6_JSON_KEY "\"ipv6\":\""
 | 
			
		||||
#define IPV6_JSON_KEY_LEN (sizeof(IPV6_JSON_KEY) - 1)
 | 
			
		||||
 | 
			
		||||
#define PORT_JSON_KEY "\"port\":"
 | 
			
		||||
#define PORT_JSON_KEY_LEN (sizeof(PORT_JSON_KEY) - 1)
 | 
			
		||||
 | 
			
		||||
#define PK_JSON_KEY "\"public_key\":\""
 | 
			
		||||
#define PK_JSON_KEY_LEN (sizeof(PK_JSON_KEY) - 1)
 | 
			
		||||
 | 
			
		||||
#define LAST_PING_JSON_KEY "\"last_ping\":"
 | 
			
		||||
#define LAST_PING_JSON_KEY_LEN (sizeof(LAST_PING_JSON_KEY) - 1)
 | 
			
		||||
 | 
			
		||||
/* Maximum allowable size of the nodes list */
 | 
			
		||||
#define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct Thread_Data {
 | 
			
		||||
    pthread_t tid;
 | 
			
		||||
    pthread_attr_t attr;
 | 
			
		||||
    pthread_mutex_t lock;
 | 
			
		||||
    volatile bool active;
 | 
			
		||||
} thread_data;
 | 
			
		||||
 | 
			
		||||
#define MAX_NODES 50
 | 
			
		||||
struct Node {
 | 
			
		||||
    char ip4[IP_MAX_SIZE + 1];
 | 
			
		||||
    bool have_ip4;
 | 
			
		||||
 | 
			
		||||
    char ip6[IP_MAX_SIZE + 1];
 | 
			
		||||
    bool have_ip6;
 | 
			
		||||
 | 
			
		||||
    char key[TOX_PUBLIC_KEY_SIZE];
 | 
			
		||||
    uint16_t port;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct DHT_Nodes {
 | 
			
		||||
    struct Node list[MAX_NODES];
 | 
			
		||||
    size_t count;
 | 
			
		||||
    time_t last_updated;
 | 
			
		||||
} Nodes;
 | 
			
		||||
 | 
			
		||||
/* Return true if address appears to be a valid ipv4 address. */
 | 
			
		||||
static bool is_ip4_address(const char *address)
 | 
			
		||||
{
 | 
			
		||||
    struct sockaddr_in s_addr;
 | 
			
		||||
    return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if address roughly appears to be a valid ipv6 address.
 | 
			
		||||
 *
 | 
			
		||||
 * TODO: Improve this function (inet_pton behaves strangely with ipv6).
 | 
			
		||||
 * for now the only guarantee is that it won't return true if the
 | 
			
		||||
 * address is a domain or ipv4 address, and should only be used if you're
 | 
			
		||||
 * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
 | 
			
		||||
 */
 | 
			
		||||
static bool is_ip6_address(const char *address)
 | 
			
		||||
{
 | 
			
		||||
    size_t num_colons = 0;
 | 
			
		||||
    char ch = 0;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; (ch = address[i]); ++i) {
 | 
			
		||||
        if (ch == '.') {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ch == ':') {
 | 
			
		||||
            ++num_colons;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return num_colons > 1 && num_colons < 8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Determine if a node is offline by comparing the age of the nodeslist
 | 
			
		||||
 * to the last time the node was successfully pinged.
 | 
			
		||||
 */
 | 
			
		||||
static bool node_is_offline(unsigned long long int last_ping)
 | 
			
		||||
{
 | 
			
		||||
    return last_ping + NODE_OFFLINE_TIMOUT <= last_ping;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if nodeslist pointed to by fp needs to be updated.
 | 
			
		||||
 * This will be the case if the file is empty, has an invalid format,
 | 
			
		||||
 * or if the file is older than the given timeout.
 | 
			
		||||
 */
 | 
			
		||||
static bool nodeslist_needs_update(const char *nodes_path)
 | 
			
		||||
{
 | 
			
		||||
    if (user_settings->nodeslist_update_freq <= 0) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FILE *fp = fopen(nodes_path, "r+");
 | 
			
		||||
 | 
			
		||||
    if (fp == NULL) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* last_scan value should be at beginning of file */
 | 
			
		||||
    char line[LAST_SCAN_JSON_KEY_LEN + 32];
 | 
			
		||||
 | 
			
		||||
    if (fgets(line, sizeof(line), fp) == NULL) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
 | 
			
		||||
    const char *last_scan_val = strstr(line, LAST_SCAN_JSON_KEY);
 | 
			
		||||
 | 
			
		||||
    if (last_scan_val == NULL) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long long int last_scan = strtoll(last_scan_val + LAST_SCAN_JSON_KEY_LEN, NULL, 10);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&thread_data.lock);
 | 
			
		||||
    Nodes.last_updated = last_scan;
 | 
			
		||||
    pthread_mutex_unlock(&thread_data.lock);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    bool is_timeout = timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60);
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    if (is_timeout) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Fetches the JSON encoded DHT nodeslist from NODES_LIST_URL.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data)
 | 
			
		||||
{
 | 
			
		||||
    CURL *c_handle = curl_easy_init();
 | 
			
		||||
 | 
			
		||||
    if (c_handle == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int err = -1;
 | 
			
		||||
 | 
			
		||||
    struct curl_slist *headers = NULL;
 | 
			
		||||
    headers = curl_slist_append(headers, "Content-Type: application/json");
 | 
			
		||||
    headers = curl_slist_append(headers, "charsets: utf-8");
 | 
			
		||||
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_URL, NODES_LIST_URL);
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data);
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data);
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L);
 | 
			
		||||
 | 
			
		||||
    int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
 | 
			
		||||
 | 
			
		||||
    if (proxy_ret != 0) {
 | 
			
		||||
        fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret);
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret);
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret);
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_perform(c_handle);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        /* If system doesn't support any of the specified ciphers suites, fall back to default */
 | 
			
		||||
        if (ret == CURLE_SSL_CIPHER) {
 | 
			
		||||
            curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
 | 
			
		||||
            ret = curl_easy_perform(c_handle);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ret != CURLE_OK) {
 | 
			
		||||
            fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret);
 | 
			
		||||
            goto on_exit;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    err = 0;
 | 
			
		||||
 | 
			
		||||
on_exit:
 | 
			
		||||
    curl_slist_free_all(headers);
 | 
			
		||||
    curl_easy_cleanup(c_handle);
 | 
			
		||||
    return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Attempts to update the DHT nodeslist.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 1 if list was updated successfully.
 | 
			
		||||
 * Return 0 if list does not need to be updated.
 | 
			
		||||
 * Return -1 if file cannot be opened.
 | 
			
		||||
 * Return -2 if http lookup failed.
 | 
			
		||||
 * Return -3 if http reponse was empty.
 | 
			
		||||
 * Return -4 if data could not be written to disk.
 | 
			
		||||
 * Return -5 if memory allocation fails.
 | 
			
		||||
 */
 | 
			
		||||
static int update_DHT_nodeslist(const char *nodes_path)
 | 
			
		||||
{
 | 
			
		||||
    if (!nodeslist_needs_update(nodes_path)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FILE *fp = fopen(nodes_path, "r+");
 | 
			
		||||
 | 
			
		||||
    if (fp == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data));
 | 
			
		||||
 | 
			
		||||
    if (recv_data == NULL) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        return -5;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (curl_fetch_nodes_JSON(recv_data) == -1) {
 | 
			
		||||
        free(recv_data);
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        return -2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (recv_data->length == 0) {
 | 
			
		||||
        free(recv_data);
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        return -3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fwrite(recv_data->data, recv_data->length, 1, fp) != 1) {
 | 
			
		||||
        free(recv_data);
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        return -4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(recv_data);
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_nodeslist_path(char *buf, size_t buf_size)
 | 
			
		||||
{
 | 
			
		||||
    char *config_dir = NULL;
 | 
			
		||||
 | 
			
		||||
    if (arg_opts.nodes_path[0]) {
 | 
			
		||||
        snprintf(buf, buf_size, "%s", arg_opts.nodes_path);
 | 
			
		||||
    } else if ((config_dir = get_user_config_dir()) != NULL) {
 | 
			
		||||
        snprintf(buf, buf_size, "%s%s%s", config_dir, CONFIGDIR, DEFAULT_NODES_FILENAME);
 | 
			
		||||
        free(config_dir);
 | 
			
		||||
    } else {
 | 
			
		||||
        snprintf(buf, buf_size, "%s", DEFAULT_NODES_FILENAME);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if json encoded string s contains a valid IP address and puts address in ip_buf.
 | 
			
		||||
 *
 | 
			
		||||
 * ip_type should be set to 1 for ipv4 address, or 0 for ipv6 addresses.
 | 
			
		||||
 * ip_buf must have room for at least IP_MAX_SIZE + 1 bytes.
 | 
			
		||||
 */
 | 
			
		||||
static bool extract_val_ip(const char *s, char *ip_buf, unsigned short int ip_type)
 | 
			
		||||
{
 | 
			
		||||
    int ip_len = char_find(0, s, '"');
 | 
			
		||||
 | 
			
		||||
    if (ip_len < IP_MIN_SIZE || ip_len > IP_MAX_SIZE) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(ip_buf, s, ip_len);
 | 
			
		||||
    ip_buf[ip_len] = 0;
 | 
			
		||||
 | 
			
		||||
    return (ip_type == 1) ? is_ip4_address(ip_buf) : is_ip6_address(ip_buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Extracts the port from json encoded string s.
 | 
			
		||||
 *
 | 
			
		||||
 * Return port number on success.
 | 
			
		||||
 * Return 0 on failure.
 | 
			
		||||
 */
 | 
			
		||||
static uint16_t extract_val_port(const char *s)
 | 
			
		||||
{
 | 
			
		||||
    long int port = strtol(s, NULL, 10);
 | 
			
		||||
    return (port > 0 && port <= MAX_PORT_RANGE) ? port : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Extracts the last pinged value from json encoded string s.
 | 
			
		||||
 *
 | 
			
		||||
 * Return timestamp on success.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
static long long int extract_val_last_pinged(const char *s)
 | 
			
		||||
{
 | 
			
		||||
    long long int last_pinged = strtoll(s, NULL, 10);
 | 
			
		||||
    return (last_pinged <= 0) ? -1 : last_pinged;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Extracts DHT public key from json encoded string s and puts key in key_buf.
 | 
			
		||||
 * key_buf must have room for at least TOX_PUBLIC_KEY_SIZE * 2 + 1 bytes.
 | 
			
		||||
 *
 | 
			
		||||
 * Return number of bytes copied to key_buf on success.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
static int extract_val_pk(const char *s, char *key_buf)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    int key_len = char_find(0, s, '"');
 | 
			
		||||
 | 
			
		||||
    if (key_len != TOX_PUBLIC_KEY_SIZE * 2) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(key_buf, s, key_len);
 | 
			
		||||
    key_buf[key_len] = 0;
 | 
			
		||||
 | 
			
		||||
    return key_len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Extracts values from json formatted string, validats them, and puts them in node.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success.
 | 
			
		||||
 * Return -1 if line is empty.
 | 
			
		||||
 * Return -2 if line does not appear to be a valid nodes list entry.
 | 
			
		||||
 * Return -3 if node appears to be offline.
 | 
			
		||||
 * Return -4 if entry does not contain either a valid ipv4 or ipv6 address.
 | 
			
		||||
 * Return -5 if port value is invalid.
 | 
			
		||||
 * Return -6 if public key is invalid.
 | 
			
		||||
 */
 | 
			
		||||
static int extract_node(const char *line, struct Node *node)
 | 
			
		||||
{
 | 
			
		||||
    if (!line) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *ip4_start = strstr(line, IPV4_JSON_KEY);
 | 
			
		||||
    const char *ip6_start = strstr(line, IPV6_JSON_KEY);
 | 
			
		||||
    const char *port_start = strstr(line, PORT_JSON_KEY);
 | 
			
		||||
    const char *key_start = strstr(line, PK_JSON_KEY);
 | 
			
		||||
    const char *last_pinged_str = strstr(line, LAST_PING_JSON_KEY);
 | 
			
		||||
 | 
			
		||||
    if (!ip4_start || !ip6_start || !port_start || !key_start || !last_pinged_str) {
 | 
			
		||||
        return -2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN);
 | 
			
		||||
 | 
			
		||||
    if (last_pinged <= 0 || node_is_offline(last_pinged)) {
 | 
			
		||||
        return -3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char ip4_string[IP_MAX_SIZE + 1];
 | 
			
		||||
    bool have_ip4 = extract_val_ip(ip4_start + IPV4_JSON_KEY_LEN, ip4_string, 1);
 | 
			
		||||
 | 
			
		||||
    char ip6_string[IP_MAX_SIZE + 1];
 | 
			
		||||
    bool have_ip6 = extract_val_ip(ip6_start + IPV6_JSON_KEY_LEN, ip6_string, 0);
 | 
			
		||||
 | 
			
		||||
    if (!have_ip6 && !have_ip4) {
 | 
			
		||||
        return -4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint16_t port = extract_val_port(port_start + PORT_JSON_KEY_LEN);
 | 
			
		||||
 | 
			
		||||
    if (port == 0) {
 | 
			
		||||
        return -5;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1];
 | 
			
		||||
    int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string);
 | 
			
		||||
 | 
			
		||||
    if (key_len == -1) {
 | 
			
		||||
        return -6;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) {
 | 
			
		||||
        return -6;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (have_ip4) {
 | 
			
		||||
        snprintf(node->ip4, sizeof(node->ip4), "%s", ip4_string);
 | 
			
		||||
        node->have_ip4 = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (have_ip6) {
 | 
			
		||||
        snprintf(node->ip6, sizeof(node->ip6), "%s", ip6_string);
 | 
			
		||||
        node->have_ip6 = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    node->port = port;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Loads the DHT nodeslist to memory from json encoded nodes file. */
 | 
			
		||||
void *load_nodeslist_thread(void *data)
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(data);
 | 
			
		||||
 | 
			
		||||
    char nodes_path[PATH_MAX];
 | 
			
		||||
    get_nodeslist_path(nodes_path, sizeof(nodes_path));
 | 
			
		||||
 | 
			
		||||
    FILE *fp = NULL;
 | 
			
		||||
 | 
			
		||||
    if (!file_exists(nodes_path)) {
 | 
			
		||||
        if ((fp = fopen(nodes_path, "w+")) == NULL) {
 | 
			
		||||
            fprintf(stderr, "nodeslist load error: failed to create file '%s'\n", nodes_path);
 | 
			
		||||
            goto on_exit;
 | 
			
		||||
        }
 | 
			
		||||
    } else if ((fp = fopen(nodes_path, "r+")) == NULL) {
 | 
			
		||||
        fprintf(stderr, "nodeslist load error: failed to open file '%s'\n", nodes_path);
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int update_err = update_DHT_nodeslist(nodes_path);
 | 
			
		||||
 | 
			
		||||
    if (update_err < 0) {
 | 
			
		||||
        fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char line[MAX_NODELIST_SIZE + 1];
 | 
			
		||||
 | 
			
		||||
    if (fgets(line, sizeof(line), fp) == NULL) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        fprintf(stderr, "nodeslist load error: file empty.\n");
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t idx = 0;
 | 
			
		||||
    const char *line_start = line;
 | 
			
		||||
 | 
			
		||||
    while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY))) {
 | 
			
		||||
        pthread_mutex_lock(&thread_data.lock);
 | 
			
		||||
        idx = Nodes.count;
 | 
			
		||||
 | 
			
		||||
        if (idx >= MAX_NODES) {
 | 
			
		||||
            pthread_mutex_unlock(&thread_data.lock);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (extract_node(line_start, &Nodes.list[idx]) == 0) {
 | 
			
		||||
            ++Nodes.count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_unlock(&thread_data.lock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If nodeslist does not contain any valid entries we set the last_scan value
 | 
			
		||||
     * to 0 so that it will fetch a new list the next time this function is called.
 | 
			
		||||
     */
 | 
			
		||||
    if (Nodes.count == 0) {
 | 
			
		||||
        const char *s = "{\"last_scan\":0}";
 | 
			
		||||
        rewind(fp);
 | 
			
		||||
        fwrite(s, strlen(s), 1, fp);  // Not much we can do if it fails
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        fprintf(stderr, "nodeslist load error: List did not contain any valid entries.\n");
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
 | 
			
		||||
on_exit:
 | 
			
		||||
    thread_data.active = false;
 | 
			
		||||
    pthread_attr_destroy(&thread_data.attr);
 | 
			
		||||
    pthread_exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Creates a new thread that will load the DHT nodeslist to memory
 | 
			
		||||
 * from json encoded nodes file obtained at NODES_LIST_URL. Only one
 | 
			
		||||
 * thread may run at a time.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success.
 | 
			
		||||
 * Return -1 if a thread is already active.
 | 
			
		||||
 * Return -2 if mutex fails to init.
 | 
			
		||||
 * Return -3 if pthread attribute fails to init.
 | 
			
		||||
 * Return -4 if pthread fails to set detached state.
 | 
			
		||||
 * Return -5 if thread creation fails.
 | 
			
		||||
 */
 | 
			
		||||
int load_DHT_nodeslist(void)
 | 
			
		||||
{
 | 
			
		||||
    if (thread_data.active) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pthread_mutex_init(&thread_data.lock, NULL) != 0) {
 | 
			
		||||
        return -2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pthread_attr_init(&thread_data.attr) != 0) {
 | 
			
		||||
        return -3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED) != 0) {
 | 
			
		||||
        return -4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    thread_data.active = true;
 | 
			
		||||
 | 
			
		||||
    if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) {
 | 
			
		||||
        thread_data.active = false;
 | 
			
		||||
        return -5;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */
 | 
			
		||||
static void DHT_bootstrap(Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_lock(&thread_data.lock);
 | 
			
		||||
    size_t num_nodes = Nodes.count;
 | 
			
		||||
    pthread_mutex_unlock(&thread_data.lock);
 | 
			
		||||
 | 
			
		||||
    if (num_nodes == 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&thread_data.lock);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) {
 | 
			
		||||
        struct Node *node = &Nodes.list[rand() % Nodes.count];
 | 
			
		||||
        const char *addr = node->have_ip4 ? node->ip4 : node->ip6;
 | 
			
		||||
 | 
			
		||||
        if (!addr) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Tox_Err_Bootstrap err;
 | 
			
		||||
        tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_BOOTSTRAP_OK) {
 | 
			
		||||
            fprintf(stderr, "Failed to bootstrap %s:%d\n", addr, node->port);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        tox_add_tcp_relay(m, addr, node->port, (uint8_t *) node->key, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_BOOTSTRAP_OK) {
 | 
			
		||||
            fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(&thread_data.lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Manages connection to the Tox DHT network. */
 | 
			
		||||
void do_tox_connection(Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    static time_t last_bootstrap_time = 0;
 | 
			
		||||
    bool connected = prompt_selfConnectionStatus() != TOX_CONNECTION_NONE;
 | 
			
		||||
 | 
			
		||||
    if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) {
 | 
			
		||||
        DHT_bootstrap(m);
 | 
			
		||||
        last_bootstrap_time = get_unix_time();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								src/bootstrap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/bootstrap.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
/*  bootstrap.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef BOOTSTRAP_H
 | 
			
		||||
#define BOOTSTRAP_H
 | 
			
		||||
 | 
			
		||||
/* Manages connection to the Tox DHT network. */
 | 
			
		||||
void do_tox_connection(Tox *m);
 | 
			
		||||
 | 
			
		||||
/* Creates a new thread that will load the DHT nodeslist to memory
 | 
			
		||||
 * from json encoded nodes file obtained at NODES_LIST_URL. Only one
 | 
			
		||||
 * thread may run at a time.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success.
 | 
			
		||||
 * Return -1 if a thread is already active.
 | 
			
		||||
 * Return -2 if mutex fails to init.
 | 
			
		||||
 * Return -3 if pthread attribute fails to init.
 | 
			
		||||
 * Return -4 if pthread fails to set detached state.
 | 
			
		||||
 * Return -5 if thread creation fails.
 | 
			
		||||
 */
 | 
			
		||||
int load_DHT_nodeslist(void);
 | 
			
		||||
 | 
			
		||||
#endif /* BOOTSTRAP_H */
 | 
			
		||||
							
								
								
									
										1105
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										1105
									
								
								src/chat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -23,13 +23,13 @@
 | 
			
		||||
#ifndef CHAT_H
 | 
			
		||||
#define CHAT_H
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
/* set CTRL to -1 if we don't want to send a control signal.
 | 
			
		||||
   set msg to NULL if we don't want to display a message */
 | 
			
		||||
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
 | 
			
		||||
void kill_chat_window(ToxWindow *self, Tox *m);
 | 
			
		||||
ToxWindow new_chat(Tox *m, int32_t friendnum);
 | 
			
		||||
ToxWindow *new_chat(Tox *m, int32_t friendnum);
 | 
			
		||||
 | 
			
		||||
#endif /* end of include guard: CHAT_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -23,23 +23,25 @@
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "chat.h"
 | 
			
		||||
#include "conference.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "groupchat.h"
 | 
			
		||||
#include "chat.h"
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
 | 
			
		||||
extern ToxWindow *prompt;
 | 
			
		||||
extern FriendsList Friends;
 | 
			
		||||
 | 
			
		||||
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc < 2) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -48,7 +50,7 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
 | 
			
		||||
    long int idx = strtol(argv[2], NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -60,17 +62,17 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
 | 
			
		||||
    } else if (strcasecmp(inoutstr, "out") == 0) {
 | 
			
		||||
        ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
 | 
			
		||||
    } else {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!ft) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ft->state == FILE_TRANSFER_INACTIVE) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -78,111 +80,145 @@ 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_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long int groupnum = strtol(argv[1], NULL, 10);
 | 
			
		||||
    long int conferencenum = strtol(argv[1], NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
 | 
			
		||||
    if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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_Conference_Invite err;
 | 
			
		||||
 | 
			
		||||
    if (!tox_conference_invite(m, self->num, conferencenum, &err)) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
    UNUSED_VAR(argc);
 | 
			
		||||
    UNUSED_VAR(argv);
 | 
			
		||||
 | 
			
		||||
    if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
 | 
			
		||||
        line_info_add(self, false, 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;
 | 
			
		||||
    const char *conferencekey = Friends.list[self->num].conference_invite.key;
 | 
			
		||||
    uint16_t length = Friends.list[self->num].conference_invite.length;
 | 
			
		||||
    uint8_t type = Friends.list[self->num].conference_invite.type;
 | 
			
		||||
 | 
			
		||||
    if (!Friends.list[self->num].group_invite.pending) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
 | 
			
		||||
    if (!Friends.list[self->num].conference_invite.pending) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int groupnum = -1;
 | 
			
		||||
    uint32_t conferencenum;
 | 
			
		||||
 | 
			
		||||
    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 (type == TOX_CONFERENCE_TYPE_TEXT) {
 | 
			
		||||
        Tox_Err_Conference_Join err;
 | 
			
		||||
        conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err);
 | 
			
		||||
 | 
			
		||||
    if (groupnum == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
 | 
			
		||||
        if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (type == TOX_CONFERENCE_TYPE_AV) {
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
        conferencenum = toxav_join_av_groupchat(m, self->num, (const uint8_t *) conferencekey, length,
 | 
			
		||||
                                                audio_conference_callback, NULL);
 | 
			
		||||
 | 
			
		||||
        if (conferencenum == (uint32_t) -1) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
#else
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
 | 
			
		||||
        return;
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
 | 
			
		||||
        tox_conference_delete(m, conferencenum, NULL);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
    if (type == TOX_CONFERENCE_TYPE_AV) {
 | 
			
		||||
        if (!init_conference_audio_input(m, conferencenum)) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long int idx = strtol(argv[1], NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
 | 
			
		||||
 | 
			
		||||
    if (!ft) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ft->state != FILE_TRANSFER_PENDING) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
 | 
			
		||||
        const char *msg =  "File transfer failed: Invalid file path.";
 | 
			
		||||
        const char *msg =  "File transfer failed: Invalid download path.";
 | 
			
		||||
        close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_FILE_CONTROL err;
 | 
			
		||||
    tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
 | 
			
		||||
    Tox_Err_File_Control err;
 | 
			
		||||
    tox_file_control(m, self->num, ft->filenumber, TOX_FILE_CONTROL_RESUME, &err);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_FILE_CONTROL_OK)
 | 
			
		||||
    if (err != TOX_ERR_FILE_CONTROL_OK) {
 | 
			
		||||
        goto on_recv_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] as: '%s'", idx, ft->file_path);
 | 
			
		||||
 | 
			
		||||
    /* prep progress bar line */
 | 
			
		||||
    char progline[MAX_STR_SIZE];
 | 
			
		||||
    init_progress_bar(progline);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
 | 
			
		||||
 | 
			
		||||
    ft->line_id = self->chatwin->hst->line_end->id + 2;
 | 
			
		||||
    ft->state = FILE_TRANSFER_STARTED;
 | 
			
		||||
@@ -190,65 +226,61 @@ 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.");
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_FILE_CONTROL_NOT_FOUND:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_FILE_CONTROL_SENDQ:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
 | 
			
		||||
            return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    const char *errmsg = NULL;
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argv[1][0] != '\"') {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* remove opening and closing quotes */
 | 
			
		||||
    char path[MAX_STR_SIZE];
 | 
			
		||||
    snprintf(path, sizeof(path), "%s", &argv[1][1]);
 | 
			
		||||
    int path_len = strlen(path) - 1;
 | 
			
		||||
    path[path_len] = '\0';
 | 
			
		||||
    snprintf(path, sizeof(path), "%s", argv[1]);
 | 
			
		||||
    int path_len = strlen(path);
 | 
			
		||||
 | 
			
		||||
    if (path_len >= MAX_STR_SIZE) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FILE *file_to_send = fopen(path, "r");
 | 
			
		||||
 | 
			
		||||
    if (file_to_send == NULL) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    off_t filesize = file_size(path);
 | 
			
		||||
 | 
			
		||||
    if (filesize == 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
 | 
			
		||||
        fclose(file_to_send);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -256,12 +288,13 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
 | 
			
		||||
    char file_name[TOX_MAX_FILENAME_LENGTH];
 | 
			
		||||
    size_t namelen = get_file_name(file_name, sizeof(file_name), path);
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_FILE_SEND err;
 | 
			
		||||
    Tox_Err_File_Send err;
 | 
			
		||||
    uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
 | 
			
		||||
                                     (uint8_t *) file_name, namelen, &err);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_FILE_SEND_OK)
 | 
			
		||||
    if (err != TOX_ERR_FILE_SEND_OK) {
 | 
			
		||||
        goto on_send_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
 | 
			
		||||
 | 
			
		||||
@@ -277,11 +310,12 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
 | 
			
		||||
 | 
			
		||||
    char sizestr[32];
 | 
			
		||||
    bytes_convert_str(sizestr, sizeof(sizestr), filesize);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
on_send_error:
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
 | 
			
		||||
            errmsg = "File transfer failed: Invalid friend.";
 | 
			
		||||
@@ -304,7 +338,7 @@ on_send_error:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
 | 
			
		||||
    tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
 | 
			
		||||
    fclose(file_to_send);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,12 @@
 | 
			
		||||
#ifndef CHAT_COMMANDS_H
 | 
			
		||||
#define CHAT_COMMANDS_H
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
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_conference_invite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_conference_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
 | 
			
		||||
@@ -41,11 +41,13 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
 | 
			
		||||
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
void cmd_vcall(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_res(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#endif /* #define CHAT_COMMANDS_H */
 | 
			
		||||
#endif /* CHAT_COMMANDS_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1418
									
								
								src/conference.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1418
									
								
								src/conference.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										118
									
								
								src/conference.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/conference.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
/*  conference.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef CONFERENCE_H
 | 
			
		||||
#define CONFERENCE_H
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
#define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH
 | 
			
		||||
#define SIDEBAR_WIDTH 16
 | 
			
		||||
 | 
			
		||||
typedef struct ConferencePeer {
 | 
			
		||||
    bool       active;
 | 
			
		||||
 | 
			
		||||
    uint8_t    pubkey[TOX_PUBLIC_KEY_SIZE];
 | 
			
		||||
    uint32_t   peernum;    /* index in chat->peer_list */
 | 
			
		||||
 | 
			
		||||
    char       name[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    size_t     name_length;
 | 
			
		||||
 | 
			
		||||
    bool       sending_audio;
 | 
			
		||||
    uint32_t   audio_out_idx;
 | 
			
		||||
    time_t     last_audio_time;
 | 
			
		||||
} ConferencePeer;
 | 
			
		||||
 | 
			
		||||
typedef struct AudioInputCallbackData {
 | 
			
		||||
    Tox *tox;
 | 
			
		||||
    uint32_t conferencenum;
 | 
			
		||||
} AudioInputCallbackData;
 | 
			
		||||
 | 
			
		||||
#define PUBKEY_STRING_SIZE (2 * TOX_PUBLIC_KEY_SIZE + 1)
 | 
			
		||||
typedef struct NameListEntry {
 | 
			
		||||
    char name[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    char pubkey_str[PUBKEY_STRING_SIZE];
 | 
			
		||||
    uint32_t peernum;
 | 
			
		||||
} NameListEntry;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int chatwin;
 | 
			
		||||
    bool active;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    int side_pos;    /* current position of the sidebar - used for scrolling up and down */
 | 
			
		||||
    time_t start_time;
 | 
			
		||||
 | 
			
		||||
    char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
 | 
			
		||||
    size_t title_length;
 | 
			
		||||
 | 
			
		||||
    ConferencePeer *peer_list;
 | 
			
		||||
    uint32_t max_idx;
 | 
			
		||||
 | 
			
		||||
    NameListEntry *name_list;
 | 
			
		||||
    uint32_t num_peers;
 | 
			
		||||
 | 
			
		||||
    bool push_to_talk_enabled;
 | 
			
		||||
    time_t ptt_last_pushed;
 | 
			
		||||
 | 
			
		||||
    bool audio_enabled;
 | 
			
		||||
    time_t last_sent_audio;
 | 
			
		||||
    uint32_t audio_in_idx;
 | 
			
		||||
    AudioInputCallbackData audio_input_callback_data;
 | 
			
		||||
} ConferenceChat;
 | 
			
		||||
 | 
			
		||||
/* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */
 | 
			
		||||
void free_conference(ToxWindow *self, uint32_t conferencenum);
 | 
			
		||||
 | 
			
		||||
int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length);
 | 
			
		||||
 | 
			
		||||
/* destroys and re-creates conference window with or without the peerlist */
 | 
			
		||||
void redraw_conference_win(ToxWindow *self);
 | 
			
		||||
 | 
			
		||||
void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length);
 | 
			
		||||
void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title);
 | 
			
		||||
int conference_enable_logging(ToxWindow *self, Tox *m, uint32_t conferencenum, struct chatlog *log);
 | 
			
		||||
 | 
			
		||||
/* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum
 | 
			
		||||
 * of `maxpeers`.
 | 
			
		||||
 * Maches each peer whose name or pubkey begins with `prefix`.
 | 
			
		||||
 * If `prefix` is exactly the pubkey of a peer, matches only that peer.
 | 
			
		||||
 * return number of entries placed in `entries`.
 | 
			
		||||
 */
 | 
			
		||||
uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries,
 | 
			
		||||
        uint32_t maxpeers);
 | 
			
		||||
 | 
			
		||||
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum);
 | 
			
		||||
bool enable_conference_audio(Tox *tox, uint32_t conferencenum);
 | 
			
		||||
bool disable_conference_audio(Tox *tox, uint32_t conferencenum);
 | 
			
		||||
bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled);
 | 
			
		||||
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum,
 | 
			
		||||
                               const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
 | 
			
		||||
                               sample_rate, void *userdata);
 | 
			
		||||
 | 
			
		||||
bool conference_mute_self(uint32_t conferencenum);
 | 
			
		||||
bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum);
 | 
			
		||||
bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold);
 | 
			
		||||
float conference_get_VAD_threshold(uint32_t conferencenum);
 | 
			
		||||
 | 
			
		||||
#endif /* CONFERENCE_H */
 | 
			
		||||
							
								
								
									
										210
									
								
								src/conference_commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								src/conference_commands.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,210 @@
 | 
			
		||||
/*  conference_commands.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "conference.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
static void print_err(ToxWindow *self, const char *error_str)
 | 
			
		||||
{
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    Tox_Err_Conference_Title err;
 | 
			
		||||
    char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        size_t tlen = tox_conference_get_title_size(m, self->num, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) {
 | 
			
		||||
            print_err(self, "Title is not set");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
 | 
			
		||||
            print_err(self, "Title is not set");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        title[tlen] = '\0';
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t len = strlen(argv[1]);
 | 
			
		||||
 | 
			
		||||
    if (len >= sizeof(title)) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(title, sizeof(title), "%s", argv[1]);
 | 
			
		||||
 | 
			
		||||
    if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    conference_rename_log_path(m, self->num, title);  // must be called first
 | 
			
		||||
 | 
			
		||||
    conference_set_title(self, self->num, title, len);
 | 
			
		||||
 | 
			
		||||
    char selfnick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    tox_self_get_name(m, (uint8_t *) selfnick);
 | 
			
		||||
 | 
			
		||||
    size_t sn_len = tox_self_get_name_size(m);
 | 
			
		||||
    selfnick[sn_len] = '\0';
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, true, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title);
 | 
			
		||||
 | 
			
		||||
    char tmp_event[MAX_STR_SIZE + 20];
 | 
			
		||||
    snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
 | 
			
		||||
    write_to_log(tmp_event, selfnick, self->chatwin->log, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    bool enable;
 | 
			
		||||
 | 
			
		||||
    if (argc == 1 && !strcasecmp(argv[1], "on")) {
 | 
			
		||||
        enable = true;
 | 
			
		||||
    } else if (argc == 1 && !strcasecmp(argv[1], "off")) {
 | 
			
		||||
        enable = false;
 | 
			
		||||
    } else {
 | 
			
		||||
        print_err(self, "Please specify: on | off");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) {
 | 
			
		||||
        print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk."
 | 
			
		||||
                  : "Disabled conference audio");
 | 
			
		||||
    } else {
 | 
			
		||||
        print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        if (conference_mute_self(self->num)) {
 | 
			
		||||
            print_err(self, "Toggled self audio mute status");
 | 
			
		||||
        } else {
 | 
			
		||||
            print_err(self, "No audio input to mute");
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        NameListEntry *entries[16];
 | 
			
		||||
        uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16);
 | 
			
		||||
 | 
			
		||||
        if (n == 0) {
 | 
			
		||||
            print_err(self, "No such peer");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (n > 1) {
 | 
			
		||||
            print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):");
 | 
			
		||||
 | 
			
		||||
            for (uint32_t i = 0; i < n; ++i) {
 | 
			
		||||
                line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (conference_mute_peer(m, self->num, entries[0]->peernum)) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name);
 | 
			
		||||
        } else {
 | 
			
		||||
            print_err(self, "Peer is not on the call");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
    UNUSED_VAR(m);
 | 
			
		||||
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f",
 | 
			
		||||
                      (double) conference_get_VAD_threshold(self->num));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        print_err(self, "Only one argument allowed.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char *end;
 | 
			
		||||
    float value = strtof(argv[1], &end);
 | 
			
		||||
 | 
			
		||||
    if (*end) {
 | 
			
		||||
        print_err(self, "Invalid input");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (conference_set_VAD_threshold(self->num, value)) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value);
 | 
			
		||||
    } else {
 | 
			
		||||
        print_err(self, "Failed to set conference audio input sensitivity.");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
    UNUSED_VAR(m);
 | 
			
		||||
 | 
			
		||||
    bool enable;
 | 
			
		||||
 | 
			
		||||
    if (argc == 1 && !strcasecmp(argv[1], "on")) {
 | 
			
		||||
        enable = true;
 | 
			
		||||
    } else if (argc == 1 && !strcasecmp(argv[1], "off")) {
 | 
			
		||||
        enable = false;
 | 
			
		||||
    } else {
 | 
			
		||||
        print_err(self, "Please specify: on | off");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!toggle_conference_push_to_talk(self->num, enable)) {
 | 
			
		||||
        print_err(self, "Failed to toggle push to talk.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled");
 | 
			
		||||
}
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
							
								
								
									
										35
									
								
								src/conference_commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/conference_commands.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
/*  conference_commands.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef CONFERENCE_COMMANDS_H
 | 
			
		||||
#define CONFERENCE_COMMANDS_H
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
 | 
			
		||||
#endif /* CONFERENCE_COMMANDS_H */
 | 
			
		||||
@@ -20,20 +20,20 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <pwd.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "configdir.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
 | 
			
		||||
/* get the user's home directory */
 | 
			
		||||
/* get the user's home directory. */
 | 
			
		||||
void get_home_dir(char *home, int size)
 | 
			
		||||
{
 | 
			
		||||
    struct passwd pwd;
 | 
			
		||||
@@ -48,8 +48,9 @@ void get_home_dir(char *home, int size)
 | 
			
		||||
    } else {
 | 
			
		||||
        hmstr = getenv("HOME");
 | 
			
		||||
 | 
			
		||||
        if (hmstr == NULL)
 | 
			
		||||
        if (hmstr == NULL) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        snprintf(buf, sizeof(buf), "%s", hmstr);
 | 
			
		||||
        hmstr = buf;
 | 
			
		||||
@@ -77,8 +78,9 @@ char *get_user_config_dir(void)
 | 
			
		||||
    len = strlen(home) + strlen("/Library/Application Support") + 1;
 | 
			
		||||
    user_config_dir = malloc(len);
 | 
			
		||||
 | 
			
		||||
    if (user_config_dir == NULL)
 | 
			
		||||
    if (user_config_dir == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(user_config_dir, len, "%s/Library/Application Support", home);
 | 
			
		||||
# else /* __APPLE__ */
 | 
			
		||||
@@ -89,8 +91,9 @@ char *get_user_config_dir(void)
 | 
			
		||||
        len = strlen(home) + strlen("/.config") + 1;
 | 
			
		||||
        user_config_dir = malloc(len);
 | 
			
		||||
 | 
			
		||||
        if (user_config_dir == NULL)
 | 
			
		||||
        if (user_config_dir == NULL) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        snprintf(user_config_dir, len, "%s/.config", home);
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -99,30 +102,29 @@ char *get_user_config_dir(void)
 | 
			
		||||
 | 
			
		||||
# endif /* __APPLE__ */
 | 
			
		||||
 | 
			
		||||
    if (!file_exists(user_config_dir)) {
 | 
			
		||||
        free(user_config_dir);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
{
 | 
			
		||||
    struct stat buf;
 | 
			
		||||
    int mkdir_err = mkdir(path, 0700);
 | 
			
		||||
 | 
			
		||||
    if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode)))
 | 
			
		||||
    if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
 | 
			
		||||
    char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1);
 | 
			
		||||
 | 
			
		||||
    if (fullpath == NULL || logpath == NULL)
 | 
			
		||||
    if (fullpath == NULL || logpath == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    strcpy(fullpath, path);
 | 
			
		||||
    strcat(fullpath, CONFIGDIR);
 | 
			
		||||
 
 | 
			
		||||
@@ -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 */
 | 
			
		||||
#endif /* CONFIGDIR_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								src/curl_util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/curl_util.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
/*  curl_util.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
#include <tox/tox.h>
 | 
			
		||||
 | 
			
		||||
#include "curl_util.h"
 | 
			
		||||
 | 
			
		||||
/* Sets proxy info for given CURL handler.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success or if no proxy is set by the client.
 | 
			
		||||
 * Returns -1 if proxy info is invalid.
 | 
			
		||||
 * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
 | 
			
		||||
 */
 | 
			
		||||
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
 | 
			
		||||
{
 | 
			
		||||
    if (proxy_type == TOX_PROXY_TYPE_NONE) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (proxy_address == NULL || port == 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Callback function for CURL to write received data.
 | 
			
		||||
 *
 | 
			
		||||
 * This function will append data from an http request to the data buffer
 | 
			
		||||
 * until the request is complete or the buffer is full. Buffer will be null terminated.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns number of bytes received from http request on success (don't change this).
 | 
			
		||||
 * Returns 0 if data exceeds buffer size.
 | 
			
		||||
 */
 | 
			
		||||
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer)
 | 
			
		||||
{
 | 
			
		||||
    struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer;
 | 
			
		||||
 | 
			
		||||
    size_t length = size * nmemb;
 | 
			
		||||
    size_t total_size = length + recv_data->length;
 | 
			
		||||
 | 
			
		||||
    if (total_size > MAX_RECV_CURL_DATA_SIZE) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(recv_data->data + recv_data->length, data, length);
 | 
			
		||||
    recv_data->data[total_size] = '\0';
 | 
			
		||||
    recv_data->length += length;
 | 
			
		||||
 | 
			
		||||
    return length;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								src/curl_util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/curl_util.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
/*  curl_util.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef CURL_UTIL_H
 | 
			
		||||
#define CURL_UTIL_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/* List based on Mozilla's recommended configurations for modern browsers */
 | 
			
		||||
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
 | 
			
		||||
 | 
			
		||||
/* Max size of an http response that we can store in Recv_Data */
 | 
			
		||||
#define MAX_RECV_CURL_DATA_SIZE 32767
 | 
			
		||||
 | 
			
		||||
/* Holds data received from curl lookup */
 | 
			
		||||
struct Recv_Curl_Data {
 | 
			
		||||
    char data[MAX_RECV_CURL_DATA_SIZE + 1];   /* Data received from curl write data callback */
 | 
			
		||||
    size_t length;  /* Total number of bytes written to data buffer (doesn't include null) */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Sets proxy info for given CURL handler.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success or if no proxy is set by the client.
 | 
			
		||||
 * Returns -1 if proxy info is invalid.
 | 
			
		||||
 * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
 | 
			
		||||
 */
 | 
			
		||||
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type);
 | 
			
		||||
 | 
			
		||||
/* Callback function for CURL to write received data.
 | 
			
		||||
 *
 | 
			
		||||
 * This function will append data from an http request to the data buffer
 | 
			
		||||
 * until the request is complete or the buffer is full. Buffer will be null terminated.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns size of bytes written to the data buffer.
 | 
			
		||||
 */
 | 
			
		||||
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer);
 | 
			
		||||
 | 
			
		||||
#endif /* CURL_UTIL_H */
 | 
			
		||||
							
								
								
									
										179
									
								
								src/execute.c
									
									
									
									
									
								
							
							
						
						
									
										179
									
								
								src/execute.c
									
									
									
									
									
								
							@@ -20,19 +20,20 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "api.h"
 | 
			
		||||
#include "chat_commands.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "global_commands.h"
 | 
			
		||||
#include "group_commands.h"
 | 
			
		||||
#include "conference_commands.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
struct cmd_func {
 | 
			
		||||
    const char *name;
 | 
			
		||||
@@ -47,11 +48,13 @@ static struct cmd_func global_commands[] = {
 | 
			
		||||
    { "/connect",   cmd_connect       },
 | 
			
		||||
    { "/decline",   cmd_decline       },
 | 
			
		||||
    { "/exit",      cmd_quit          },
 | 
			
		||||
    { "/group",     cmd_groupchat     },
 | 
			
		||||
    { "/conference", cmd_conference    },
 | 
			
		||||
    { "/help",      cmd_prompt_help   },
 | 
			
		||||
    { "/log",       cmd_log           },
 | 
			
		||||
    { "/myid",      cmd_myid          },
 | 
			
		||||
#ifdef QRCODE
 | 
			
		||||
    { "/myqr",      cmd_myqr          },
 | 
			
		||||
#endif /* QRCODE */
 | 
			
		||||
    { "/nick",      cmd_nick          },
 | 
			
		||||
    { "/note",      cmd_note          },
 | 
			
		||||
    { "/nospam",    cmd_nospam        },
 | 
			
		||||
@@ -65,15 +68,18 @@ static struct cmd_func global_commands[] = {
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    { "/lsvdev",    cmd_list_video_devices  },
 | 
			
		||||
    { "/svdev" ,    cmd_change_video_device },
 | 
			
		||||
    { "/svdev",     cmd_change_video_device },
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
    { "/run",       cmd_run           },
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
    { NULL,         NULL              },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct cmd_func chat_commands[] = {
 | 
			
		||||
    { "/cancel",    cmd_cancelfile        },
 | 
			
		||||
    { "/invite",    cmd_groupinvite },
 | 
			
		||||
    { "/join",      cmd_join_group  },
 | 
			
		||||
    { "/invite",    cmd_conference_invite },
 | 
			
		||||
    { "/join",      cmd_conference_join   },
 | 
			
		||||
    { "/savefile",  cmd_savefile          },
 | 
			
		||||
    { "/sendfile",  cmd_sendfile          },
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
@@ -83,69 +89,127 @@ static struct cmd_func chat_commands[] = {
 | 
			
		||||
    { "/hangup",    cmd_hangup            },
 | 
			
		||||
    { "/mute",      cmd_mute              },
 | 
			
		||||
    { "/sense",     cmd_sense             },
 | 
			
		||||
    { "/bitrate",   cmd_bitrate           },
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    { "/vcall",     cmd_vcall             },
 | 
			
		||||
    { "/video",     cmd_video             },
 | 
			
		||||
    { "/res",       cmd_res               },
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    { NULL,         NULL                  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct cmd_func group_commands[] = {
 | 
			
		||||
    { "/title",     cmd_set_title   },
 | 
			
		||||
static struct cmd_func conference_commands[] = {
 | 
			
		||||
    { "/title",     cmd_conference_set_title },
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    { "/mute",      cmd_mute        },
 | 
			
		||||
    { "/sense",     cmd_sense       },
 | 
			
		||||
    { "/audio",     cmd_enable_audio },
 | 
			
		||||
    { "/mute",      cmd_conference_mute   },
 | 
			
		||||
    { "/ptt",       cmd_conference_push_to_talk },
 | 
			
		||||
    { "/sense",     cmd_conference_sense  },
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
    { NULL,         NULL             },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Parses input command and puts args into arg array.
 | 
			
		||||
   Returns number of arguments on success, -1 on failure. */
 | 
			
		||||
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
#define SPECIAL_COMMANDS 7
 | 
			
		||||
#else
 | 
			
		||||
#define SPECIAL_COMMANDS 6
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
/* Special commands are commands that only take one argument even if it contains spaces */
 | 
			
		||||
static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
 | 
			
		||||
    "/avatar",
 | 
			
		||||
    "/nick",
 | 
			
		||||
    "/note",
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
    "/run",
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
    "/sendfile",
 | 
			
		||||
    "/title",
 | 
			
		||||
    "/mute",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Returns true if input command is in the special_commands array. */
 | 
			
		||||
static bool is_special_command(const char *input)
 | 
			
		||||
{
 | 
			
		||||
    const int s = char_find(0, input, ' ');
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < SPECIAL_COMMANDS; ++i) {
 | 
			
		||||
        if (strncmp(input, special_commands[i], s) == 0) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parses commands in the special_commands array. Unlike parse_command, this function
 | 
			
		||||
 * does not split the input string at spaces.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the number of arguments.
 | 
			
		||||
 */
 | 
			
		||||
static int parse_special_command(const char *input, char (*args)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    int len = strlen(input);
 | 
			
		||||
    int s = char_find(0, input, ' ');
 | 
			
		||||
 | 
			
		||||
    memcpy(args[0], input, s);
 | 
			
		||||
    args[0][s++] = '\0';    // increment to remove space after "/command "
 | 
			
		||||
 | 
			
		||||
    if (s >= len) {
 | 
			
		||||
        return 1;  // No additional args
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(args[1], input + s, len - s);
 | 
			
		||||
    args[1][len - s] = '\0';
 | 
			
		||||
 | 
			
		||||
    return 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parses input command and puts args into arg array.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the number of arguments.
 | 
			
		||||
 */
 | 
			
		||||
static int parse_command(const char *input, char (*args)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (is_special_command(input)) {
 | 
			
		||||
        return parse_special_command(input, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char *cmd = strdup(input);
 | 
			
		||||
 | 
			
		||||
    if (cmd == NULL)
 | 
			
		||||
    if (cmd == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int num_args = 0;
 | 
			
		||||
    int i = 0;    /* index of last char in an argument */
 | 
			
		||||
 | 
			
		||||
    /* characters wrapped in double quotes count as one arg */
 | 
			
		||||
    while (num_args < MAX_NUM_ARGS) {
 | 
			
		||||
        int qt_ofst = 0;    /* set to 1 to offset index for quote char at end of arg */
 | 
			
		||||
        int i = char_find(0, cmd, ' ');    // index of last char in an argument
 | 
			
		||||
        memcpy(args[num_args], cmd, i);
 | 
			
		||||
        args[num_args++][i] = '\0';
 | 
			
		||||
 | 
			
		||||
        if (*cmd == '\"') {
 | 
			
		||||
            qt_ofst = 1;
 | 
			
		||||
            i = char_find(1, cmd, '\"');
 | 
			
		||||
 | 
			
		||||
            if (cmd[i] == '\0') {
 | 
			
		||||
                const char *errmsg = "Invalid argument. Did you forget a closing \"?";
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
                free(cmd);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            i = char_find(0, cmd, ' ');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        memcpy(args[num_args], cmd, i + qt_ofst);
 | 
			
		||||
        args[num_args++][i + qt_ofst] = '\0';
 | 
			
		||||
 | 
			
		||||
        if (cmd[i] == '\0')    /* no more args */
 | 
			
		||||
        if (cmd[i] == '\0') {  // no more args
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        char tmp[MAX_STR_SIZE];
 | 
			
		||||
        snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
 | 
			
		||||
        strcpy(cmd, tmp);    /* tmp will always fit inside cmd */
 | 
			
		||||
        strcpy(cmd, tmp);    // tmp will always fit inside cmd
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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])
 | 
			
		||||
{
 | 
			
		||||
@@ -163,34 +227,49 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c
 | 
			
		||||
 | 
			
		||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
 | 
			
		||||
{
 | 
			
		||||
    if (string_is_empty(input))
 | 
			
		||||
    if (string_is_empty(input)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char args[MAX_NUM_ARGS][MAX_STR_SIZE];
 | 
			
		||||
    int num_args = parse_command(w, self, input, args);
 | 
			
		||||
    int num_args = parse_command(input, args);
 | 
			
		||||
 | 
			
		||||
    if (num_args == -1)
 | 
			
		||||
    if (num_args <= 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Try to match input command to command functions. If non-global command mode is specified,
 | 
			
		||||
       try specified mode's commands first, then upon failure try global commands.
 | 
			
		||||
 | 
			
		||||
       Note: Global commands must come last in case of duplicate command names */
 | 
			
		||||
     * try specified mode's commands first, then upon failure try global commands.
 | 
			
		||||
     *
 | 
			
		||||
     * Note: Global commands must come last in case of duplicate command names
 | 
			
		||||
     */
 | 
			
		||||
    switch (mode) {
 | 
			
		||||
        case CHAT_COMMAND_MODE:
 | 
			
		||||
            if (do_command(w, self, m, num_args, chat_commands, args) == 0)
 | 
			
		||||
            if (do_command(w, self, m, num_args, chat_commands, args) == 0) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case GROUPCHAT_COMMAND_MODE:
 | 
			
		||||
            if (do_command(w, self, m, num_args, group_commands, args) == 0)
 | 
			
		||||
        case CONFERENCE_COMMAND_MODE:
 | 
			
		||||
            if (do_command(w, self, m, num_args, conference_commands, args) == 0) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (do_command(w, self, m, num_args, global_commands, args) == 0)
 | 
			
		||||
    if (do_command(w, self, m, num_args, global_commands, args) == 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
 | 
			
		||||
    if (do_plugin_command(num_args, args) == 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,9 +31,9 @@
 | 
			
		||||
enum {
 | 
			
		||||
    GLOBAL_COMMAND_MODE,
 | 
			
		||||
    CHAT_COMMAND_MODE,
 | 
			
		||||
    GROUPCHAT_COMMAND_MODE,
 | 
			
		||||
    CONFERENCE_COMMAND_MODE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
 | 
			
		||||
 | 
			
		||||
#endif /* #define EXECUTE_H */
 | 
			
		||||
#endif /* EXECUTE_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -20,115 +20,94 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
extern FriendsList Friends;
 | 
			
		||||
 | 
			
		||||
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
 | 
			
		||||
#define NUM_PROG_MARKS 50
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#define STR_BUF_SIZE 30
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
    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)
 | 
			
		||||
    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[STR_BUF_SIZE];
 | 
			
		||||
    snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
 | 
			
		||||
 | 
			
		||||
    char bps_str[STR_BUF_SIZE];
 | 
			
		||||
    bytes_convert_str(bps_str, sizeof(bps_str), bps);
 | 
			
		||||
 | 
			
		||||
    char prog_line[NUM_PROG_MARKS + 1];
 | 
			
		||||
    prog_line[0] = 0;
 | 
			
		||||
 | 
			
		||||
    int n = pct_done / (100 / NUM_PROG_MARKS);
 | 
			
		||||
    int i, j;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < n; ++i)
 | 
			
		||||
        strcat(msg, "#");
 | 
			
		||||
    for (i = 0; i < n; ++i) {
 | 
			
		||||
        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);
 | 
			
		||||
    size_t line_buf_size = strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7;
 | 
			
		||||
    char *full_line = malloc(line_buf_size);
 | 
			
		||||
 | 
			
		||||
    line_info_set(self, line_id, msg);
 | 
			
		||||
    if (full_line == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(full_line, line_buf_size, "%s [%s] %s/s", pct_str, prog_line, bps_str);
 | 
			
		||||
 | 
			
		||||
    line_info_set(self, line_id, full_line);
 | 
			
		||||
 | 
			
		||||
    free(full_line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
 | 
			
		||||
static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft)
 | 
			
		||||
{
 | 
			
		||||
    if (ft->state == FILE_TRANSFER_INACTIVE)
 | 
			
		||||
    if (ft->state == FILE_TRANSFER_INACTIVE) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Timeout must be set to 1 second to show correct bytes per second */
 | 
			
		||||
    if (!timed_out(ft->last_line_progress, 1))
 | 
			
		||||
    if (!timed_out(ft->last_line_progress, 1)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double remain = ft->file_size - ft->position;
 | 
			
		||||
    double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
 | 
			
		||||
@@ -139,34 +118,39 @@ static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* refreshes active file transfer status bars. */
 | 
			
		||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
 | 
			
		||||
void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
 | 
			
		||||
        refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
 | 
			
		||||
    for (size_t i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        refresh_progress_helper(self, &Friends.list[friendnumber].file_receiver[i]);
 | 
			
		||||
        refresh_progress_helper(self, &Friends.list[friendnumber].file_sender[i]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
 | 
			
		||||
 * Returns NULL if filenum is invalid.
 | 
			
		||||
 */
 | 
			
		||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
 | 
			
		||||
static void clear_file_transfer(struct FileTransfer *ft)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
    *ft = (struct FileTransfer) {
 | 
			
		||||
        0
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
 | 
			
		||||
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
 | 
			
		||||
 * Returns NULL if filenumber is invalid.
 | 
			
		||||
 */
 | 
			
		||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber)
 | 
			
		||||
{
 | 
			
		||||
    for (size_t i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        struct FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i];
 | 
			
		||||
 | 
			
		||||
        if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
 | 
			
		||||
        if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) {
 | 
			
		||||
            return ft_send;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
 | 
			
		||||
        struct FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i];
 | 
			
		||||
 | 
			
		||||
        if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
 | 
			
		||||
        if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) {
 | 
			
		||||
            return ft_recv;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -174,22 +158,22 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
 | 
			
		||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
 | 
			
		||||
 * Returns NULL on failure.
 | 
			
		||||
 */
 | 
			
		||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
 | 
			
		||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index,
 | 
			
		||||
        FILE_TRANSFER_DIRECTION direction)
 | 
			
		||||
{
 | 
			
		||||
    if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
 | 
			
		||||
    if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
    for (size_t i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
 | 
			
		||||
                                              &Friends.list[friendnum].file_sender[i] :
 | 
			
		||||
                                              &Friends.list[friendnum].file_receiver[i];
 | 
			
		||||
                                      &Friends.list[friendnumber].file_sender[i] :
 | 
			
		||||
                                      &Friends.list[friendnumber].file_receiver[i];
 | 
			
		||||
 | 
			
		||||
        if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
 | 
			
		||||
        if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) {
 | 
			
		||||
            return ft;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -197,23 +181,19 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t
 | 
			
		||||
/* Returns a pointer to an unused file sender.
 | 
			
		||||
 * Returns NULL if all file senders are in use.
 | 
			
		||||
 */
 | 
			
		||||
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
 | 
			
		||||
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, uint8_t type)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
 | 
			
		||||
    for (size_t i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
 | 
			
		||||
 | 
			
		||||
        if (ft->state == FILE_TRANSFER_INACTIVE) {
 | 
			
		||||
            memset(ft, 0, sizeof(struct FileTransfer));
 | 
			
		||||
            clear_file_transfer(ft);
 | 
			
		||||
            ft->window = window;
 | 
			
		||||
            ft->index = i;
 | 
			
		||||
            ft->friendnum = friendnum;
 | 
			
		||||
            ft->filenum = filenum;
 | 
			
		||||
            ft->friendnumber = friendnumber;
 | 
			
		||||
            ft->filenumber = filenumber;
 | 
			
		||||
            ft->file_type = type;
 | 
			
		||||
            ft->last_keep_alive = get_unix_time();
 | 
			
		||||
            ft->state = FILE_TRANSFER_PENDING;
 | 
			
		||||
            ft->direction = FILE_TRANSFER_SEND;
 | 
			
		||||
            return ft;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -224,23 +204,20 @@ static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnu
 | 
			
		||||
/* Returns a pointer to an unused file receiver.
 | 
			
		||||
 * Returns NULL if all file receivers are in use.
 | 
			
		||||
 */
 | 
			
		||||
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
 | 
			
		||||
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
 | 
			
		||||
        uint8_t type)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
 | 
			
		||||
    for (size_t i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        struct FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i];
 | 
			
		||||
 | 
			
		||||
        if (ft->state == FILE_TRANSFER_INACTIVE) {
 | 
			
		||||
            memset(ft, 0, sizeof(struct FileTransfer));
 | 
			
		||||
            clear_file_transfer(ft);
 | 
			
		||||
            ft->window = window;
 | 
			
		||||
            ft->index = i;
 | 
			
		||||
            ft->friendnum = friendnum;
 | 
			
		||||
            ft->filenum = filenum;
 | 
			
		||||
            ft->friendnumber = friendnumber;
 | 
			
		||||
            ft->filenumber = filenumber;
 | 
			
		||||
            ft->file_type = type;
 | 
			
		||||
            ft->last_keep_alive = get_unix_time();
 | 
			
		||||
            ft->state = FILE_TRANSFER_PENDING;
 | 
			
		||||
            ft->direction = FILE_TRANSFER_RECV;
 | 
			
		||||
            return ft;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -251,14 +228,16 @@ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friend
 | 
			
		||||
/* Initializes an unused file transfer and returns its pointer.
 | 
			
		||||
 * Returns NULL on failure.
 | 
			
		||||
 */
 | 
			
		||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
 | 
			
		||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
 | 
			
		||||
                                       FILE_TRANSFER_DIRECTION direction, uint8_t type)
 | 
			
		||||
{
 | 
			
		||||
    if (direction == FILE_TRANSFER_RECV)
 | 
			
		||||
        return new_file_receiver(window, friendnum, filenum, type);
 | 
			
		||||
    if (direction == FILE_TRANSFER_RECV) {
 | 
			
		||||
        return new_file_receiver(window, friendnumber, filenumber, type);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (direction == FILE_TRANSFER_SEND)
 | 
			
		||||
        return new_file_sender(window, friendnum, filenum, type);
 | 
			
		||||
    if (direction == FILE_TRANSFER_SEND) {
 | 
			
		||||
        return new_file_sender(window, friendnumber, filenumber, type);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -272,45 +251,59 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui
 | 
			
		||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
 | 
			
		||||
                         Notification sound_type)
 | 
			
		||||
{
 | 
			
		||||
    if (!ft)
 | 
			
		||||
    if (!ft) {
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (ft->state == FILE_TRANSFER_INACTIVE)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (ft->file)
 | 
			
		||||
        fclose(ft->file);
 | 
			
		||||
 | 
			
		||||
    if (CTRL >= 0)
 | 
			
		||||
        tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
 | 
			
		||||
 | 
			
		||||
    if (message && self) {
 | 
			
		||||
        if (self->active_box != -1 && sound_type != silent)
 | 
			
		||||
            box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
 | 
			
		||||
        else
 | 
			
		||||
            box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(ft, 0, sizeof(struct FileTransfer));
 | 
			
		||||
    if (ft->state == FILE_TRANSFER_INACTIVE) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ft->file) {
 | 
			
		||||
        fclose(ft->file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (CTRL >= 0) {
 | 
			
		||||
        tox_file_control(m, ft->friendnumber, ft->filenumber, (Tox_File_Control) CTRL, NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (message && self) {
 | 
			
		||||
        if (self->active_box != -1 && sound_type != silent) {
 | 
			
		||||
            box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
 | 
			
		||||
        } else {
 | 
			
		||||
            box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    clear_file_transfer(ft);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Kills all active file transfers for friendnum */
 | 
			
		||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
 | 
			
		||||
/* Kills active outgoing avatar file transfers for friendnumber */
 | 
			
		||||
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
    for (size_t i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
 | 
			
		||||
        close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
 | 
			
		||||
        if (ft->file_type == TOX_FILE_KIND_AVATAR) {
 | 
			
		||||
            close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Kills all active file transfers for friendnumber */
 | 
			
		||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber)
 | 
			
		||||
{
 | 
			
		||||
    for (size_t i = 0; i < MAX_FILES; ++i) {
 | 
			
		||||
        close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
 | 
			
		||||
        close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void kill_all_file_transfers(Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < Friends.max_idx; ++i)
 | 
			
		||||
    for (size_t i = 0; i < Friends.max_idx; ++i) {
 | 
			
		||||
        kill_all_file_transfers_friend(m, Friends.list[i].num);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,16 +25,15 @@
 | 
			
		||||
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
 | 
			
		||||
#define KiB 1024
 | 
			
		||||
#define MiB 1048576       /* 1024^2 */
 | 
			
		||||
#define GiB 1073741824    /* 1024^3 */
 | 
			
		||||
 | 
			
		||||
#define MAX_FILES 32
 | 
			
		||||
#define TIMEOUT_FILESENDER 120
 | 
			
		||||
 | 
			
		||||
typedef enum FILE_TRANSFER_STATE {
 | 
			
		||||
    FILE_TRANSFER_INACTIVE,
 | 
			
		||||
@@ -52,25 +51,20 @@ struct FileTransfer {
 | 
			
		||||
    ToxWindow *window;
 | 
			
		||||
    FILE *file;
 | 
			
		||||
    FILE_TRANSFER_STATE state;
 | 
			
		||||
    FILE_TRANSFER_DIRECTION direction;
 | 
			
		||||
    uint8_t file_type;
 | 
			
		||||
    char file_name[TOX_MAX_FILENAME_LENGTH + 1];
 | 
			
		||||
    char file_path[PATH_MAX + 1];    /* Not used by senders */
 | 
			
		||||
    double   bps;
 | 
			
		||||
    uint32_t filenum;
 | 
			
		||||
    uint32_t friendnum;
 | 
			
		||||
    uint32_t filenumber;
 | 
			
		||||
    uint32_t friendnumber;
 | 
			
		||||
    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 */
 | 
			
		||||
    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);
 | 
			
		||||
@@ -79,24 +73,24 @@ void init_progress_bar(char *progline);
 | 
			
		||||
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
 | 
			
		||||
 | 
			
		||||
/* refreshes active file transfer status bars. */
 | 
			
		||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
 | 
			
		||||
void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber);
 | 
			
		||||
 | 
			
		||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
 | 
			
		||||
 * Returns NULL if filenum is invalid.
 | 
			
		||||
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
 | 
			
		||||
 * Returns NULL if filenumber is invalid.
 | 
			
		||||
 */
 | 
			
		||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
 | 
			
		||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
 | 
			
		||||
 * Returns NULL on failure.
 | 
			
		||||
 */
 | 
			
		||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
 | 
			
		||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index,
 | 
			
		||||
        FILE_TRANSFER_DIRECTION direction);
 | 
			
		||||
 | 
			
		||||
/* Initializes an unused file transfer and returns its pointer.
 | 
			
		||||
 * Returns NULL on failure.
 | 
			
		||||
 */
 | 
			
		||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
 | 
			
		||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
 | 
			
		||||
                                       FILE_TRANSFER_DIRECTION direction, uint8_t type);
 | 
			
		||||
 | 
			
		||||
/* Closes file transfer ft.
 | 
			
		||||
@@ -107,9 +101,12 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui
 | 
			
		||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
 | 
			
		||||
                         Notification sound_type);
 | 
			
		||||
 | 
			
		||||
/* Kills all active file transfers for friendnum */
 | 
			
		||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
 | 
			
		||||
/* Kills active outgoing avatar file transfers for friendnumber */
 | 
			
		||||
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber);
 | 
			
		||||
 | 
			
		||||
/* Kills all active file transfers for friendnumber */
 | 
			
		||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber);
 | 
			
		||||
 | 
			
		||||
void kill_all_file_transfers(Tox *m);
 | 
			
		||||
 | 
			
		||||
#endif  /* #define FILE_TRANSFERS_H */
 | 
			
		||||
#endif /* FILE_TRANSFERS_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										663
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										663
									
								
								src/friendlist.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -25,9 +25,9 @@
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
 | 
			
		||||
struct LastOnline {
 | 
			
		||||
    uint64_t last_on;
 | 
			
		||||
@@ -35,7 +35,7 @@ struct LastOnline {
 | 
			
		||||
    char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GroupChatInvite {
 | 
			
		||||
struct ConferenceInvite {
 | 
			
		||||
    char *key;
 | 
			
		||||
    uint16_t length;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
@@ -44,20 +44,20 @@ struct GroupChatInvite {
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    char name[TOXIC_MAX_NAME_LENGTH + 1];
 | 
			
		||||
    int namelength;
 | 
			
		||||
    uint16_t namelength;
 | 
			
		||||
    char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
 | 
			
		||||
    size_t statusmsg_len;
 | 
			
		||||
    char pub_key[TOX_PUBLIC_KEY_SIZE];
 | 
			
		||||
    uint32_t num;
 | 
			
		||||
    int chatwin;
 | 
			
		||||
    bool active;
 | 
			
		||||
    TOX_CONNECTION connection_status;
 | 
			
		||||
    Tox_Connection connection_status;
 | 
			
		||||
    bool is_typing;
 | 
			
		||||
    bool logging_on;    /* saves preference for friend irrespective of global settings */
 | 
			
		||||
    uint8_t status;
 | 
			
		||||
    Tox_User_Status status;
 | 
			
		||||
 | 
			
		||||
    struct LastOnline last_online;
 | 
			
		||||
    struct GroupChatInvite group_invite;
 | 
			
		||||
    struct ConferenceInvite conference_invite;
 | 
			
		||||
 | 
			
		||||
    struct FileTransfer file_receiver[MAX_FILES];
 | 
			
		||||
    struct FileTransfer file_sender[MAX_FILES];
 | 
			
		||||
@@ -65,7 +65,7 @@ typedef struct {
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    char name[TOXIC_MAX_NAME_LENGTH + 1];
 | 
			
		||||
    int namelength;
 | 
			
		||||
    uint16_t namelength;
 | 
			
		||||
    char pub_key[TOX_PUBLIC_KEY_SIZE];
 | 
			
		||||
    uint32_t num;
 | 
			
		||||
    bool active;
 | 
			
		||||
@@ -81,14 +81,24 @@ typedef struct {
 | 
			
		||||
    ToxicFriend *list;
 | 
			
		||||
} FriendsList;
 | 
			
		||||
 | 
			
		||||
ToxWindow new_friendlist(void);
 | 
			
		||||
ToxWindow *new_friendlist(void);
 | 
			
		||||
void friendlist_onInit(ToxWindow *self, Tox *m);
 | 
			
		||||
void disable_chatwin(uint32_t f_num);
 | 
			
		||||
int get_friendnum(uint8_t *name);
 | 
			
		||||
int load_blocklist(char *data);
 | 
			
		||||
void kill_friendlist(void);
 | 
			
		||||
void kill_friendlist(ToxWindow *self);
 | 
			
		||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
 | 
			
		||||
Tox_User_Status get_friend_status(uint32_t friendnumber);
 | 
			
		||||
Tox_Connection get_friend_connection_status(uint32_t friendnumber);
 | 
			
		||||
 | 
			
		||||
/* sorts friendlist_index first by connection status then alphabetically */
 | 
			
		||||
void sort_friendlist_index(void);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns true if friend associated with `public_key` is in the block list.
 | 
			
		||||
 *
 | 
			
		||||
 * `public_key` must be at least TOX_PUBLIC_KEY_SIZE bytes.
 | 
			
		||||
 */
 | 
			
		||||
bool friend_is_blocked(const char *public_key);
 | 
			
		||||
 | 
			
		||||
#endif /* end of include guard: FRIENDLIST_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -23,19 +23,20 @@
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "groupchat.h"
 | 
			
		||||
#include "prompt.h"
 | 
			
		||||
#include "help.h"
 | 
			
		||||
#include "term_mplex.h"
 | 
			
		||||
#include "avatars.h"
 | 
			
		||||
#include "conference.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "help.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "name_lookup.h"
 | 
			
		||||
#include "prompt.h"
 | 
			
		||||
#include "qr_code.h"
 | 
			
		||||
#include "term_mplex.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "toxic_strings.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
extern char *DATA_FILE;
 | 
			
		||||
extern ToxWindow *prompt;
 | 
			
		||||
@@ -45,54 +46,58 @@ extern FriendRequests FrndRequests;
 | 
			
		||||
/* command functions */
 | 
			
		||||
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long int req = strtol(argv[1], NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!FrndRequests.request[req].active) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_FRIEND_ADD err;
 | 
			
		||||
    Tox_Err_Friend_Add err;
 | 
			
		||||
    uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_FRIEND_ADD_OK) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
 | 
			
		||||
        return;
 | 
			
		||||
    } else {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
 | 
			
		||||
        on_friendadded(m, friendnum, true);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
 | 
			
		||||
        on_friend_added(m, friendnum, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
 | 
			
		||||
    FrndRequests.request[req] = (struct friend_request) {
 | 
			
		||||
        0
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = FrndRequests.max_idx; i > 0; --i) {
 | 
			
		||||
        if (FrndRequests.request[i - 1].active)
 | 
			
		||||
        if (FrndRequests.request[i - 1].active) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FrndRequests.max_idx = i;
 | 
			
		||||
    --FrndRequests.num_requests;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg)
 | 
			
		||||
{
 | 
			
		||||
    const char *errmsg;
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_FRIEND_ADD err;
 | 
			
		||||
    uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err);
 | 
			
		||||
    Tox_Err_Friend_Add err;
 | 
			
		||||
    uint32_t f_num = tox_friend_add(m, (const uint8_t *) id_bin, (const uint8_t *) msg, strlen(msg), &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_FRIEND_ADD_TOO_LONG:
 | 
			
		||||
@@ -125,23 +130,26 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_FRIEND_ADD_OK:
 | 
			
		||||
            errmsg = "Friend request sent.";
 | 
			
		||||
            on_friendadded(m, f_num, true);
 | 
			
		||||
            on_friend_added(m, f_num, true);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_FRIEND_ADD_NULL:
 | 
			
		||||
 | 
			
		||||
        /* fallthrough */
 | 
			
		||||
        default:
 | 
			
		||||
            errmsg = "Faile to add friend: Unknown error.";
 | 
			
		||||
            errmsg = "Failed to add friend: Unknown error.";
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -150,7 +158,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
 | 
			
		||||
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        if (argv[2][0] != '\"') {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -184,13 +192,18 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
 | 
			
		||||
            xx[2] = '\0';
 | 
			
		||||
 | 
			
		||||
            if (sscanf(xx, "%02x", &x) != 1) {
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
 | 
			
		||||
                line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            id_bin[i] = x;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (friend_is_blocked(id_bin)) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cmd_add_helper(self, m, id_bin, msg);
 | 
			
		||||
    } else {    /* assume id is a username@domain address and do http name server lookup */
 | 
			
		||||
        name_lookup(self, m, id_bin, id, msg);
 | 
			
		||||
@@ -199,24 +212,20 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
 | 
			
		||||
 | 
			
		||||
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    if (argc < 2 || strlen(argv[1]) < 3) {
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc != 1 || strlen(argv[1]) < 3) {
 | 
			
		||||
        avatar_unset(m);
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argv[1][0] != '\"') {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* remove opening and closing quotes */
 | 
			
		||||
    char path[MAX_STR_SIZE];
 | 
			
		||||
    snprintf(path, sizeof(path), "%s", &argv[1][1]);
 | 
			
		||||
    int len = strlen(path) - 1;
 | 
			
		||||
    snprintf(path, sizeof(path), "%s", argv[1]);
 | 
			
		||||
    int len = strlen(path);
 | 
			
		||||
 | 
			
		||||
    if (len <= 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -225,25 +234,31 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
 | 
			
		||||
    get_file_name(filename, sizeof(filename), path);
 | 
			
		||||
 | 
			
		||||
    if (avatar_set(m, path, len) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
 | 
			
		||||
                      "Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
 | 
			
		||||
                      MAX_AVATAR_FILE_SIZE);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(m);
 | 
			
		||||
    UNUSED_VAR(argc);
 | 
			
		||||
    UNUSED_VAR(argv);
 | 
			
		||||
 | 
			
		||||
    line_info_clear(self->chatwin->hst);
 | 
			
		||||
    force_refresh(window);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc != 3) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -254,32 +269,34 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
 | 
			
		||||
    long int port = strtol(port_str, NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if (port <= 0 || port > MAX_PORT_RANGE) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
 | 
			
		||||
        line_info_add(self, false, 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.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_BOOTSTRAP err;
 | 
			
		||||
    Tox_Err_Bootstrap err;
 | 
			
		||||
    tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err);
 | 
			
		||||
    tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err);
 | 
			
		||||
 | 
			
		||||
    switch (err) {
 | 
			
		||||
        case TOX_ERR_BOOTSTRAP_BAD_HOST:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_BOOTSTRAP_BAD_PORT:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TOX_ERR_BOOTSTRAP_NULL:
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -287,154 +304,181 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
 | 
			
		||||
 | 
			
		||||
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
    UNUSED_VAR(m);
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long int req = strtol(argv[1], NULL, 10);
 | 
			
		||||
 | 
			
		||||
    if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!FrndRequests.request[req].active) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
 | 
			
		||||
    FrndRequests.request[req] = (struct friend_request) {
 | 
			
		||||
        0
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = FrndRequests.max_idx; i > 0; --i) {
 | 
			
		||||
        if (FrndRequests.request[i - 1].active)
 | 
			
		||||
        if (FrndRequests.request[i - 1].active) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FrndRequests.max_idx = i;
 | 
			
		||||
    --FrndRequests.num_requests;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
 | 
			
		||||
    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 (!strcasecmp(argv[1], "audio")) {
 | 
			
		||||
        type = TOX_CONFERENCE_TYPE_AV;
 | 
			
		||||
    } else if (!strcasecmp(argv[1], "text")) {
 | 
			
		||||
        type = TOX_CONFERENCE_TYPE_TEXT;
 | 
			
		||||
    } else {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int groupnum = -1;
 | 
			
		||||
    uint32_t conferencenum = 0;
 | 
			
		||||
 | 
			
		||||
    if (type == TOX_GROUPCHAT_TYPE_TEXT)
 | 
			
		||||
        groupnum = tox_add_groupchat(m);
 | 
			
		||||
/*#ifdef AUDIO
 | 
			
		||||
    else
 | 
			
		||||
        groupnum = toxav_add_av_groupchat(m, NULL, NULL);
 | 
			
		||||
#endif*/
 | 
			
		||||
    if (type == TOX_CONFERENCE_TYPE_TEXT) {
 | 
			
		||||
        Tox_Err_Conference_New err;
 | 
			
		||||
 | 
			
		||||
    if (groupnum == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
 | 
			
		||||
        conferencenum = tox_conference_new(m, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_CONFERENCE_NEW_OK) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (type == TOX_CONFERENCE_TYPE_AV) {
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
        conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL);
 | 
			
		||||
 | 
			
		||||
        if (conferencenum == (uint32_t) -1) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
#else
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
 | 
			
		||||
        return;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
 | 
			
		||||
        tox_conference_delete(m, conferencenum, NULL);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum);
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
    if (type == TOX_CONFERENCE_TYPE_AV) {
 | 
			
		||||
        if (!init_conference_audio_input(m, conferencenum)) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    const char *msg;
 | 
			
		||||
    struct chatlog *log = self->chatwin->log;
 | 
			
		||||
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
        if (log->log_on)
 | 
			
		||||
        if (log->log_on) {
 | 
			
		||||
            msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)";
 | 
			
		||||
        else
 | 
			
		||||
        } else {
 | 
			
		||||
            msg = "Logging for this window is OFF; type \"/log on\" to enable.";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *swch = argv[1];
 | 
			
		||||
 | 
			
		||||
    if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
 | 
			
		||||
        char myid[TOX_ADDRESS_SIZE];
 | 
			
		||||
        tox_self_get_address(m, (uint8_t *) myid);
 | 
			
		||||
 | 
			
		||||
        int log_ret = -1;
 | 
			
		||||
 | 
			
		||||
        if (self->is_chat) {
 | 
			
		||||
            Friends.list[self->num].logging_on = true;
 | 
			
		||||
            log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
 | 
			
		||||
        } else if (self->is_prompt) {
 | 
			
		||||
            log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
 | 
			
		||||
        } else if (self->is_groupchat) {
 | 
			
		||||
            log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
        msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log.";
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
        return;
 | 
			
		||||
    } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
 | 
			
		||||
        if (self->is_chat)
 | 
			
		||||
        if (self->type == WINDOW_TYPE_CHAT) {
 | 
			
		||||
            Friends.list[self->num].logging_on = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        log_disable(log);
 | 
			
		||||
 | 
			
		||||
        msg = "Logging disabled.";
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.";
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
    UNUSED_VAR(argc);
 | 
			
		||||
    UNUSED_VAR(argv);
 | 
			
		||||
 | 
			
		||||
    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 print ID.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef QRCODE
 | 
			
		||||
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    char id_string[TOX_ADDRESS_SIZE * 2 + 1];
 | 
			
		||||
    char bin_id[TOX_ADDRESS_SIZE];
 | 
			
		||||
    tox_self_get_address(m, (uint8_t *) bin_id);
 | 
			
		||||
 | 
			
		||||
    if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -444,51 +488,97 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
 | 
			
		||||
    nick[nick_len] = '\0';
 | 
			
		||||
 | 
			
		||||
    size_t data_file_len = strlen(DATA_FILE);
 | 
			
		||||
    char dir[data_file_len + 1];
 | 
			
		||||
    char *dir = malloc(data_file_len + 1);
 | 
			
		||||
 | 
			
		||||
    if (dir == NULL) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
#ifdef QRPNG
 | 
			
		||||
 | 
			
		||||
    FILE *output = fopen(qr_path, "wb");
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'");
 | 
			
		||||
        free(dir);
 | 
			
		||||
        return;
 | 
			
		||||
    } else if (!strcmp(argv[1], "txt")) {
 | 
			
		||||
 | 
			
		||||
    if (output == NULL) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
#endif /* QRPNG */
 | 
			
		||||
        size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT);
 | 
			
		||||
        char *qr_path = malloc(qr_path_buf_size);
 | 
			
		||||
 | 
			
		||||
        if (qr_path == NULL) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory");
 | 
			
		||||
            free(dir);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    if (ID_to_QRcode(id_string, output) == -1) {
 | 
			
		||||
        fclose(output);
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
        snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
 | 
			
		||||
 | 
			
		||||
        if (ID_to_QRcode_txt(id_string, qr_path) == -1) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
            free(dir);
 | 
			
		||||
            free(qr_path);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
 | 
			
		||||
 | 
			
		||||
    fclose(output);
 | 
			
		||||
        free(qr_path);
 | 
			
		||||
 | 
			
		||||
#ifdef QRPNG
 | 
			
		||||
    } else if (!strcmp(argv[1], "png")) {
 | 
			
		||||
        size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT_PNG);
 | 
			
		||||
        char *qr_path = malloc(qr_path_buf_size);
 | 
			
		||||
 | 
			
		||||
        if (qr_path == NULL) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory");
 | 
			
		||||
            free(dir);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG);
 | 
			
		||||
 | 
			
		||||
        if (ID_to_QRcode_png(id_string, qr_path) == -1) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
 | 
			
		||||
            free(dir);
 | 
			
		||||
            free(qr_path);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
 | 
			
		||||
 | 
			
		||||
        free(qr_path);
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]);
 | 
			
		||||
        free(dir);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif /* QRPNG */
 | 
			
		||||
 | 
			
		||||
    free(dir);
 | 
			
		||||
}
 | 
			
		||||
#endif /* QRCODE */
 | 
			
		||||
 | 
			
		||||
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char nick[MAX_STR_SIZE];
 | 
			
		||||
    size_t len = 0;
 | 
			
		||||
 | 
			
		||||
    if (argv[1][0] == '\"') {    /* remove opening and closing quotes */
 | 
			
		||||
        snprintf(nick, sizeof(nick), "%s", &argv[1][1]);
 | 
			
		||||
        len = strlen(nick) - 1;
 | 
			
		||||
        nick[len] = '\0';
 | 
			
		||||
    } else {
 | 
			
		||||
    snprintf(nick, sizeof(nick), "%s", argv[1]);
 | 
			
		||||
        len = strlen(nick);
 | 
			
		||||
    }
 | 
			
		||||
    size_t len = strlen(nick);
 | 
			
		||||
 | 
			
		||||
    if (!valid_nick(nick)) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -503,48 +593,70 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
 | 
			
		||||
 | 
			
		||||
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argv[1][0] != '\"') {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
 | 
			
		||||
        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';
 | 
			
		||||
 | 
			
		||||
    prompt_update_statusmessage(prompt, m, msg);
 | 
			
		||||
    prompt_update_statusmessage(prompt, m, argv[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    uint32_t nospam = rand();   /* should be random enough */
 | 
			
		||||
    tox_self_set_nospam(m, nospam);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your Tox ID has been changed to:");
 | 
			
		||||
    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, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value.");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint32_t old_nospam = tox_self_get_nospam(m);
 | 
			
		||||
    tox_self_set_nospam(m, (uint32_t) nospam);
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, false, 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, "Any services that relied on your old ID will need to be updated manually.");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
 | 
			
		||||
                  "Any services that relied on your old ID will need to be updated manually.");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'",
 | 
			
		||||
                  old_nospam);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
    UNUSED_VAR(m);
 | 
			
		||||
    UNUSED_VAR(argc);
 | 
			
		||||
    UNUSED_VAR(argv);
 | 
			
		||||
 | 
			
		||||
    help_init_menu(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
    UNUSED_VAR(argc);
 | 
			
		||||
    UNUSED_VAR(argv);
 | 
			
		||||
    UNUSED_VAR(self);
 | 
			
		||||
 | 
			
		||||
    exit_toxic_success(m);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
    UNUSED_VAR(m);
 | 
			
		||||
    UNUSED_VAR(argc);
 | 
			
		||||
    UNUSED_VAR(argv);
 | 
			
		||||
 | 
			
		||||
    if (FrndRequests.num_requests == 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -552,8 +664,9 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
 | 
			
		||||
    int count = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < FrndRequests.max_idx; ++i) {
 | 
			
		||||
        if (!FrndRequests.request[i].active)
 | 
			
		||||
        if (!FrndRequests.request[i].active) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
 | 
			
		||||
 | 
			
		||||
@@ -563,62 +676,49 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
 | 
			
		||||
            strcat(id, d);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
 | 
			
		||||
 | 
			
		||||
        if (++count < FrndRequests.num_requests)
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
        if (++count < FrndRequests.num_requests) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    bool have_note = false;
 | 
			
		||||
    UNUSED_VAR(window);
 | 
			
		||||
 | 
			
		||||
    const char *errmsg;
 | 
			
		||||
 | 
			
		||||
    lock_status ();
 | 
			
		||||
    lock_status();
 | 
			
		||||
 | 
			
		||||
    if (argc >= 2) {
 | 
			
		||||
        have_note = true;
 | 
			
		||||
    } else if (argc < 1) {
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        errmsg = "Require a status. Statuses are: online, busy and away.";
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *status_str = argv[1];
 | 
			
		||||
    TOX_USER_STATUS status;
 | 
			
		||||
    Tox_User_Status status;
 | 
			
		||||
 | 
			
		||||
    if (!strcasecmp(status_str, "online"))
 | 
			
		||||
    if (!strcasecmp(status_str, "online")) {
 | 
			
		||||
        status = TOX_USER_STATUS_NONE;
 | 
			
		||||
    else if (!strcasecmp(status_str, "away"))
 | 
			
		||||
    } else if (!strcasecmp(status_str, "away")) {
 | 
			
		||||
        status = TOX_USER_STATUS_AWAY;
 | 
			
		||||
    else if (!strcasecmp(status_str, "busy"))
 | 
			
		||||
    } else if (!strcasecmp(status_str, "busy")) {
 | 
			
		||||
        status = TOX_USER_STATUS_BUSY;
 | 
			
		||||
    else {
 | 
			
		||||
    } else {
 | 
			
		||||
        errmsg = "Invalid status. Valid statuses are: online, busy and away.";
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tox_self_set_status(m, status);
 | 
			
		||||
    prompt_update_status(prompt, status);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
 | 
			
		||||
 | 
			
		||||
    if (have_note) {
 | 
			
		||||
        if (argv[2][0] != '\"') {
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
 | 
			
		||||
            goto finish;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* remove opening and closing quotes */
 | 
			
		||||
        char msg[MAX_STR_SIZE];
 | 
			
		||||
        snprintf(msg, sizeof(msg), "%s", &argv[2][1]);
 | 
			
		||||
        int len = strlen(msg) - 1;
 | 
			
		||||
        msg[len] = '\0';
 | 
			
		||||
 | 
			
		||||
        prompt_update_statusmessage(prompt, m, msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
finish:
 | 
			
		||||
    unlock_status ();
 | 
			
		||||
    unlock_status();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,8 @@
 | 
			
		||||
#ifndef GLOBAL_COMMANDS_H
 | 
			
		||||
#define GLOBAL_COMMANDS_H
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
@@ -32,10 +32,12 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
 | 
			
		||||
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_conference(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#ifdef QRCODE
 | 
			
		||||
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* QRCODE */
 | 
			
		||||
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_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]);
 | 
			
		||||
@@ -56,4 +58,8 @@ void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)
 | 
			
		||||
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#endif /* #define GLOBAL_COMMANDS_H */
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
#endif /* GLOBAL_COMMANDS_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
/*  group_commands.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    char title[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");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argv[1][0] != '\"') {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* remove opening and closing quotes */
 | 
			
		||||
    snprintf(title, sizeof(title), "%s", &argv[1][1]);
 | 
			
		||||
    int len = strlen(title) - 1;
 | 
			
		||||
    title[len] = '\0';
 | 
			
		||||
 | 
			
		||||
    if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title.");
 | 
			
		||||
        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);
 | 
			
		||||
    selfnick[sn_len] = '\0';
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
 | 
			
		||||
 | 
			
		||||
    char tmp_event[MAX_STR_SIZE];
 | 
			
		||||
    snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
 | 
			
		||||
    write_to_log(tmp_event, selfnick, self->chatwin->log, true);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										908
									
								
								src/groupchat.c
									
									
									
									
									
								
							
							
						
						
									
										908
									
								
								src/groupchat.c
									
									
									
									
									
								
							@@ -1,908 +0,0 @@
 | 
			
		||||
/*  groupchat.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _GNU_SOURCE
 | 
			
		||||
#define _GNU_SOURCE    /* needed for strcasestr() and wcswidth() */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <wchar.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#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 */
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "groupchat.h"
 | 
			
		||||
#include "prompt.h"
 | 
			
		||||
#include "toxic_strings.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "input.h"
 | 
			
		||||
#include "help.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "autocomplete.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
 | 
			
		||||
extern char *DATA_FILE;
 | 
			
		||||
 | 
			
		||||
static GroupChat groupchats[MAX_GROUPCHAT_NUM];
 | 
			
		||||
static int max_groupchat_index = 0;
 | 
			
		||||
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
extern struct Winthread Winthread;
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#define AC_NUM_GROUP_COMMANDS 24
 | 
			
		||||
#else
 | 
			
		||||
#define AC_NUM_GROUP_COMMANDS 20
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
/* Array of groupchat command names used for tab completion. */
 | 
			
		||||
static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
 | 
			
		||||
    { "/accept"     },
 | 
			
		||||
    { "/add"        },
 | 
			
		||||
    { "/avatar"     },
 | 
			
		||||
    { "/clear"      },
 | 
			
		||||
    { "/close"      },
 | 
			
		||||
    { "/connect"    },
 | 
			
		||||
    { "/decline"    },
 | 
			
		||||
    { "/exit"       },
 | 
			
		||||
    { "/group"      },
 | 
			
		||||
    { "/help"       },
 | 
			
		||||
    { "/log"        },
 | 
			
		||||
    { "/myid"       },
 | 
			
		||||
    { "/myqr"       },
 | 
			
		||||
    { "/nick"       },
 | 
			
		||||
    { "/note"       },
 | 
			
		||||
    { "/nospam"     },
 | 
			
		||||
    { "/quit"       },
 | 
			
		||||
    { "/requests"   },
 | 
			
		||||
    { "/status"     },
 | 
			
		||||
    { "/title"      },
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
    { "/lsdev"       },
 | 
			
		||||
    { "/sdev"        },
 | 
			
		||||
    { "/mute"        },
 | 
			
		||||
    { "/sense"       },
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
static int group_audio_open_out_device(int groupnum);
 | 
			
		||||
static int group_audio_close_out_device(int groupnum);
 | 
			
		||||
#endif  /* AUDIO */
 | 
			
		||||
 | 
			
		||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type)
 | 
			
		||||
{
 | 
			
		||||
    if (groupnum > MAX_GROUPCHAT_NUM)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    ToxWindow self = new_group_chat(m, groupnum);
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i <= max_groupchat_index; ++i) {
 | 
			
		||||
        if (!groupchats[i].active) {
 | 
			
		||||
            groupchats[i].chatwin = add_window(m, self);
 | 
			
		||||
            groupchats[i].active = true;
 | 
			
		||||
            groupchats[i].num_peers = 0;
 | 
			
		||||
            groupchats[i].type = type;
 | 
			
		||||
            groupchats[i].start_time = get_unix_time();
 | 
			
		||||
 | 
			
		||||
            groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
 | 
			
		||||
            groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
 | 
			
		||||
            groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
 | 
			
		||||
            groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
 | 
			
		||||
 | 
			
		||||
            if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL
 | 
			
		||||
                || groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL)
 | 
			
		||||
                exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY);
 | 
			
		||||
 | 
			
		||||
            memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
 | 
			
		||||
            groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
            if (type == TOX_GROUPCHAT_TYPE_AV)
 | 
			
		||||
                if (group_audio_open_out_device(i) == -1)
 | 
			
		||||
                    fprintf(stderr, "Group Audio failed to init\n");
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
            set_active_window(groupchats[i].chatwin);
 | 
			
		||||
 | 
			
		||||
            if (i == max_groupchat_index)
 | 
			
		||||
                ++max_groupchat_index;
 | 
			
		||||
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void kill_groupchat_window(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    log_disable(ctx->log);
 | 
			
		||||
    line_info_cleanup(ctx->hst);
 | 
			
		||||
    delwin(ctx->linewin);
 | 
			
		||||
    delwin(ctx->history);
 | 
			
		||||
    delwin(ctx->sidebar);
 | 
			
		||||
    free(ctx->log);
 | 
			
		||||
    free(ctx);
 | 
			
		||||
    free(self->help);
 | 
			
		||||
    del_window(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
 | 
			
		||||
{
 | 
			
		||||
    tox_del_groupchat(m, groupnum);
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    group_audio_close_out_device(groupnum);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    free(groupchats[groupnum].peer_names);
 | 
			
		||||
    free(groupchats[groupnum].oldpeer_names);
 | 
			
		||||
    free(groupchats[groupnum].peer_name_lengths);
 | 
			
		||||
    free(groupchats[groupnum].oldpeer_name_lengths);
 | 
			
		||||
    memset(&groupchats[groupnum], 0, sizeof(GroupChat));
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = max_groupchat_index; i > 0; --i) {
 | 
			
		||||
        if (groupchats[i - 1].active)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    max_groupchat_index = i;
 | 
			
		||||
    kill_groupchat_window(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* destroys and re-creates groupchat window with or without the peerlist */
 | 
			
		||||
void redraw_groupchat_win(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    endwin();
 | 
			
		||||
    refresh();
 | 
			
		||||
    clear();
 | 
			
		||||
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(stdscr, y2, x2);
 | 
			
		||||
    y2 -= 2;
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (ctx->sidebar) {
 | 
			
		||||
        delwin(ctx->sidebar);
 | 
			
		||||
        ctx->sidebar = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    delwin(ctx->linewin);
 | 
			
		||||
    delwin(ctx->history);
 | 
			
		||||
    delwin(self->window);
 | 
			
		||||
 | 
			
		||||
    self->window = newwin(y2, x2, 0, 0);
 | 
			
		||||
    ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
 | 
			
		||||
 | 
			
		||||
    if (self->show_peerlist) {
 | 
			
		||||
        ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
 | 
			
		||||
        ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
 | 
			
		||||
    } else {
 | 
			
		||||
        ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    scrollok(ctx->history, 0);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
 | 
			
		||||
                                     const char *msg, uint16_t len)
 | 
			
		||||
{
 | 
			
		||||
    if (self->num != groupnum)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    char nick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    get_group_nick_truncate(m, nick, peernum, groupnum);
 | 
			
		||||
 | 
			
		||||
    char selfnick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    tox_self_get_name(m, (uint8_t *) selfnick);
 | 
			
		||||
 | 
			
		||||
    size_t sn_len = tox_self_get_name_size(m);
 | 
			
		||||
    selfnick[sn_len] = '\0';
 | 
			
		||||
 | 
			
		||||
    int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
 | 
			
		||||
 | 
			
		||||
    /* Only play sound if mentioned by someone else */
 | 
			
		||||
    if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
 | 
			
		||||
        sound_notify(self, generic_message, NT_WNDALERT_0, NULL);
 | 
			
		||||
 | 
			
		||||
        if (self->active_box != -1)
 | 
			
		||||
            box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg);
 | 
			
		||||
        else
 | 
			
		||||
            box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
 | 
			
		||||
 | 
			
		||||
        nick_clr = RED;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        sound_notify(self, silent, NT_WNDALERT_1, NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, nick_clr, "%s", msg);
 | 
			
		||||
    write_to_log(msg, nick, ctx->log, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *action,
 | 
			
		||||
                                    uint16_t len)
 | 
			
		||||
{
 | 
			
		||||
    if (self->num != groupnum)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    char nick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    get_group_nick_truncate(m, nick, peernum, groupnum);
 | 
			
		||||
 | 
			
		||||
    char selfnick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    tox_self_get_name(m, (uint8_t *) selfnick);
 | 
			
		||||
 | 
			
		||||
    size_t n_len = tox_self_get_name_size(m);
 | 
			
		||||
    selfnick[n_len] = '\0';
 | 
			
		||||
 | 
			
		||||
    if (strcasestr(action, selfnick)) {
 | 
			
		||||
        sound_notify(self, generic_message, NT_WNDALERT_0, NULL);
 | 
			
		||||
 | 
			
		||||
        if (self->active_box != -1)
 | 
			
		||||
            box_silent_notify2(self, NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
 | 
			
		||||
        else
 | 
			
		||||
            box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        sound_notify(self, silent, NT_WNDALERT_1, NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
 | 
			
		||||
    write_to_log(action, nick, ctx->log, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *title,
 | 
			
		||||
                                         uint8_t length)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (self->num != groupnum)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    set_window_title(self, title, length);
 | 
			
		||||
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
 | 
			
		||||
    /* don't announce title when we join the room */
 | 
			
		||||
    if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    char nick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    get_group_nick_truncate(m, nick, peernum, groupnum);
 | 
			
		||||
    line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
 | 
			
		||||
 | 
			
		||||
    char tmp_event[MAX_STR_SIZE];
 | 
			
		||||
    snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
 | 
			
		||||
    write_to_log(tmp_event, nick, ctx->log, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Puts two copies of peerlist/lengths in chat instance */
 | 
			
		||||
static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers)
 | 
			
		||||
{
 | 
			
		||||
    /* Assumes these are initiated in init_groupchat_win */
 | 
			
		||||
    free(groupchats[gnum].peer_names);
 | 
			
		||||
    free(groupchats[gnum].oldpeer_names);
 | 
			
		||||
    free(groupchats[gnum].peer_name_lengths);
 | 
			
		||||
    free(groupchats[gnum].oldpeer_name_lengths);
 | 
			
		||||
 | 
			
		||||
    int N = TOX_MAX_NAME_LENGTH;
 | 
			
		||||
 | 
			
		||||
    groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
 | 
			
		||||
    groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
 | 
			
		||||
    groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
 | 
			
		||||
    groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers);
 | 
			
		||||
 | 
			
		||||
    if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
 | 
			
		||||
        || groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint16_t u_len = strlen(UNKNOWN_NAME);
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < npeers; ++i) {
 | 
			
		||||
         if (!lengths[i]) {
 | 
			
		||||
            memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len);
 | 
			
		||||
            groupchats[gnum].peer_names[i * N + u_len] = '\0';
 | 
			
		||||
            groupchats[gnum].peer_name_lengths[i] = u_len;
 | 
			
		||||
        } else {
 | 
			
		||||
            uint16_t n_len = MIN(lengths[i], TOXIC_MAX_NAME_LENGTH - 1);
 | 
			
		||||
            memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], n_len);
 | 
			
		||||
            groupchats[gnum].peer_names[i * N + n_len] = '\0';
 | 
			
		||||
            groupchats[gnum].peer_name_lengths[i] = n_len;
 | 
			
		||||
            filter_str((char *) &groupchats[gnum].peer_names[i * N], n_len);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
 | 
			
		||||
    memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths, sizeof(uint16_t) * npeers);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct group_add_thrd {
 | 
			
		||||
    Tox *m;
 | 
			
		||||
    ToxWindow *self;
 | 
			
		||||
    int peernum;
 | 
			
		||||
    int groupnum;
 | 
			
		||||
    uint64_t timestamp;
 | 
			
		||||
    pthread_t tid;
 | 
			
		||||
    pthread_attr_t attr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Waits GROUP_EVENT_WAIT seconds for a new peer to set their name before announcing them */
 | 
			
		||||
void *group_add_wait(void *data)
 | 
			
		||||
{
 | 
			
		||||
    struct group_add_thrd *thrd = (struct group_add_thrd *) data;
 | 
			
		||||
    ToxWindow *self = thrd->self;
 | 
			
		||||
    Tox *m = thrd->m;
 | 
			
		||||
    char peername[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
 | 
			
		||||
    /* keep polling for a name that differs from the default until we run out of time */
 | 
			
		||||
    while (true) {
 | 
			
		||||
        usleep(100000);
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
        get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
 | 
			
		||||
 | 
			
		||||
        if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, GROUP_EVENT_WAIT)) {
 | 
			
		||||
            pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *event = "has joined the room";
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
 | 
			
		||||
    write_to_log(event, (char *) peername, self->chatwin->log, true);
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    pthread_attr_destroy(&thrd->attr);
 | 
			
		||||
    free(thrd);
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t change)
 | 
			
		||||
{
 | 
			
		||||
    if (self->num != groupnum)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (groupnum > max_groupchat_index)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
 | 
			
		||||
    int num_peers = groupchats[groupnum].num_peers;
 | 
			
		||||
 | 
			
		||||
    if (peernum > num_peers)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    /* get old peer name before updating name list */
 | 
			
		||||
    uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
 | 
			
		||||
    if (change != TOX_CHAT_CHANGE_PEER_ADD) {
 | 
			
		||||
        memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH],
 | 
			
		||||
               sizeof(oldpeername));
 | 
			
		||||
        uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
 | 
			
		||||
        oldpeername[old_n_len] = '\0';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Update name/len lists */
 | 
			
		||||
    uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
 | 
			
		||||
    uint16_t tmp_peerlens[num_peers];
 | 
			
		||||
 | 
			
		||||
    if (tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers) == -1) {
 | 
			
		||||
        memset(tmp_peerlist, 0, sizeof(tmp_peerlist));
 | 
			
		||||
        memset(tmp_peerlens, 0, sizeof(tmp_peerlens));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
 | 
			
		||||
 | 
			
		||||
    /* get current peername then sort namelist */
 | 
			
		||||
    uint8_t peername[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
 | 
			
		||||
    if (change != TOX_CHAT_CHANGE_PEER_DEL) {
 | 
			
		||||
        uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
 | 
			
		||||
        memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
 | 
			
		||||
        peername[n_len] = '\0';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    const char *event;
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
 | 
			
		||||
    switch (change) {
 | 
			
		||||
        case TOX_CHAT_CHANGE_PEER_ADD:
 | 
			
		||||
            if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT))
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd));
 | 
			
		||||
            thrd->m = m;
 | 
			
		||||
            thrd->peernum = peernum;
 | 
			
		||||
            thrd->groupnum = groupnum;
 | 
			
		||||
            thrd->self = self;
 | 
			
		||||
            thrd->timestamp = get_unix_time();
 | 
			
		||||
 | 
			
		||||
            if (pthread_attr_init(&thrd->attr) != 0) {
 | 
			
		||||
                free(thrd);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (pthread_attr_setdetachstate(&thrd->attr, PTHREAD_CREATE_DETACHED) != 0) {
 | 
			
		||||
                pthread_attr_destroy(&thrd->attr);
 | 
			
		||||
                free(thrd);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (pthread_create(&thrd->tid, &thrd->attr, group_add_wait, (void *) thrd) != 0) {
 | 
			
		||||
                pthread_attr_destroy(&thrd->attr);
 | 
			
		||||
                free(thrd);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TOX_CHAT_CHANGE_PEER_DEL:
 | 
			
		||||
            event = "has left the room";
 | 
			
		||||
            line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event);
 | 
			
		||||
 | 
			
		||||
            if (groupchats[self->num].side_pos > 0)
 | 
			
		||||
                --groupchats[self->num].side_pos;
 | 
			
		||||
 | 
			
		||||
            write_to_log(event, (char *) oldpeername, ctx->log, true);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TOX_CHAT_CHANGE_PEER_NAME:
 | 
			
		||||
            if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            /* ignore initial name change (TODO: this is a bad way to do this) */
 | 
			
		||||
            if (strcmp((char *) oldpeername, DEFAULT_TOX_NAME) == 0)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            event = " is now known as ";
 | 
			
		||||
            line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, NAME_CHANGE, 0, 0, event);
 | 
			
		||||
 | 
			
		||||
            char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
 | 
			
		||||
            snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
 | 
			
		||||
            write_to_log(tmp_event, (char *) oldpeername, ctx->log, true);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sound_notify(self, silent, NT_WNDALERT_2, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
 | 
			
		||||
{
 | 
			
		||||
    if (action == NULL) {
 | 
			
		||||
        wprintw(ctx->history, "Invalid syntax.\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) {
 | 
			
		||||
        const char *errmsg = " * Failed to send action.";
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    int x, y, y2, x2;
 | 
			
		||||
    getyx(self->window, y, x);
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (x2 <= 0 || y2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (self->help->active) {
 | 
			
		||||
        help_onKey(self, key);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ltr) {    /* char is printable */
 | 
			
		||||
        input_new_char(self, key, x, y, x2, y2);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (line_info_onKey(self, key))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (input_handle(self, key, x, y, x2, y2))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (key == '\t') {  /* TAB key: auto-completes peer name or command */
 | 
			
		||||
        if (ctx->len > 0) {
 | 
			
		||||
            int diff;
 | 
			
		||||
 | 
			
		||||
            /* TODO: make this not suck */
 | 
			
		||||
            if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) {
 | 
			
		||||
                diff = complete_line(self, groupchats[self->num].peer_names, groupchats[self->num].num_peers,
 | 
			
		||||
                                     TOX_MAX_NAME_LENGTH);
 | 
			
		||||
            } else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
 | 
			
		||||
                diff = dir_match(self, m, ctx->line, L"/avatar");
 | 
			
		||||
            } else {
 | 
			
		||||
                diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (diff != -1) {
 | 
			
		||||
                if (x + diff > x2 - 1) {
 | 
			
		||||
                    int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
 | 
			
		||||
                    ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                sound_notify(self, notif_error, 0, NULL);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            sound_notify(self, notif_error, 0, NULL);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (key == user_settings->key_peer_list_down) {    /* Scroll peerlist up and down one position */
 | 
			
		||||
        int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
 | 
			
		||||
 | 
			
		||||
        if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
 | 
			
		||||
            ++groupchats[self->num].side_pos;
 | 
			
		||||
    } else if (key == user_settings->key_peer_list_up) {
 | 
			
		||||
        if (groupchats[self->num].side_pos > 0)
 | 
			
		||||
            --groupchats[self->num].side_pos;
 | 
			
		||||
    } else if (key == '\n') {
 | 
			
		||||
        rm_trailing_spaces_buf(ctx);
 | 
			
		||||
 | 
			
		||||
        char line[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
        if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
 | 
			
		||||
            memset(&line, 0, sizeof(line));
 | 
			
		||||
 | 
			
		||||
        if (!string_is_empty(line))
 | 
			
		||||
            add_line_to_hist(ctx);
 | 
			
		||||
 | 
			
		||||
        if (line[0] == '/') {
 | 
			
		||||
            if (strcmp(line, "/close") == 0) {
 | 
			
		||||
                close_groupchat(self, m, self->num);
 | 
			
		||||
                return;
 | 
			
		||||
            } else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
 | 
			
		||||
                send_group_action(self, ctx, m, line + strlen("/me "));
 | 
			
		||||
            } else {
 | 
			
		||||
                execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!string_is_empty(line)) {
 | 
			
		||||
            if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) {
 | 
			
		||||
                const char *errmsg = " * Failed to send message.";
 | 
			
		||||
                line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wclear(ctx->linewin);
 | 
			
		||||
        wmove(self->window, y2 - CURS_Y_OFFSET, 0);
 | 
			
		||||
        reset_buf(ctx);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void groupchat_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (x2 <= 0 || y2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    line_info_print(self);
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    wclear(ctx->linewin);
 | 
			
		||||
 | 
			
		||||
    curs_set(1);
 | 
			
		||||
 | 
			
		||||
    if (ctx->len > 0)
 | 
			
		||||
        mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
 | 
			
		||||
 | 
			
		||||
    wclear(ctx->sidebar);
 | 
			
		||||
    mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
 | 
			
		||||
 | 
			
		||||
    if (self->show_peerlist) {
 | 
			
		||||
        mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
 | 
			
		||||
        mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
 | 
			
		||||
 | 
			
		||||
        int num_peers = groupchats[self->num].num_peers;
 | 
			
		||||
 | 
			
		||||
        wmove(ctx->sidebar, 0, 1);
 | 
			
		||||
        wattron(ctx->sidebar, A_BOLD);
 | 
			
		||||
        wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
 | 
			
		||||
        wattroff(ctx->sidebar, A_BOLD);
 | 
			
		||||
 | 
			
		||||
        mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
 | 
			
		||||
        mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
 | 
			
		||||
 | 
			
		||||
        int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < num_peers && i < maxlines; ++i) {
 | 
			
		||||
            wmove(ctx->sidebar, i + 2, 1);
 | 
			
		||||
            int peer = i + groupchats[self->num].side_pos;
 | 
			
		||||
 | 
			
		||||
            /* truncate nick to fit in side panel without modifying list */
 | 
			
		||||
            char tmpnck[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
            int maxlen = SIDEBAR_WIDTH - 2;
 | 
			
		||||
            memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen);
 | 
			
		||||
            tmpnck[maxlen] = '\0';
 | 
			
		||||
 | 
			
		||||
            wprintw(ctx->sidebar, "%s\n", tmpnck);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int y, x;
 | 
			
		||||
    getyx(self->window, y, x);
 | 
			
		||||
    (void) x;
 | 
			
		||||
    int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
 | 
			
		||||
    wmove(self->window, y + 1, new_x);
 | 
			
		||||
 | 
			
		||||
    wrefresh(self->window);
 | 
			
		||||
 | 
			
		||||
    if (self->help->active)
 | 
			
		||||
        help_onDraw(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void groupchat_onInit(ToxWindow *self, Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (x2 <= 0 || y2 <= 0)
 | 
			
		||||
        exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES);
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
 | 
			
		||||
    ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
 | 
			
		||||
    ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
 | 
			
		||||
 | 
			
		||||
    ctx->hst = calloc(1, sizeof(struct history));
 | 
			
		||||
    ctx->log = calloc(1, sizeof(struct chatlog));
 | 
			
		||||
 | 
			
		||||
    if (ctx->log == NULL || ctx->hst == NULL)
 | 
			
		||||
        exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY);
 | 
			
		||||
 | 
			
		||||
    line_info_init(ctx->hst);
 | 
			
		||||
 | 
			
		||||
    if (user_settings->autolog == AUTOLOG_ON) {
 | 
			
		||||
        char myid[TOX_ADDRESS_SIZE];
 | 
			
		||||
        tox_self_get_address(m, (uint8_t *) myid);
 | 
			
		||||
 | 
			
		||||
        if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1)
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
 | 
			
		||||
 | 
			
		||||
    scrollok(ctx->history, 0);
 | 
			
		||||
    wmove(self->window, y2 - CURS_Y_OFFSET, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
static int group_audio_open_out_device(int groupnum)
 | 
			
		||||
{
 | 
			
		||||
    char dname[MAX_STR_SIZE];
 | 
			
		||||
    get_primary_device_name(output, dname, sizeof(dname));
 | 
			
		||||
    dname[MAX_STR_SIZE - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
    groupchats[groupnum].audio.dvhandle = alcOpenDevice(dname);
 | 
			
		||||
 | 
			
		||||
    if (groupchats[groupnum].audio.dvhandle == NULL)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    groupchats[groupnum].audio.dvctx = alcCreateContext(groupchats[groupnum].audio.dvhandle, NULL);
 | 
			
		||||
    alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
 | 
			
		||||
    alGenBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
 | 
			
		||||
    alGenSources((uint32_t) 1, &groupchats[groupnum].audio.source);
 | 
			
		||||
    alSourcei(groupchats[groupnum].audio.source, AL_LOOPING, AL_FALSE);
 | 
			
		||||
 | 
			
		||||
    if (alcGetError(groupchats[groupnum].audio.dvhandle) != AL_NO_ERROR) {
 | 
			
		||||
        group_audio_close_out_device(groupnum);
 | 
			
		||||
        groupchats[groupnum].audio.dvhandle = NULL;
 | 
			
		||||
        groupchats[groupnum].audio.dvctx = NULL;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alSourceQueueBuffers(groupchats[groupnum].audio.source, OPENAL_BUFS, groupchats[groupnum].audio.buffers);
 | 
			
		||||
    alSourcePlay(groupchats[groupnum].audio.source);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int group_audio_close_out_device(int groupnum)
 | 
			
		||||
{
 | 
			
		||||
    if (!groupchats[groupnum].audio.dvhandle)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if (!groupchats[groupnum].audio.dvctx)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if (alcGetCurrentContext() != groupchats[groupnum].audio.dvctx)
 | 
			
		||||
        alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
 | 
			
		||||
 | 
			
		||||
    alDeleteSources((uint32_t) 1, &groupchats[groupnum].audio.source);
 | 
			
		||||
    alDeleteBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
 | 
			
		||||
 | 
			
		||||
    alcMakeContextCurrent(NULL);
 | 
			
		||||
    alcDestroyContext(groupchats[groupnum].audio.dvctx);
 | 
			
		||||
 | 
			
		||||
    if (!alcCloseDevice(groupchats[groupnum].audio.dvhandle))
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static int group_audio_write(int peernum, int groupnum, const int16_t *pcm, unsigned int samples, uint8_t channels,
 | 
			
		||||
//                              unsigned int sample_rate)
 | 
			
		||||
// {
 | 
			
		||||
//     if (!pcm)
 | 
			
		||||
//         return -1;
 | 
			
		||||
 | 
			
		||||
//     if (channels == 0 || channels > 2)
 | 
			
		||||
//         return -2;
 | 
			
		||||
 | 
			
		||||
//     ALuint bufid;
 | 
			
		||||
//     ALint processed = 0, queued = 0;
 | 
			
		||||
 | 
			
		||||
//     alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_PROCESSED, &processed);
 | 
			
		||||
//     alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_QUEUED, &queued);
 | 
			
		||||
//     fprintf(stderr, "source: %d, queued: %d, processed: %d\n", groupchats[groupnum].audio.source, queued, processed);
 | 
			
		||||
 | 
			
		||||
//     if (processed) {
 | 
			
		||||
//         ALuint bufids[processed];
 | 
			
		||||
//         alSourceUnqueueBuffers(groupchats[groupnum].audio.source, processed, bufids);
 | 
			
		||||
//         alDeleteBuffers(processed - 1, bufids + 1);
 | 
			
		||||
//         bufid = bufids[0];
 | 
			
		||||
//     } else if (queued < 16) {
 | 
			
		||||
//         alGenBuffers(1, &bufid);
 | 
			
		||||
//     } else {
 | 
			
		||||
//         return -3;
 | 
			
		||||
//     }
 | 
			
		||||
 | 
			
		||||
//     int length = samples * channels * sizeof(int16_t);
 | 
			
		||||
 | 
			
		||||
//     alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, pcm, length, sample_rate);
 | 
			
		||||
//     alSourceQueueBuffers(groupchats[groupnum].audio.source, 1, &bufid);
 | 
			
		||||
 | 
			
		||||
//     ALint state;
 | 
			
		||||
//     alGetSourcei(groupchats[groupnum].audio.source, AL_SOURCE_STATE, &state);
 | 
			
		||||
 | 
			
		||||
//     if (state != AL_PLAYING)
 | 
			
		||||
//         alSourcePlay(groupchats[groupnum].audio.source);
 | 
			
		||||
 | 
			
		||||
//     return 0;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
static void groupchat_onWriteDevice(ToxWindow *self, Tox *m, uint32_t groupnum, int peernum, const int16_t *pcm,
 | 
			
		||||
                                    unsigned int samples, uint8_t channels, unsigned int sample_rate)
 | 
			
		||||
{
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
    // if (groupnum != self->num)
 | 
			
		||||
    //     return;
 | 
			
		||||
 | 
			
		||||
    // if (peernum < 0)
 | 
			
		||||
    //     return;
 | 
			
		||||
 | 
			
		||||
    // if (groupchats[groupnum].audio.dvhandle == NULL)
 | 
			
		||||
    //     fprintf(stderr, "dvhandle is null)\n");
 | 
			
		||||
 | 
			
		||||
    // if (groupchats[groupnum].audio.dvctx == NULL)
 | 
			
		||||
    //     fprintf(stderr, "ctx is null\n");
 | 
			
		||||
 | 
			
		||||
    // int ret = group_audio_write(peernum, groupnum, pcm, samples, channels, sample_rate);
 | 
			
		||||
    // fprintf(stderr, "write: %d\n", ret);
 | 
			
		||||
}
 | 
			
		||||
#endif  /* AUDIO */
 | 
			
		||||
 | 
			
		||||
ToxWindow new_group_chat(Tox *m, int groupnum)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow ret;
 | 
			
		||||
    memset(&ret, 0, sizeof(ret));
 | 
			
		||||
 | 
			
		||||
    ret.active = true;
 | 
			
		||||
    ret.is_groupchat = true;
 | 
			
		||||
 | 
			
		||||
    ret.onKey = &groupchat_onKey;
 | 
			
		||||
    ret.onDraw = &groupchat_onDraw;
 | 
			
		||||
    ret.onInit = &groupchat_onInit;
 | 
			
		||||
    ret.onGroupMessage = &groupchat_onGroupMessage;
 | 
			
		||||
    ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
 | 
			
		||||
    ret.onGroupAction = &groupchat_onGroupAction;
 | 
			
		||||
    ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    ret.onWriteDevice = &groupchat_onWriteDevice;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
 | 
			
		||||
 | 
			
		||||
    ChatContext *chatwin = calloc(1, sizeof(ChatContext));
 | 
			
		||||
    Help *help = calloc(1, sizeof(Help));
 | 
			
		||||
 | 
			
		||||
    if (chatwin == NULL || help == NULL)
 | 
			
		||||
        exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
 | 
			
		||||
 | 
			
		||||
    ret.chatwin = chatwin;
 | 
			
		||||
    ret.help = help;
 | 
			
		||||
 | 
			
		||||
    ret.num = groupnum;
 | 
			
		||||
    ret.show_peerlist = true;
 | 
			
		||||
    ret.active_box = -1;
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
/*  groupchat.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef GROUPCHAT_H
 | 
			
		||||
#define GROUPCHAT_H
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
#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];
 | 
			
		||||
};
 | 
			
		||||
#endif  /* AUDIO */
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int chatwin;
 | 
			
		||||
    bool active;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    int num_peers;
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
/* destroys and re-creates groupchat window with or without the peerlist */
 | 
			
		||||
void redraw_groupchat_win(ToxWindow *self);
 | 
			
		||||
 | 
			
		||||
ToxWindow new_group_chat(Tox *m, int groupnum);
 | 
			
		||||
 | 
			
		||||
#endif /* #define GROUPCHAT_H */
 | 
			
		||||
							
								
								
									
										192
									
								
								src/help.c
									
									
									
									
									
								
							
							
						
						
									
										192
									
								
								src/help.c
									
									
									
									
									
								
							@@ -22,24 +22,34 @@
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "help.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
#include "api.h"
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
#define HELP_MENU_HEIGHT 10
 | 
			
		||||
#else
 | 
			
		||||
#define HELP_MENU_HEIGHT 9
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
#define HELP_MENU_WIDTH 26
 | 
			
		||||
 | 
			
		||||
void help_init_menu(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    if (self->help->win)
 | 
			
		||||
    if (self->help->win) {
 | 
			
		||||
        delwin(self->help->win);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int y2, x2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH)
 | 
			
		||||
    if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
 | 
			
		||||
    self->help->active = true;
 | 
			
		||||
@@ -49,19 +59,24 @@ void help_init_menu(ToxWindow *self)
 | 
			
		||||
static void help_exit(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    delwin(self->help->win);
 | 
			
		||||
    memset(self->help, 0, sizeof(Help));
 | 
			
		||||
 | 
			
		||||
    *(self->help) = (struct Help) {
 | 
			
		||||
        0
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void help_init_window(ToxWindow *self, int height, int width)
 | 
			
		||||
{
 | 
			
		||||
    if (self->help->win)
 | 
			
		||||
    if (self->help->win) {
 | 
			
		||||
        delwin(self->help->win);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int y2, x2;
 | 
			
		||||
    getmaxyx(stdscr, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    height = MIN(height, y2);
 | 
			
		||||
    width = MIN(width, x2);
 | 
			
		||||
@@ -89,11 +104,18 @@ static void help_draw_menu(ToxWindow *self)
 | 
			
		||||
    wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
 | 
			
		||||
    wprintw(win, "hat commands\n");
 | 
			
		||||
 | 
			
		||||
    wprintw(win, " g");
 | 
			
		||||
    wprintw(win, " c");
 | 
			
		||||
    wattron(win, A_BOLD | COLOR_PAIR(BLUE));
 | 
			
		||||
    wprintw(win, "r");
 | 
			
		||||
    wprintw(win, "o");
 | 
			
		||||
    wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
 | 
			
		||||
    wprintw(win, "oup commands\n");
 | 
			
		||||
    wprintw(win, "nference commands\n");
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
    wattron(win, A_BOLD | COLOR_PAIR(BLUE));
 | 
			
		||||
    wprintw(win, " p");
 | 
			
		||||
    wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
 | 
			
		||||
    wprintw(win, "lugin commands\n");
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
    wattron(win, A_BOLD | COLOR_PAIR(BLUE));
 | 
			
		||||
    wprintw(win, " f");
 | 
			
		||||
@@ -112,14 +134,15 @@ static void help_draw_menu(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "it menu\n");
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
    wrefresh(win);
 | 
			
		||||
    wnoutrefresh(win);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void help_draw_bottom_menu(WINDOW *win)
 | 
			
		||||
{
 | 
			
		||||
    int y2, x2;
 | 
			
		||||
    getmaxyx(win, y2, x2);
 | 
			
		||||
    (void) x2;
 | 
			
		||||
 | 
			
		||||
    UNUSED_VAR(x2);
 | 
			
		||||
 | 
			
		||||
    wmove(win, y2 - 2, 1);
 | 
			
		||||
 | 
			
		||||
@@ -154,11 +177,17 @@ static void help_draw_global(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "  /status <type> <msg>       : Set status with optional note\n");
 | 
			
		||||
    wprintw(win, "  /note <msg>                : Set a personal note\n");
 | 
			
		||||
    wprintw(win, "  /nick <nick>               : Set your nickname\n");
 | 
			
		||||
    wprintw(win, "  /nospam                    : Change part of your Tox ID to stop spam\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, "  /conference <type>         : Create a conference where type: text | audio\n");
 | 
			
		||||
    wprintw(win, "  /myid                      : Print your Tox ID\n");
 | 
			
		||||
#ifdef QRCODE
 | 
			
		||||
#ifdef QRPNG
 | 
			
		||||
    wprintw(win, "  /myqr <txt> or <png>       : Print your Tox ID's QR code to a file.\n");
 | 
			
		||||
#else
 | 
			
		||||
    wprintw(win, "  /myqr                      : Print your Tox ID's QR code to a file.\n");
 | 
			
		||||
#endif /* QRPNG */
 | 
			
		||||
#endif /* QRCODE */
 | 
			
		||||
    wprintw(win, "  /clear                     : Clear window history\n");
 | 
			
		||||
    wprintw(win, "  /close                     : Close the current chat window\n");
 | 
			
		||||
    wprintw(win, "  /quit or /exit             : Exit Toxic\n");
 | 
			
		||||
@@ -181,10 +210,18 @@ static void help_draw_global(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "  /svdev <type> <id>         : Set active video device\n");
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
    wattron(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "\n Scripting:\n");
 | 
			
		||||
    wattroff(win, A_BOLD);
 | 
			
		||||
 | 
			
		||||
    wprintw(win, "  /run <path>                : Load and run the script at path\n");
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
    wrefresh(win);
 | 
			
		||||
    wnoutrefresh(win);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void help_draw_chat(ToxWindow *self)
 | 
			
		||||
@@ -197,8 +234,8 @@ 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, "  /invite <n>                : Invite contact to a conference \n");
 | 
			
		||||
    wprintw(win, "  /join                      : Join a pending conference\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");
 | 
			
		||||
@@ -215,19 +252,22 @@ static void help_draw_chat(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "  /sdev <type> <id>          : Change active device\n");
 | 
			
		||||
    wprintw(win, "  /mute <type>               : Mute active device if in call\n");
 | 
			
		||||
    wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n");
 | 
			
		||||
    wprintw(win, "  /bitrate <n>               : Set the audio encoding bitrate\n");
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    wattron(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "\n Video:\n");
 | 
			
		||||
    wattroff(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "  /video                     : Toggle video call\n");
 | 
			
		||||
    wprintw(win, "  /res <width> <height>      : Set video resolution\n");
 | 
			
		||||
    wprintw(win, "  /vcall                     : Video call\n");
 | 
			
		||||
    wprintw(win, "  /video                     : Toggle video in call\n");
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
    wrefresh(win);
 | 
			
		||||
    wnoutrefresh(win);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void help_draw_keys(ToxWindow *self)
 | 
			
		||||
@@ -244,34 +284,66 @@ static void help_draw_keys(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "  Page Up and Page Down     : Scroll window history one line\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+F and Ctrl+V         : Scroll window history half a page\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+up and Ctrl+down     : Scroll peer list in conference\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+B                    : Toggle the conference peerlist\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+J                    : Insert new line\n");
 | 
			
		||||
    wprintw(win, "  Ctrl+T                    : Toggle paste mode\n\n");
 | 
			
		||||
    wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n");
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
    wrefresh(win);
 | 
			
		||||
    wnoutrefresh(win);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void help_draw_group(ToxWindow *self)
 | 
			
		||||
static void help_draw_conference(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    WINDOW *win = self->help->win;
 | 
			
		||||
 | 
			
		||||
    wmove(win, 1, 1);
 | 
			
		||||
 | 
			
		||||
    wattron(win, A_BOLD | COLOR_PAIR(RED));
 | 
			
		||||
    wprintw(win, "Group commands:\n");
 | 
			
		||||
    wprintw(win, "Conference 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, "  /title <msg>               : Show/set conference title\n");
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    wattron(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "\n Audio:\n");
 | 
			
		||||
    wattroff(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "  /audio <on> or <off>       : Enable/disable audio in an audio conference\n");
 | 
			
		||||
    wprintw(win, "  /mute                      : Toggle self audio mute status\n");
 | 
			
		||||
    wprintw(win, "  /mute <nick> or <pubkey>   : Toggle peer audio mute status\n");
 | 
			
		||||
    wprintw(win, "  /ptt <on> or <off>         : Toggle audio input Push-To-Talk (F2 to activate)\n");
 | 
			
		||||
    wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n\n");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
    wrefresh(win);
 | 
			
		||||
    wnoutrefresh(win);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
static void help_draw_plugin(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    WINDOW *win = self->help->win;
 | 
			
		||||
 | 
			
		||||
    wmove(win, 1, 1);
 | 
			
		||||
 | 
			
		||||
    wattron(win, A_BOLD | COLOR_PAIR(RED));
 | 
			
		||||
    wprintw(win, "Plugin commands:\n");
 | 
			
		||||
    wattroff(win, A_BOLD | COLOR_PAIR(RED));
 | 
			
		||||
 | 
			
		||||
    draw_handler_help(win);
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
    wnoutrefresh(win);
 | 
			
		||||
}
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
static void help_draw_contacts(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    WINDOW *win = self->help->win;
 | 
			
		||||
@@ -291,55 +363,72 @@ static void help_draw_contacts(ToxWindow *self)
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
    wrefresh(win);
 | 
			
		||||
    wnoutrefresh(win);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void help_onKey(ToxWindow *self, wint_t key)
 | 
			
		||||
{
 | 
			
		||||
    switch(key) {
 | 
			
		||||
        case 'x':
 | 
			
		||||
    int height;
 | 
			
		||||
 | 
			
		||||
    switch (key) {
 | 
			
		||||
        case L'x':
 | 
			
		||||
        case T_KEY_ESC:
 | 
			
		||||
            help_exit(self);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'c':
 | 
			
		||||
        case L'c':
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            help_init_window(self, 22, 80);
 | 
			
		||||
            help_init_window(self, 25, 80);
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
            help_init_window(self, 19, 80);
 | 
			
		||||
            help_init_window(self, 20, 80);
 | 
			
		||||
#else
 | 
			
		||||
            help_init_window(self, 10, 80);
 | 
			
		||||
#endif
 | 
			
		||||
            self->help->type = HELP_CHAT;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'g':
 | 
			
		||||
        case L'g':
 | 
			
		||||
            height = 22;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            help_init_window(self, 30, 80);
 | 
			
		||||
            height += 8;
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
            help_init_window(self, 26, 80);
 | 
			
		||||
#else
 | 
			
		||||
            help_init_window(self, 22, 80);
 | 
			
		||||
            height += 4;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
            height += 2;
 | 
			
		||||
#endif
 | 
			
		||||
            help_init_window(self, height, 80);
 | 
			
		||||
            self->help->type = HELP_GLOBAL;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'r':
 | 
			
		||||
            help_init_window(self, 6, 80);
 | 
			
		||||
            self->help->type = HELP_GROUP;
 | 
			
		||||
        case L'o':
 | 
			
		||||
            height = 6;
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
            height += 7;
 | 
			
		||||
#endif
 | 
			
		||||
            help_init_window(self, height, 80);
 | 
			
		||||
            self->help->type = HELP_CONFERENCE;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'f':
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
 | 
			
		||||
        case L'p':
 | 
			
		||||
            help_init_window(self, 4 + num_registered_handlers(), help_max_width());
 | 
			
		||||
            self->help->type = HELP_PLUGIN;
 | 
			
		||||
            break;
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
        case L'f':
 | 
			
		||||
            help_init_window(self, 10, 80);
 | 
			
		||||
            self->help->type = HELP_CONTACTS;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'k':
 | 
			
		||||
            help_init_window(self, 13, 80);
 | 
			
		||||
        case L'k':
 | 
			
		||||
            help_init_window(self, 15, 80);
 | 
			
		||||
            self->help->type = HELP_KEYS;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'm':
 | 
			
		||||
        case L'm':
 | 
			
		||||
            help_init_menu(self);
 | 
			
		||||
            self->help->type = HELP_MENU;
 | 
			
		||||
            break;
 | 
			
		||||
@@ -348,9 +437,7 @@ void help_onKey(ToxWindow *self, wint_t key)
 | 
			
		||||
 | 
			
		||||
void help_onDraw(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    curs_set(0);
 | 
			
		||||
 | 
			
		||||
    switch(self->help->type) {
 | 
			
		||||
    switch (self->help->type) {
 | 
			
		||||
        case HELP_MENU:
 | 
			
		||||
            help_draw_menu(self);
 | 
			
		||||
            return;
 | 
			
		||||
@@ -371,8 +458,15 @@ void help_onDraw(ToxWindow *self)
 | 
			
		||||
            help_draw_contacts(self);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case HELP_GROUP:
 | 
			
		||||
            help_draw_group(self);
 | 
			
		||||
        case HELP_CONFERENCE:
 | 
			
		||||
            help_draw_conference(self);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
 | 
			
		||||
        case HELP_PLUGIN:
 | 
			
		||||
            help_draw_plugin(self);
 | 
			
		||||
            break;
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,13 +30,16 @@ typedef enum {
 | 
			
		||||
    HELP_MENU,
 | 
			
		||||
    HELP_GLOBAL,
 | 
			
		||||
    HELP_CHAT,
 | 
			
		||||
    HELP_GROUP,
 | 
			
		||||
    HELP_CONFERENCE,
 | 
			
		||||
    HELP_KEYS,
 | 
			
		||||
    HELP_CONTACTS,
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
    HELP_PLUGIN,
 | 
			
		||||
#endif
 | 
			
		||||
} HELP_TYPES;
 | 
			
		||||
 | 
			
		||||
void help_onDraw(ToxWindow *self);
 | 
			
		||||
void help_init_menu(ToxWindow *self);
 | 
			
		||||
void help_onKey(ToxWindow *self, wint_t key);
 | 
			
		||||
 | 
			
		||||
#endif /* #define HELP_H */
 | 
			
		||||
#endif /* HELP_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										126
									
								
								src/input.c
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								src/input.c
									
									
									
									
									
								
							@@ -26,25 +26,29 @@
 | 
			
		||||
 | 
			
		||||
#include <wchar.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic_strings.h"
 | 
			
		||||
#include "conference.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "groupchat.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "toxic_strings.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
 | 
			
		||||
/* add a char to input field and buffer */
 | 
			
		||||
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
 | 
			
		||||
void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    /* 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;
 | 
			
		||||
@@ -74,42 +78,45 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
    int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
 | 
			
		||||
    int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
 | 
			
		||||
 | 
			
		||||
    if (ctx->start && (x >= mx_x - cur_len))
 | 
			
		||||
    if (ctx->start && (x >= mx_x - cur_len)) {
 | 
			
		||||
        ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
 | 
			
		||||
    else if (ctx->start)
 | 
			
		||||
    } else if (ctx->start) {
 | 
			
		||||
        ctx->start = MAX(0, ctx->start - cur_len);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* delete a char via delete key from input field and buffer */
 | 
			
		||||
static void input_delete(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    if (del_char_buf_frnt(self->chatwin) == -1)
 | 
			
		||||
    if (del_char_buf_frnt(self->chatwin) == -1) {
 | 
			
		||||
        sound_notify(self, notif_error, 0, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* delete last typed word */
 | 
			
		||||
static void input_del_word(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
static void input_del_word(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (del_word_buf(ctx) == -1) {
 | 
			
		||||
        sound_notify(self, notif_error, 0, NULL);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* deletes entire line before cursor from input field and buffer */
 | 
			
		||||
static void input_discard(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    if (discard_buf(self->chatwin) == -1)
 | 
			
		||||
    if (discard_buf(self->chatwin) == -1) {
 | 
			
		||||
        sound_notify(self, notif_error, 0, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* deletes entire line after cursor from input field and buffer */
 | 
			
		||||
static void input_kill(ChatContext *ctx)
 | 
			
		||||
{
 | 
			
		||||
    if (kill_buf(ctx) == -1)
 | 
			
		||||
    if (kill_buf(ctx) == -1) {
 | 
			
		||||
        sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void input_yank(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
@@ -131,7 +138,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* moves cursor/line position to end of line in input field and buffer */
 | 
			
		||||
static void input_mv_end(ToxWindow *self, int y, int mx_x)
 | 
			
		||||
static void input_mv_end(ToxWindow *self, int mx_x)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
@@ -146,8 +153,9 @@ static void input_mv_home(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (ctx->pos <= 0)
 | 
			
		||||
    if (ctx->pos <= 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ctx->pos = 0;
 | 
			
		||||
    ctx->start = 0;
 | 
			
		||||
@@ -158,18 +166,44 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (ctx->pos <= 0)
 | 
			
		||||
    if (ctx->pos <= 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
 | 
			
		||||
    int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
 | 
			
		||||
 | 
			
		||||
    --ctx->pos;
 | 
			
		||||
 | 
			
		||||
    if (ctx->start && (x >= mx_x - cur_len))
 | 
			
		||||
    if (ctx->start > 0 && (x >= mx_x - cur_len)) {
 | 
			
		||||
        int s_len = wcwidth(ctx->line[ctx->start - 1]);
 | 
			
		||||
        ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
 | 
			
		||||
    else if (ctx->start)
 | 
			
		||||
    } else if (ctx->start > 0) {
 | 
			
		||||
        ctx->start = MAX(0, ctx->start - cur_len);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* moves the cursor to the beginning of the previous word in input field and buffer */
 | 
			
		||||
static void input_skip_left(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (ctx->pos <= 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int count = 0;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        --ctx->pos;
 | 
			
		||||
        count += wcwidth(ctx->line[ctx->pos]);
 | 
			
		||||
    } while (ctx->pos > 0 && (ctx->line[ctx->pos - 1] != L' ' || ctx->line[ctx->pos] == L' '));
 | 
			
		||||
 | 
			
		||||
    if (ctx->start > 0 && (x >= mx_x - count)) {
 | 
			
		||||
        int s_len = wcwidth(ctx->line[ctx->start - 1]);
 | 
			
		||||
        ctx->start = MAX(0, ctx->start - 1 + (s_len - count));
 | 
			
		||||
    } else if (ctx->start > 0) {
 | 
			
		||||
        ctx->start = MAX(0, ctx->start - count);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* moves cursor/line position right in input field and buffer */
 | 
			
		||||
@@ -177,8 +211,9 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (ctx->pos >= ctx->len)
 | 
			
		||||
    if (ctx->pos >= ctx->len) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ++ctx->pos;
 | 
			
		||||
 | 
			
		||||
@@ -190,6 +225,29 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* moves the cursor to the end of the next word in input field and buffer */
 | 
			
		||||
static void input_skip_right(ToxWindow *self, int x, int mx_x)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (ctx->pos >= ctx->len) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int count = 0;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        count += wcwidth(ctx->line[ctx->pos]);
 | 
			
		||||
        ++ctx->pos;
 | 
			
		||||
    } while (ctx->pos < ctx->len && !(ctx->line[ctx->pos] == L' ' && ctx->line[ctx->pos - 1] != L' '));
 | 
			
		||||
 | 
			
		||||
    int newpos = x + count;
 | 
			
		||||
 | 
			
		||||
    if (newpos >= mx_x) {
 | 
			
		||||
        ctx->start += (1 + (newpos - mx_x));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* puts a line history item in input field and buffer */
 | 
			
		||||
static void input_history(ToxWindow *self, wint_t key, int mx_x)
 | 
			
		||||
{
 | 
			
		||||
@@ -202,7 +260,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
 | 
			
		||||
 | 
			
		||||
/* Handles non-printable input keys that behave the same for all types of chat windows.
 | 
			
		||||
   return true if key matches a function, false otherwise */
 | 
			
		||||
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
 | 
			
		||||
bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x)
 | 
			
		||||
{
 | 
			
		||||
    bool match = true;
 | 
			
		||||
 | 
			
		||||
@@ -229,7 +287,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case T_KEY_C_W:
 | 
			
		||||
            input_del_word(self, x, mx_x);
 | 
			
		||||
            input_del_word(self);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case KEY_HOME:
 | 
			
		||||
@@ -239,7 +297,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
 | 
			
		||||
 | 
			
		||||
        case KEY_END:
 | 
			
		||||
        case T_KEY_C_E:
 | 
			
		||||
            input_mv_end(self, y, mx_x);
 | 
			
		||||
            input_mv_end(self, mx_x);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case KEY_LEFT:
 | 
			
		||||
@@ -259,6 +317,14 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
 | 
			
		||||
            force_refresh(self->chatwin->history);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case T_KEY_C_LEFT:
 | 
			
		||||
            input_skip_left(self, x, mx_x);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case T_KEY_C_RIGHT:
 | 
			
		||||
            input_skip_right(self, x, mx_x);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            match = false;
 | 
			
		||||
            break;
 | 
			
		||||
@@ -266,15 +332,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 (self->is_groupchat) {
 | 
			
		||||
    if (!match) {
 | 
			
		||||
        if (key == user_settings->key_toggle_peerlist) {
 | 
			
		||||
            if (self->type == WINDOW_TYPE_CONFERENCE) {
 | 
			
		||||
                self->show_peerlist ^= 1;
 | 
			
		||||
            redraw_groupchat_win(self);
 | 
			
		||||
                redraw_conference_win(self);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            match = true;
 | 
			
		||||
        } else if (key == user_settings->key_toggle_pastemode) {
 | 
			
		||||
            self->chatwin->pastemode ^= 1;
 | 
			
		||||
            match = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return match;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,10 +24,10 @@
 | 
			
		||||
#define INPUT_H
 | 
			
		||||
 | 
			
		||||
/* add a char to input field and buffer for given chatcontext */
 | 
			
		||||
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
 | 
			
		||||
void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x);
 | 
			
		||||
 | 
			
		||||
/* Handles non-printable input keys that behave the same for all types of chat windows.
 | 
			
		||||
   return true if key matches a function, false otherwise */
 | 
			
		||||
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
 | 
			
		||||
bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x);
 | 
			
		||||
 | 
			
		||||
#endif /* #define INPUT_H */
 | 
			
		||||
#endif /* INPUT_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										610
									
								
								src/line_info.c
									
									
									
									
									
								
							
							
						
						
									
										610
									
								
								src/line_info.c
									
									
									
									
									
								
							@@ -20,19 +20,19 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "conference.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "groupchat.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "message_queue.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
 | 
			
		||||
@@ -40,12 +40,13 @@ void line_info_init(struct history *hst)
 | 
			
		||||
{
 | 
			
		||||
    hst->line_root = calloc(1, sizeof(struct line_info));
 | 
			
		||||
 | 
			
		||||
    if (hst->line_root == NULL)
 | 
			
		||||
    if (hst->line_root == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hst->line_start = hst->line_root;
 | 
			
		||||
    hst->line_end = hst->line_start;
 | 
			
		||||
    hst->queue_sz = 0;
 | 
			
		||||
    hst->queue_size = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* resets line_start (moves to end of chat history) */
 | 
			
		||||
@@ -53,26 +54,28 @@ void line_info_reset_start(ToxWindow *self, struct history *hst)
 | 
			
		||||
{
 | 
			
		||||
    struct line_info *line = hst->line_end;
 | 
			
		||||
 | 
			
		||||
    if (line->prev == NULL)
 | 
			
		||||
    if (line == NULL || line->prev == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int y2, x2;
 | 
			
		||||
    int y2;
 | 
			
		||||
    int x2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
    UNUSED_VAR(x2);
 | 
			
		||||
 | 
			
		||||
    int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;
 | 
			
		||||
    int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
 | 
			
		||||
    int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
 | 
			
		||||
    int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
 | 
			
		||||
    int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst;
 | 
			
		||||
 | 
			
		||||
    int curlines = 0;
 | 
			
		||||
    int nxtlines = line->newlines + (line->len / (x2 - side_offst));
 | 
			
		||||
    uint16_t curlines = 0;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        curlines += 1 + nxtlines;
 | 
			
		||||
        curlines += line->format_lines;
 | 
			
		||||
        line = line->prev;
 | 
			
		||||
        nxtlines = line->newlines + (line->len / (x2 - side_offst));
 | 
			
		||||
    } while (line->prev && curlines + nxtlines < max_y);
 | 
			
		||||
    } while (line->prev && curlines + line->format_lines <= max_y);
 | 
			
		||||
 | 
			
		||||
    hst->line_start = line;
 | 
			
		||||
 | 
			
		||||
    self->scroll_pause = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void line_info_cleanup(struct history *hst)
 | 
			
		||||
@@ -85,12 +88,11 @@ void line_info_cleanup(struct history *hst)
 | 
			
		||||
        tmp1 = tmp2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < hst->queue_sz; ++i) {
 | 
			
		||||
        if (hst->queue[i])
 | 
			
		||||
    for (size_t i = 0; i < hst->queue_size; ++i) {
 | 
			
		||||
        if (hst->queue[i]) {
 | 
			
		||||
            free(hst->queue[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(hst);
 | 
			
		||||
}
 | 
			
		||||
@@ -114,39 +116,250 @@ static void line_info_root_fwd(struct history *hst)
 | 
			
		||||
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
 | 
			
		||||
static struct line_info *line_info_ret_queue(struct history *hst)
 | 
			
		||||
{
 | 
			
		||||
    if (hst->queue_sz <= 0)
 | 
			
		||||
    if (hst->queue_size == 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct line_info *line = hst->queue[0];
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < hst->queue_sz; ++i)
 | 
			
		||||
    for (size_t i = 0; i < hst->queue_size; ++i) {
 | 
			
		||||
        hst->queue[i] = hst->queue[i + 1];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    --hst->queue_sz;
 | 
			
		||||
    --hst->queue_size;
 | 
			
		||||
 | 
			
		||||
    return line;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* creates new line_info line and puts it in the queue. */
 | 
			
		||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
 | 
			
		||||
/* Prints a maximum of `n` chars from `s` to `win`.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 1 if the string contains a newline byte.
 | 
			
		||||
 * Return 0 if string does not contain a newline byte.
 | 
			
		||||
 * Return -1 if printing was aborted.
 | 
			
		||||
 */
 | 
			
		||||
static int print_n_chars(WINDOW *win, const char *s, size_t n, int max_y)
 | 
			
		||||
{
 | 
			
		||||
    bool newline = false;
 | 
			
		||||
    char ch;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < n && (ch = s[i]); ++i) {
 | 
			
		||||
        if (ch == '\n') {
 | 
			
		||||
            newline = true;
 | 
			
		||||
 | 
			
		||||
            int x;
 | 
			
		||||
            int y;
 | 
			
		||||
            UNUSED_VAR(x);
 | 
			
		||||
            getyx(win, y, x);
 | 
			
		||||
 | 
			
		||||
            // make sure cursor will wrap correctly after newline to prevent display bugs
 | 
			
		||||
            if (y + 1 >= max_y) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (win) {
 | 
			
		||||
            wprintw(win, "%c", ch);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return newline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the index of the last space character in `s` found before `limit`.
 | 
			
		||||
 * Returns -1 if no space is found.
 | 
			
		||||
 */
 | 
			
		||||
static int rspace_index(const char *s, int limit)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = limit; i >= 0; --i) {
 | 
			
		||||
        if (s[i] == ' ') {
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the first index in `s` containing a newline byte found before `limit`.
 | 
			
		||||
 * Returns -1 if no newline  is found.
 | 
			
		||||
 */
 | 
			
		||||
static int newline_index(const char *s, int limit)
 | 
			
		||||
{
 | 
			
		||||
    char ch;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < limit && (ch = s[i]); ++i) {
 | 
			
		||||
        if (ch == '\n') {
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the number of newline bytes in `s` */
 | 
			
		||||
static unsigned int newline_count(const char *s)
 | 
			
		||||
{
 | 
			
		||||
    char ch;
 | 
			
		||||
    unsigned int count = 0;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; (ch = s[i]); ++i) {
 | 
			
		||||
        if (ch == '\n') {
 | 
			
		||||
            ++count;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Prints `line` message to window, wrapping at the last word that fits on the current line.
 | 
			
		||||
 * This function updates the `format_lines` field of `line` according to current window dimensions.
 | 
			
		||||
 *
 | 
			
		||||
 * If `win` is null nothing will be printed to the window. This is useful to set the
 | 
			
		||||
 * `format_lines` field on initialization.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success.
 | 
			
		||||
 * Return -1 if not all characters in line's message were printed to screen.
 | 
			
		||||
 */
 | 
			
		||||
static int print_wrap(WINDOW *win, struct line_info *line, int max_x, int max_y)
 | 
			
		||||
{
 | 
			
		||||
    int x;
 | 
			
		||||
    int y;
 | 
			
		||||
    UNUSED_VAR(y);
 | 
			
		||||
 | 
			
		||||
    const char *msg = line->msg;
 | 
			
		||||
    uint16_t length = line->msg_len;
 | 
			
		||||
    uint16_t lines = 0;
 | 
			
		||||
    const int x_start = line->len - line->msg_len - 1;  // manually keep track of x position because ncurses sucks
 | 
			
		||||
    int x_limit = max_x - x_start;
 | 
			
		||||
 | 
			
		||||
    if (x_limit <= 1) {
 | 
			
		||||
        fprintf(stderr, "Warning: x_limit <= 0 in print_wrap(): %d\n", x_limit);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (msg) {
 | 
			
		||||
        getyx(win, y, x);
 | 
			
		||||
 | 
			
		||||
        // next line would print past window limit so we abort; we don't want to update format_lines
 | 
			
		||||
        if (x > x_start) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (length < x_limit) {
 | 
			
		||||
            int p_ret = print_n_chars(win, msg, length, max_y);
 | 
			
		||||
 | 
			
		||||
            if (p_ret == 1) {
 | 
			
		||||
                lines += newline_count(msg);
 | 
			
		||||
            } else if (p_ret == -1) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ++lines;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int newline_idx = newline_index(msg, x_limit - 1);
 | 
			
		||||
 | 
			
		||||
        if (newline_idx >= 0) {
 | 
			
		||||
            if (print_n_chars(win, msg, newline_idx + 1, max_y) == -1) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            msg += (newline_idx + 1);
 | 
			
		||||
            length -= (newline_idx + 1);
 | 
			
		||||
            x_limit = max_x; // if we find a newline we stop adding column padding for rest of message
 | 
			
		||||
            ++lines;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int space_idx = rspace_index(msg, x_limit - 1);
 | 
			
		||||
 | 
			
		||||
        if (space_idx >= 1) {
 | 
			
		||||
            if (print_n_chars(win, msg, space_idx, max_y) == -1) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            msg += space_idx + 1;
 | 
			
		||||
            length -= (space_idx + 1);
 | 
			
		||||
 | 
			
		||||
            if (win) {
 | 
			
		||||
                waddch(win, '\n');
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (print_n_chars(win, msg, x_limit, max_y) == -1) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            msg += x_limit;
 | 
			
		||||
            length -= x_limit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add padding to the start of the next line
 | 
			
		||||
        if (win && x_limit < max_x) {
 | 
			
		||||
            for (size_t i = 0; i < x_start; ++i) {
 | 
			
		||||
                waddch(win, ' ');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ++lines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (win && line->noread_flag) {
 | 
			
		||||
        getyx(win, y, x);
 | 
			
		||||
 | 
			
		||||
        if (x >= max_x - 1 || x == x_start) {
 | 
			
		||||
            ++lines;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wattron(win, COLOR_PAIR(RED));
 | 
			
		||||
        wprintw(win, " x");
 | 
			
		||||
        wattroff(win, COLOR_PAIR(RED));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line->format_lines = lines;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void line_info_init_line(ToxWindow *self, struct line_info *line)
 | 
			
		||||
{
 | 
			
		||||
    int y2;
 | 
			
		||||
    int x2;
 | 
			
		||||
    UNUSED_VAR(y2);
 | 
			
		||||
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT;
 | 
			
		||||
    const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2;
 | 
			
		||||
 | 
			
		||||
    print_wrap(NULL, line, max_x, max_y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* creates new line_info line and puts it in the queue.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the id of the new line.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type,
 | 
			
		||||
                  uint8_t bold, uint8_t colour, const char *msg, ...)
 | 
			
		||||
{
 | 
			
		||||
    if (!self)
 | 
			
		||||
        return;
 | 
			
		||||
    if (!self) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct history *hst = self->chatwin->hst;
 | 
			
		||||
 | 
			
		||||
    if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
 | 
			
		||||
        return;
 | 
			
		||||
    if (hst->queue_size >= MAX_LINE_INFO_QUEUE) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct line_info *new_line = calloc(1, sizeof(struct line_info));
 | 
			
		||||
 | 
			
		||||
    if (new_line == NULL)
 | 
			
		||||
    if (new_line == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
 | 
			
		||||
    char frmt_msg[MAX_LINE_INFO_MSG_SIZE];
 | 
			
		||||
    frmt_msg[0] = 0;
 | 
			
		||||
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start(args, msg);
 | 
			
		||||
@@ -158,34 +371,36 @@ 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;
 | 
			
		||||
            len += strlen(user_settings->line_normal) + 2; // two spaces
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case IN_MSG:
 | 
			
		||||
 | 
			
		||||
        /* fallthrough */
 | 
			
		||||
        case OUT_MSG:
 | 
			
		||||
            len += strlen(user_settings->line_normal) + 3;
 | 
			
		||||
            len += strlen(user_settings->line_normal) + 3; // two spaces and a ':' char
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case CONNECTION:
 | 
			
		||||
            len += strlen(user_settings->line_join) + 2;
 | 
			
		||||
            len += strlen(user_settings->line_join) + 2;  // two spaces
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case DISCONNECTION:
 | 
			
		||||
            len += strlen(user_settings->line_quit) + 2;
 | 
			
		||||
            len += strlen(user_settings->line_quit) + 2;  // two spaces
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case SYS_MSG:
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case NAME_CHANGE:
 | 
			
		||||
            len += strlen(user_settings->line_alert) + 1;
 | 
			
		||||
            len += strlen(user_settings->line_alert) + 2;  // two spaces
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case PROMPT:
 | 
			
		||||
            ++len;
 | 
			
		||||
            len += 2;  // '$' char and a space
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
@@ -193,20 +408,16 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint16_t msg_len = 0;
 | 
			
		||||
 | 
			
		||||
    if (frmt_msg[0]) {
 | 
			
		||||
        snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
 | 
			
		||||
        len += strlen(new_line->msg);
 | 
			
		||||
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; frmt_msg[i]; ++i) {
 | 
			
		||||
            if (frmt_msg[i] == '\n')
 | 
			
		||||
                ++new_line->newlines;
 | 
			
		||||
        }
 | 
			
		||||
        msg_len = strlen(new_line->msg);
 | 
			
		||||
        len += msg_len;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (timestr) {
 | 
			
		||||
        snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
 | 
			
		||||
    if (show_timestamp) {
 | 
			
		||||
        get_time_str(new_line->timestr, sizeof(new_line->timestr));
 | 
			
		||||
        len += strlen(new_line->timestr) + 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -220,14 +431,20 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
 | 
			
		||||
        len += strlen(new_line->name2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    new_line->id = (hst->line_end->id + 1 + hst->queue_size) % INT_MAX;
 | 
			
		||||
    new_line->len = len;
 | 
			
		||||
    new_line->msg_len = msg_len;
 | 
			
		||||
    new_line->type = type;
 | 
			
		||||
    new_line->bold = bold;
 | 
			
		||||
    new_line->colour = colour;
 | 
			
		||||
    new_line->noread_flag = false;
 | 
			
		||||
    new_line->timestamp = get_unix_time();
 | 
			
		||||
 | 
			
		||||
    hst->queue[hst->queue_sz++] = new_line;
 | 
			
		||||
    line_info_init_line(self, new_line);
 | 
			
		||||
 | 
			
		||||
    hst->queue[hst->queue_size++] = new_line;
 | 
			
		||||
 | 
			
		||||
    return new_line->id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
 | 
			
		||||
@@ -236,36 +453,21 @@ static void line_info_check_queue(ToxWindow *self)
 | 
			
		||||
    struct history *hst = self->chatwin->hst;
 | 
			
		||||
    struct line_info *line = line_info_ret_queue(hst);
 | 
			
		||||
 | 
			
		||||
    if (line == NULL)
 | 
			
		||||
    if (line == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (hst->start_id > user_settings->history_size)
 | 
			
		||||
    if (hst->start_id > user_settings->history_size) {
 | 
			
		||||
        line_info_root_fwd(hst);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line->id = hst->line_end->id + 1;
 | 
			
		||||
    line->prev = hst->line_end;
 | 
			
		||||
    hst->line_end->next = line;
 | 
			
		||||
    hst->line_end = line;
 | 
			
		||||
    hst->line_end->id = line->id;
 | 
			
		||||
 | 
			
		||||
    int y, y2, x, x2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
    getyx(self->chatwin->history, y, x);
 | 
			
		||||
    (void) x;
 | 
			
		||||
 | 
			
		||||
    if (x2 <= SIDEBAR_WIDTH)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;   /* offset width of groupchat sidebar */
 | 
			
		||||
    int lines = 1 + line->newlines + (line->len / (x2 - offst));
 | 
			
		||||
    int max_y = y2 - CHATBOX_HEIGHT;
 | 
			
		||||
 | 
			
		||||
    /* move line_start forward proportionate to the number of new lines */
 | 
			
		||||
    if (y + lines - 1 >= max_y) {
 | 
			
		||||
        while (lines > 0 && hst->line_start->next) {
 | 
			
		||||
            lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst));
 | 
			
		||||
            hst->line_start = hst->line_start->next;
 | 
			
		||||
            ++hst->start_id;
 | 
			
		||||
        }
 | 
			
		||||
    if (!self->scroll_pause) {
 | 
			
		||||
        line_info_reset_start(self, hst);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -275,8 +477,9 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    if (ctx == NULL)
 | 
			
		||||
    if (ctx == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct history *hst = ctx->hst;
 | 
			
		||||
 | 
			
		||||
@@ -284,28 +487,55 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
    line_info_check_queue(self);
 | 
			
		||||
 | 
			
		||||
    WINDOW *win = ctx->history;
 | 
			
		||||
 | 
			
		||||
    wclear(win);
 | 
			
		||||
    int y2, x2;
 | 
			
		||||
 | 
			
		||||
    int y2;
 | 
			
		||||
 | 
			
		||||
    int x2;
 | 
			
		||||
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (x2 <= SIDEBAR_WIDTH)
 | 
			
		||||
    if (x2 - 1 <= SIDEBAR_WIDTH) {  // leave room on x axis for sidebar padding
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (self->is_groupchat)
 | 
			
		||||
    if (self->type == WINDOW_TYPE_CONFERENCE) {
 | 
			
		||||
        wmove(win, 0, 0);
 | 
			
		||||
    else
 | 
			
		||||
        wmove(win, 2, 0);
 | 
			
		||||
    } else {
 | 
			
		||||
        wmove(win, TOP_BAR_HEIGHT, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct line_info *line = hst->line_start->next;
 | 
			
		||||
    int numlines = 0;
 | 
			
		||||
 | 
			
		||||
    while (line && numlines++ <= y2) {
 | 
			
		||||
    if (!line) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT;
 | 
			
		||||
    const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2;
 | 
			
		||||
    uint16_t numlines = line->format_lines;
 | 
			
		||||
    int print_ret = 0;
 | 
			
		||||
 | 
			
		||||
    while (line && numlines++ <= max_y && print_ret == 0) {
 | 
			
		||||
        int y;
 | 
			
		||||
        int x;
 | 
			
		||||
        UNUSED_VAR(y);
 | 
			
		||||
 | 
			
		||||
        getyx(win, y, x);
 | 
			
		||||
 | 
			
		||||
        if (x > 0) { // Prevents us from printing off the screen
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint8_t type = line->type;
 | 
			
		||||
 | 
			
		||||
        switch (type) {
 | 
			
		||||
            case OUT_MSG:
 | 
			
		||||
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case OUT_MSG_READ:
 | 
			
		||||
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case IN_MSG:
 | 
			
		||||
                wattron(win, COLOR_PAIR(BLUE));
 | 
			
		||||
@@ -314,44 +544,49 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
 | 
			
		||||
                int nameclr = GREEN;
 | 
			
		||||
 | 
			
		||||
                if (line->colour)
 | 
			
		||||
                if (line->colour) {
 | 
			
		||||
                    nameclr = line->colour;
 | 
			
		||||
                else if (type == IN_MSG)
 | 
			
		||||
                } else if (type == IN_MSG) {
 | 
			
		||||
                    nameclr = CYAN;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                wattron(win, COLOR_PAIR(nameclr));
 | 
			
		||||
                wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
 | 
			
		||||
                wattroff(win, COLOR_PAIR(nameclr));
 | 
			
		||||
 | 
			
		||||
                if (line->msg[0] == '>')
 | 
			
		||||
                if (line->msg[0] == 0) {
 | 
			
		||||
                    waddch(win, '\n');
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (line->msg[0] == '>') {
 | 
			
		||||
                    wattron(win, COLOR_PAIR(GREEN));
 | 
			
		||||
                else if (line->msg[0] == '<')
 | 
			
		||||
                } else if (line->msg[0] == '<') {
 | 
			
		||||
                    wattron(win, COLOR_PAIR(RED));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "%s", line->msg);
 | 
			
		||||
                print_ret = print_wrap(win, line, max_x, max_y);
 | 
			
		||||
 | 
			
		||||
                if (line->msg[0] == '>')
 | 
			
		||||
                if (line->msg[0] == '>') {
 | 
			
		||||
                    wattroff(win, COLOR_PAIR(GREEN));
 | 
			
		||||
                else if (line->msg[0] == '<')
 | 
			
		||||
                } else if (line->msg[0] == '<') {
 | 
			
		||||
                    wattroff(win, COLOR_PAIR(RED));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
 | 
			
		||||
                    wattron(win, COLOR_PAIR(RED));
 | 
			
		||||
                    wprintw(win, " x", line->msg);
 | 
			
		||||
                    wattroff(win, COLOR_PAIR(RED));
 | 
			
		||||
 | 
			
		||||
                    if (line->noread_flag == false) {
 | 
			
		||||
                if (type == OUT_MSG && !line->read_flag) {
 | 
			
		||||
                    if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
 | 
			
		||||
                        line->noread_flag = true;
 | 
			
		||||
                        line->len += 2;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "\n", line->msg);
 | 
			
		||||
                waddch(win, '\n');
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case OUT_ACTION_READ:
 | 
			
		||||
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case OUT_ACTION:
 | 
			
		||||
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case IN_ACTION:
 | 
			
		||||
                wattron(win, COLOR_PAIR(BLUE));
 | 
			
		||||
@@ -359,21 +594,17 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
                wattroff(win, COLOR_PAIR(BLUE));
 | 
			
		||||
 | 
			
		||||
                wattron(win, COLOR_PAIR(YELLOW));
 | 
			
		||||
                wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
 | 
			
		||||
                wprintw(win, "%s %s ", user_settings->line_normal, line->name1);
 | 
			
		||||
                print_ret = print_wrap(win, line, max_x, max_y);
 | 
			
		||||
                wattroff(win, COLOR_PAIR(YELLOW));
 | 
			
		||||
 | 
			
		||||
                if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
 | 
			
		||||
                    wattron(win, COLOR_PAIR(RED));
 | 
			
		||||
                    wprintw(win, " x", line->msg);
 | 
			
		||||
                    wattroff(win, COLOR_PAIR(RED));
 | 
			
		||||
 | 
			
		||||
                    if (line->noread_flag == false) {
 | 
			
		||||
                if (type == OUT_ACTION && !line->read_flag) {
 | 
			
		||||
                    if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
 | 
			
		||||
                        line->noread_flag = true;
 | 
			
		||||
                        line->len += 2;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "\n", line->msg);
 | 
			
		||||
                waddch(win, '\n');
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case SYS_MSG:
 | 
			
		||||
@@ -383,19 +614,24 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
                    wattroff(win, COLOR_PAIR(BLUE));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (line->bold)
 | 
			
		||||
                if (line->bold) {
 | 
			
		||||
                    wattron(win, A_BOLD);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (line->colour)
 | 
			
		||||
                if (line->colour) {
 | 
			
		||||
                    wattron(win, COLOR_PAIR(line->colour));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "%s\n", line->msg);
 | 
			
		||||
                print_ret = print_wrap(win, line, max_x, max_y);
 | 
			
		||||
                waddch(win, '\n');
 | 
			
		||||
 | 
			
		||||
                if (line->bold)
 | 
			
		||||
                if (line->bold) {
 | 
			
		||||
                    wattroff(win, A_BOLD);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (line->colour)
 | 
			
		||||
                if (line->colour) {
 | 
			
		||||
                    wattroff(win, COLOR_PAIR(line->colour));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
@@ -404,10 +640,11 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
                wprintw(win, "$ ");
 | 
			
		||||
                wattroff(win, COLOR_PAIR(GREEN));
 | 
			
		||||
 | 
			
		||||
                if (line->msg[0])
 | 
			
		||||
                    wprintw(win, "%s", line->msg);
 | 
			
		||||
                if (line->msg[0]) {
 | 
			
		||||
                    print_ret = print_wrap(win, line, max_x, max_y);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "\n");
 | 
			
		||||
                waddch(win, '\n');
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case CONNECTION:
 | 
			
		||||
@@ -422,7 +659,9 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
                wprintw(win, "%s ", line->name1);
 | 
			
		||||
                wattroff(win, A_BOLD);
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "%s\n", line->msg);
 | 
			
		||||
                print_ret = print_wrap(win, line, max_x, max_y);
 | 
			
		||||
                waddch(win, '\n');
 | 
			
		||||
 | 
			
		||||
                wattroff(win, COLOR_PAIR(line->colour));
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
@@ -439,7 +678,9 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
                wprintw(win, "%s ", line->name1);
 | 
			
		||||
                wattroff(win, A_BOLD);
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "%s\n", line->msg);
 | 
			
		||||
                print_ret = print_wrap(win, line, max_x, max_y);
 | 
			
		||||
                waddch(win, '\n');
 | 
			
		||||
 | 
			
		||||
                wattroff(win, COLOR_PAIR(line->colour));
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
@@ -455,7 +696,7 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
                wprintw(win, "%s", line->name1);
 | 
			
		||||
                wattroff(win, A_BOLD);
 | 
			
		||||
 | 
			
		||||
                wprintw(win, "%s", line->msg);
 | 
			
		||||
                print_ret = print_wrap(win, line, max_x, max_y);
 | 
			
		||||
 | 
			
		||||
                wattron(win, A_BOLD);
 | 
			
		||||
                wprintw(win, "%s\n", line->name2);
 | 
			
		||||
@@ -469,8 +710,41 @@ void line_info_print(ToxWindow *self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* keep calling until queue is empty */
 | 
			
		||||
    if (hst->queue_sz > 0)
 | 
			
		||||
    if (hst->queue_size > 0) {
 | 
			
		||||
        line_info_print(self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return true if all lines starting from `line` can fit on the screen.
 | 
			
		||||
 */
 | 
			
		||||
static bool line_info_screen_fit(ToxWindow *self, struct line_info *line)
 | 
			
		||||
{
 | 
			
		||||
    if (!line) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int x2;
 | 
			
		||||
    int y2;
 | 
			
		||||
    getmaxyx(self->chatwin->history, y2, x2);
 | 
			
		||||
 | 
			
		||||
    UNUSED_VAR(x2);
 | 
			
		||||
 | 
			
		||||
    const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
 | 
			
		||||
    const int max_y = y2 - top_offset;
 | 
			
		||||
 | 
			
		||||
    uint16_t lines = line->format_lines;
 | 
			
		||||
 | 
			
		||||
    while (line) {
 | 
			
		||||
        if (lines > max_y) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lines += line->format_lines;
 | 
			
		||||
        line = line->next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* puts msg in specified line_info msg buffer */
 | 
			
		||||
@@ -480,6 +754,9 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
 | 
			
		||||
 | 
			
		||||
    while (line) {
 | 
			
		||||
        if (line->id == id) {
 | 
			
		||||
            size_t new_len = strlen(msg);
 | 
			
		||||
            line->len = line->len - line->msg_len + new_len;
 | 
			
		||||
            line->msg_len = new_len;
 | 
			
		||||
            snprintf(line->msg, sizeof(line->msg), "%s", msg);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -488,47 +765,75 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* static void line_info_goto_root(struct history *hst)
 | 
			
		||||
static void line_info_scroll_up(ToxWindow *self, struct history *hst)
 | 
			
		||||
{
 | 
			
		||||
    hst->line_start = hst->line_root;
 | 
			
		||||
} */
 | 
			
		||||
 | 
			
		||||
static void line_info_scroll_up(struct history *hst)
 | 
			
		||||
{
 | 
			
		||||
    if (hst->line_start->prev)
 | 
			
		||||
    if (hst->line_start->prev) {
 | 
			
		||||
        hst->line_start = hst->line_start->prev;
 | 
			
		||||
    else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
 | 
			
		||||
        self->scroll_pause = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void line_info_scroll_down(struct history *hst)
 | 
			
		||||
static void line_info_scroll_down(ToxWindow *self, struct history *hst)
 | 
			
		||||
{
 | 
			
		||||
    if (hst->line_start->next)
 | 
			
		||||
        hst->line_start = hst->line_start->next;
 | 
			
		||||
    else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
 | 
			
		||||
    struct line_info *next = hst->line_start->next;
 | 
			
		||||
 | 
			
		||||
    if (next && self->scroll_pause) {
 | 
			
		||||
        if (line_info_screen_fit(self, next->next)) {
 | 
			
		||||
            line_info_reset_start(self, hst);
 | 
			
		||||
        } else {
 | 
			
		||||
            hst->line_start = next;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        line_info_reset_start(self, hst);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void line_info_page_up(ToxWindow *self, struct history *hst)
 | 
			
		||||
{
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    int x2;
 | 
			
		||||
    int y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
    (void) x2;
 | 
			
		||||
    int jump_dist = y2 / 2;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < jump_dist && hst->line_start->prev; ++i)
 | 
			
		||||
    UNUSED_VAR(x2);
 | 
			
		||||
 | 
			
		||||
    const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
 | 
			
		||||
    const int max_y = y2 - top_offset;
 | 
			
		||||
    size_t jump_dist = max_y / 2;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < jump_dist && hst->line_start->prev; ++i) {
 | 
			
		||||
        hst->line_start = hst->line_start->prev;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self->scroll_pause = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void line_info_page_down(ToxWindow *self, struct history *hst)
 | 
			
		||||
{
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
    (void) x2;
 | 
			
		||||
    int jump_dist = y2 / 2;
 | 
			
		||||
    int i;
 | 
			
		||||
    if (!self->scroll_pause) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < jump_dist && hst->line_start->next; ++i)
 | 
			
		||||
        hst->line_start = hst->line_start->next;
 | 
			
		||||
    int x2;
 | 
			
		||||
    int y2;
 | 
			
		||||
    getmaxyx(self->chatwin->history, y2, x2);
 | 
			
		||||
 | 
			
		||||
    UNUSED_VAR(x2);
 | 
			
		||||
 | 
			
		||||
    const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
 | 
			
		||||
    const int max_y = y2 - top_offset;
 | 
			
		||||
    size_t jump_dist = max_y / 2;
 | 
			
		||||
 | 
			
		||||
    struct line_info *next = hst->line_start->next;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < jump_dist && next; ++i) {
 | 
			
		||||
        if (line_info_screen_fit(self, next->next)) {
 | 
			
		||||
            line_info_reset_start(self, hst);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hst->line_start = next;
 | 
			
		||||
        next = hst->line_start->next;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool line_info_onKey(ToxWindow *self, wint_t key)
 | 
			
		||||
@@ -538,20 +843,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) {
 | 
			
		||||
        line_info_scroll_up(hst);
 | 
			
		||||
    }
 | 
			
		||||
    else if (key == user_settings->key_scroll_line_down) {
 | 
			
		||||
        line_info_scroll_down(hst);
 | 
			
		||||
    }
 | 
			
		||||
    else if (key == user_settings->key_page_bottom) {
 | 
			
		||||
    } else if (key == user_settings->key_scroll_line_up) {
 | 
			
		||||
        line_info_scroll_up(self, hst);
 | 
			
		||||
    } else if (key == user_settings->key_scroll_line_down) {
 | 
			
		||||
        line_info_scroll_down(self, hst);
 | 
			
		||||
    } else if (key == user_settings->key_page_bottom) {
 | 
			
		||||
        line_info_reset_start(self, hst);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
    } else {
 | 
			
		||||
        match = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,15 +23,15 @@
 | 
			
		||||
#ifndef LINE_INFO_H
 | 
			
		||||
#define LINE_INFO_H
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_HISTORY 100000
 | 
			
		||||
#define MIN_HISTORY 40
 | 
			
		||||
#define MAX_LINE_INFO_QUEUE 1024
 | 
			
		||||
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32    /* needs extra room for log loading */
 | 
			
		||||
#define MAX_LINE_INFO_MSG_SIZE (MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32) /* needs extra room for log loading */
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
typedef enum LINE_TYPE {
 | 
			
		||||
    SYS_MSG,
 | 
			
		||||
    IN_MSG,
 | 
			
		||||
    OUT_MSG,
 | 
			
		||||
@@ -50,14 +50,16 @@ 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;
 | 
			
		||||
    uint8_t noread_flag;   /* true if a line should be flagged as unread */
 | 
			
		||||
    bool    noread_flag;   /* true if a line should be flagged as unread */
 | 
			
		||||
    bool    read_flag;     /* true if a message has been flagged as read */
 | 
			
		||||
    uint32_t id;
 | 
			
		||||
    uint16_t len;   /* combined len of entire line */
 | 
			
		||||
    uint8_t newlines;
 | 
			
		||||
    uint16_t len;        /* combined length of entire line */
 | 
			
		||||
    uint16_t msg_len;    /* length of the message */
 | 
			
		||||
    uint16_t format_lines;  /* number of lines the combined string takes up (dynamically set) */
 | 
			
		||||
 | 
			
		||||
    struct line_info *prev;
 | 
			
		||||
    struct line_info *next;
 | 
			
		||||
@@ -71,11 +73,15 @@ struct history {
 | 
			
		||||
    uint32_t start_id;    /* keeps track of where line_start should be when at bottom of history */
 | 
			
		||||
 | 
			
		||||
    struct line_info *queue[MAX_LINE_INFO_QUEUE];
 | 
			
		||||
    int queue_sz;
 | 
			
		||||
    size_t queue_size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* creates new line_info line and puts it in the queue. */
 | 
			
		||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
 | 
			
		||||
/* creates new line_info line and puts it in the queue.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the id of the new line.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type,
 | 
			
		||||
                  uint8_t bold, uint8_t colour, const char *msg, ...);
 | 
			
		||||
 | 
			
		||||
/* Prints a section of history starting at line_start */
 | 
			
		||||
@@ -96,4 +102,4 @@ void line_info_reset_start(ToxWindow *self, struct history *hst);
 | 
			
		||||
void line_info_init(struct history *hst);
 | 
			
		||||
bool line_info_onKey(ToxWindow *self, wint_t key);    /* returns true if key is a match */
 | 
			
		||||
 | 
			
		||||
#endif /* #define LINE_INFO_H */
 | 
			
		||||
#endif /* LINE_INFO_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										305
									
								
								src/log.c
									
									
									
									
									
								
							
							
						
						
									
										305
									
								
								src/log.c
									
									
									
									
									
								
							@@ -22,41 +22,46 @@
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#include "configdir.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
 | 
			
		||||
/* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h)
 | 
			
		||||
   A prompt log is in the format: LOGDIR/selfkey-home.log
 | 
			
		||||
   A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log
 | 
			
		||||
   A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log
 | 
			
		||||
 | 
			
		||||
   Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used.
 | 
			
		||||
 | 
			
		||||
   Returns 0 on success, -1 if the path is too long */
 | 
			
		||||
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
 | 
			
		||||
/* Creates a log path and puts it in `dest.
 | 
			
		||||
 *
 | 
			
		||||
 * There are two types of logs: chat logs and prompt logs (see LOG_TYPE in log.h)
 | 
			
		||||
 * A prompt log is in the format: LOGDIR/selfkey-home.log
 | 
			
		||||
 * A chat log is in the format: LOGDIR/selfkey-name-otherkey.log
 | 
			
		||||
 *
 | 
			
		||||
 * For friend chats `otherkey` is the first 6 bytes of the friend's Tox ID.
 | 
			
		||||
 * For Conferences/groups `otherkey` is the first 6 bytes of the group's unique ID.
 | 
			
		||||
 *
 | 
			
		||||
 * Return path length on success.
 | 
			
		||||
 * Return -1 if the path is too long.
 | 
			
		||||
 */
 | 
			
		||||
static int get_log_path(char *dest, int destsize, const char *name, const char *selfkey, const char *otherkey)
 | 
			
		||||
{
 | 
			
		||||
    if (!valid_nick(name))
 | 
			
		||||
    if (!valid_nick(name)) {
 | 
			
		||||
        name = UNKNOWN_NAME;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *namedash = logtype == LOG_PROMPT ? "" : "-";
 | 
			
		||||
    const char *namedash = otherkey ? "-" : "";
 | 
			
		||||
    const char *set_path = user_settings->chatlogs_path;
 | 
			
		||||
 | 
			
		||||
    char *user_config_dir = get_user_config_dir();
 | 
			
		||||
    int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
 | 
			
		||||
    path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
 | 
			
		||||
 | 
			
		||||
    /* first 6 digits of selfkey */
 | 
			
		||||
    char self_id[32];
 | 
			
		||||
    /* first 6 bytes of selfkey */
 | 
			
		||||
    char self_id[32] = {0};
 | 
			
		||||
    path_len += KEY_IDENT_DIGITS * 2;
 | 
			
		||||
    sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
 | 
			
		||||
    sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
 | 
			
		||||
@@ -65,19 +70,13 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
 | 
			
		||||
 | 
			
		||||
    char other_id[32] = {0};
 | 
			
		||||
 | 
			
		||||
    switch (logtype) {
 | 
			
		||||
        case LOG_CHAT:
 | 
			
		||||
    if (otherkey) {
 | 
			
		||||
        /* first 6 bytes of otherkey */
 | 
			
		||||
        path_len += KEY_IDENT_DIGITS * 2;
 | 
			
		||||
        sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
 | 
			
		||||
        sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
 | 
			
		||||
        sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
 | 
			
		||||
        other_id[KEY_IDENT_DIGITS * 2] = '\0';
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case LOG_GROUP:
 | 
			
		||||
            strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time());
 | 
			
		||||
            path_len += strlen(other_id);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (path_len >= destsize) {
 | 
			
		||||
@@ -85,32 +84,43 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!string_is_empty(set_path))
 | 
			
		||||
    if (!string_is_empty(set_path)) {
 | 
			
		||||
        snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id);
 | 
			
		||||
    else
 | 
			
		||||
    } else {
 | 
			
		||||
        snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(user_config_dir);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
    return path_len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Opens log file or creates a new one */
 | 
			
		||||
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
 | 
			
		||||
/* Initializes log path for `log`.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
static int init_logging_session(const char *name, const char *selfkey, const char *otherkey, struct chatlog *log,
 | 
			
		||||
                                LOG_TYPE type)
 | 
			
		||||
{
 | 
			
		||||
    if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL))
 | 
			
		||||
    if (log == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (selfkey == NULL || (type == LOG_TYPE_CHAT && otherkey == NULL)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char log_path[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1)
 | 
			
		||||
        return -1;
 | 
			
		||||
    int path_len = get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey);
 | 
			
		||||
 | 
			
		||||
    log->file = fopen(log_path, "a+");
 | 
			
		||||
    snprintf(log->path, sizeof(log->path), "%s", log_path);
 | 
			
		||||
 | 
			
		||||
    if (log->file == NULL)
 | 
			
		||||
    if (path_len == -1 || path_len >= sizeof(log->path)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(log->path, log_path, path_len);
 | 
			
		||||
    log->path[path_len] = 0;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -119,8 +129,13 @@ static int init_logging_session(char *name, const char *selfkey, const char *oth
 | 
			
		||||
 | 
			
		||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
 | 
			
		||||
{
 | 
			
		||||
    if (!log->log_on)
 | 
			
		||||
    if (log == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!log->log_on) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (log->file == NULL) {
 | 
			
		||||
        log->log_on = false;
 | 
			
		||||
@@ -129,10 +144,11 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
 | 
			
		||||
 | 
			
		||||
    char name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
 | 
			
		||||
 | 
			
		||||
    if (event)
 | 
			
		||||
    if (event) {
 | 
			
		||||
        snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
 | 
			
		||||
    else
 | 
			
		||||
    } else {
 | 
			
		||||
        snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *t = user_settings->log_timestamp_format;
 | 
			
		||||
    char s[MAX_STR_SIZE];
 | 
			
		||||
@@ -147,86 +163,162 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
 | 
			
		||||
 | 
			
		||||
void log_disable(struct chatlog *log)
 | 
			
		||||
{
 | 
			
		||||
    if (log->file != NULL)
 | 
			
		||||
        fclose(log->file);
 | 
			
		||||
    if (log == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(log, 0, sizeof(struct chatlog));
 | 
			
		||||
    if (log->file != NULL) {
 | 
			
		||||
        fclose(log->file);
 | 
			
		||||
        log->file = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log->lastwrite = 0;
 | 
			
		||||
    log->log_on = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
 | 
			
		||||
int log_enable(struct chatlog *log)
 | 
			
		||||
{
 | 
			
		||||
    log->log_on = true;
 | 
			
		||||
 | 
			
		||||
    if (log->file != NULL)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
 | 
			
		||||
        log_disable(log);
 | 
			
		||||
    if (log == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (log->log_on) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (*log->path == 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (log->file != NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log->file = fopen(log->path, "a+");
 | 
			
		||||
 | 
			
		||||
    if (log->file == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log->log_on = true;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Loads previous history from chat log */
 | 
			
		||||
void load_chat_history(ToxWindow *self, struct chatlog *log)
 | 
			
		||||
/* Initializes a log. This function must be called before any other logging operations.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type)
 | 
			
		||||
{
 | 
			
		||||
    if (log->file == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
    if (log == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (log->file != NULL || log->log_on) {
 | 
			
		||||
        fprintf(stderr, "Warning: Called log_init() on an already initialized log\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (init_logging_session(name, selfkey, otherkey, log, type) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log_disable(log);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Loads chat log history and prints it to `self` window.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success or if log file doesn't exist.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int load_chat_history(ToxWindow *self, struct chatlog *log)
 | 
			
		||||
{
 | 
			
		||||
    if (log == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (*log->path == 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    off_t sz = file_size(log->path);
 | 
			
		||||
 | 
			
		||||
    if (sz <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    char *hstbuf = malloc(sz + 1);
 | 
			
		||||
 | 
			
		||||
    if (hstbuf == NULL)
 | 
			
		||||
        exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
 | 
			
		||||
 | 
			
		||||
    if (fseek(log->file, 0L, SEEK_SET) == -1) {
 | 
			
		||||
        free(hstbuf);
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
 | 
			
		||||
        return;
 | 
			
		||||
    if (sz <= 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fread(hstbuf, sz, 1, log->file) != 1) {
 | 
			
		||||
        free(hstbuf);
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
 | 
			
		||||
        return;
 | 
			
		||||
    FILE *fp = fopen(log->path, "r");
 | 
			
		||||
 | 
			
		||||
    if (fp == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hstbuf[sz] = '\0';
 | 
			
		||||
    char *buf = malloc(sz + 1);
 | 
			
		||||
 | 
			
		||||
    if (buf == NULL) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fseek(fp, 0L, SEEK_SET) == -1) {
 | 
			
		||||
        free(buf);
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fread(buf, sz, 1, fp) != 1) {
 | 
			
		||||
        free(buf);
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
 | 
			
		||||
    buf[sz] = 0;
 | 
			
		||||
 | 
			
		||||
    /* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
 | 
			
		||||
    int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
 | 
			
		||||
    int start, count = 0;
 | 
			
		||||
 | 
			
		||||
    int start = 0;
 | 
			
		||||
    int count = 0;
 | 
			
		||||
 | 
			
		||||
    /* start at end and backtrace L lines or to the beginning of buffer */
 | 
			
		||||
    for (start = sz - 1; start >= 0 && count < L; --start) {
 | 
			
		||||
        if (hstbuf[start] == '\n')
 | 
			
		||||
        if (buf[start] == '\n') {
 | 
			
		||||
            ++count;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *line = strtok(&hstbuf[start + 1], "\n");
 | 
			
		||||
    char *tmp = NULL;
 | 
			
		||||
    const char *line = strtok_r(&buf[start + 1], "\n", &tmp);
 | 
			
		||||
 | 
			
		||||
    if (line == NULL) {
 | 
			
		||||
        free(hstbuf);
 | 
			
		||||
        return;
 | 
			
		||||
        free(buf);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (line != NULL && count--) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
 | 
			
		||||
        line = strtok(NULL, "\n");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
 | 
			
		||||
        line = strtok_r(NULL, "\n", &tmp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
    free(hstbuf);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, YELLOW, "---");
 | 
			
		||||
 | 
			
		||||
    free(buf);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* renames chatlog file replacing src with dest.
 | 
			
		||||
   Returns 0 on success or if no log exists, -1 on failure. */
 | 
			
		||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum)
 | 
			
		||||
/* Renames chatlog file `src` to `dest`.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success or if no log exists.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow *toxwin = get_window_ptr(winnum);
 | 
			
		||||
    struct chatlog *log = NULL;
 | 
			
		||||
@@ -235,35 +327,60 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
 | 
			
		||||
    /* disable log if necessary and save its state */
 | 
			
		||||
    if (toxwin != NULL) {
 | 
			
		||||
        log = toxwin->chatwin->log;
 | 
			
		||||
 | 
			
		||||
        if (log == NULL) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        log_on = log->log_on;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (log_on)
 | 
			
		||||
    if (log_on) {
 | 
			
		||||
        log_disable(log);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char newpath[MAX_STR_SIZE];
 | 
			
		||||
    char oldpath[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
    if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1)
 | 
			
		||||
    if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey) == -1) {
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!file_exists(oldpath))
 | 
			
		||||
    if (!file_exists(oldpath)) {
 | 
			
		||||
        init_logging_session(dest, selfkey, otherkey, log, LOG_TYPE_CHAT);  // still need to rename path
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1)
 | 
			
		||||
    int new_path_len = get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey);
 | 
			
		||||
 | 
			
		||||
    if (new_path_len == -1 || new_path_len >= MAX_STR_SIZE) {
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (rename(oldpath, newpath) != 0)
 | 
			
		||||
    if (file_exists(newpath)) {
 | 
			
		||||
        if (remove(oldpath) != 0) {
 | 
			
		||||
            fprintf(stderr, "Warning: remove() failed to remove log path `%s`\n", oldpath);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (rename(oldpath, newpath) != 0) {
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (log_on)
 | 
			
		||||
        log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
 | 
			
		||||
    if (log != NULL) {
 | 
			
		||||
        memcpy(log->path, newpath, new_path_len);
 | 
			
		||||
        log->path[new_path_len] = 0;
 | 
			
		||||
 | 
			
		||||
        if (log_on) {
 | 
			
		||||
            log_enable(log);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
on_error:
 | 
			
		||||
    if (log_on)
 | 
			
		||||
        log_enable(src, selfkey, otherkey, log, LOG_CHAT);
 | 
			
		||||
 | 
			
		||||
    if (log_on) {
 | 
			
		||||
        log_enable(log);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								src/log.h
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								src/log.h
									
									
									
									
									
								
							@@ -25,35 +25,48 @@
 | 
			
		||||
 | 
			
		||||
struct chatlog {
 | 
			
		||||
    FILE *file;
 | 
			
		||||
    uint64_t lastwrite;
 | 
			
		||||
    time_t lastwrite;
 | 
			
		||||
    char path[MAX_STR_SIZE];
 | 
			
		||||
    bool log_on;    /* specific to current chat window */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    LOG_GROUP,
 | 
			
		||||
    LOG_PROMPT,
 | 
			
		||||
    LOG_CHAT,
 | 
			
		||||
typedef enum LOG_TYPE {
 | 
			
		||||
    LOG_TYPE_PROMPT,
 | 
			
		||||
    LOG_TYPE_CHAT,
 | 
			
		||||
} LOG_TYPE;
 | 
			
		||||
 | 
			
		||||
/* Initializes a log. This function must be called before any other logging operations.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type);
 | 
			
		||||
 | 
			
		||||
/* formats/writes line to log file */
 | 
			
		||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
 | 
			
		||||
 | 
			
		||||
/* enables logging for specified log and creates/fetches file if necessary.
 | 
			
		||||
/* enables logging for specified log.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
 | 
			
		||||
int log_enable(struct chatlog *log);
 | 
			
		||||
 | 
			
		||||
/* disables logging for specified log and closes file */
 | 
			
		||||
void log_disable(struct chatlog *log);
 | 
			
		||||
 | 
			
		||||
/* Loads previous history from chat log */
 | 
			
		||||
void load_chat_history(ToxWindow *self, struct chatlog *log);
 | 
			
		||||
/* Loads chat log history and prints it to `self` window.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success or if log file doesn't exist.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int load_chat_history(ToxWindow *self, struct chatlog *log);
 | 
			
		||||
 | 
			
		||||
/* renames chatlog file replacing src with dest.
 | 
			
		||||
   Returns 0 on success or if no log exists, -1 on failure. */
 | 
			
		||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
 | 
			
		||||
/* Renames chatlog file `src` to `dest`.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success or if no log exists.
 | 
			
		||||
 * Return -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum);
 | 
			
		||||
 | 
			
		||||
#endif /* #define LOG_H */
 | 
			
		||||
#endif /* LOG_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -22,12 +22,12 @@
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "message_queue.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "message_queue.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
void cqueue_cleanup(struct chat_queue *q)
 | 
			
		||||
{
 | 
			
		||||
@@ -42,19 +42,24 @@ void cqueue_cleanup(struct chat_queue *q)
 | 
			
		||||
    free(q);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id)
 | 
			
		||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id)
 | 
			
		||||
{
 | 
			
		||||
    if (line_id < 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
 | 
			
		||||
 | 
			
		||||
    if (new_m == NULL)
 | 
			
		||||
    if (new_m == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
 | 
			
		||||
    new_m->len = len;
 | 
			
		||||
    new_m->type = type;
 | 
			
		||||
    new_m->line_id = line_id;
 | 
			
		||||
    new_m->last_send_try = 0;
 | 
			
		||||
    new_m->receipt = 0;
 | 
			
		||||
    new_m->receipt = -1;
 | 
			
		||||
    new_m->next = NULL;
 | 
			
		||||
 | 
			
		||||
    if (q->root == NULL) {
 | 
			
		||||
@@ -81,9 +86,9 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
 | 
			
		||||
 | 
			
		||||
        line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
 | 
			
		||||
 | 
			
		||||
        if (line->noread_flag == true) {
 | 
			
		||||
            line->len -= 2;
 | 
			
		||||
        if (line->noread_flag) {
 | 
			
		||||
            line->noread_flag = false;
 | 
			
		||||
            line->read_flag = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
@@ -93,6 +98,7 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
 | 
			
		||||
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
 | 
			
		||||
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
 | 
			
		||||
{
 | 
			
		||||
    struct chatlog *log = self->chatwin->log;
 | 
			
		||||
    struct chat_queue *q = self->chatwin->cqueue;
 | 
			
		||||
    struct cqueue_msg *msg = q->root;
 | 
			
		||||
 | 
			
		||||
@@ -102,53 +108,84 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (log->log_on) {
 | 
			
		||||
            char selfname[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
            tox_self_get_name(m, (uint8_t *) selfname);
 | 
			
		||||
 | 
			
		||||
            size_t len = tox_self_get_name_size(m);
 | 
			
		||||
        selfname[len] = '\0';
 | 
			
		||||
            selfname[len] = 0;
 | 
			
		||||
 | 
			
		||||
            write_to_log(msg->message, selfname, log, msg->type == OUT_ACTION);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION);
 | 
			
		||||
        cqueue_mark_read(self, msg);
 | 
			
		||||
 | 
			
		||||
        struct cqueue_msg *next = msg->next;
 | 
			
		||||
 | 
			
		||||
        if (msg->prev == NULL) {    /* root */
 | 
			
		||||
            if (next)
 | 
			
		||||
            if (next) {
 | 
			
		||||
                next->prev = NULL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            free(msg);
 | 
			
		||||
            q->root = next;
 | 
			
		||||
        } else {
 | 
			
		||||
            struct cqueue_msg *prev = msg->prev;
 | 
			
		||||
            free(msg);
 | 
			
		||||
            prev->next = next;
 | 
			
		||||
            next->prev = prev;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        free(msg);
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CQUEUE_TRY_SEND_INTERVAL 60
 | 
			
		||||
// We use knowledge of toxcore internals (bad!) to determine that if we haven't received a read receipt for a
 | 
			
		||||
// sent packet after this amount of time, the connection has been severed and the packet needs to be re-sent.
 | 
			
		||||
#define TRY_SEND_TIMEOUT 32
 | 
			
		||||
 | 
			
		||||
/* Tries to send the oldest unsent message in queue. */
 | 
			
		||||
/*
 | 
			
		||||
 * Marks all timed out messages in queue as unsent.
 | 
			
		||||
 */
 | 
			
		||||
static void cqueue_check_timeouts(struct cqueue_msg *msg)
 | 
			
		||||
{
 | 
			
		||||
    while (msg) {
 | 
			
		||||
        if (timed_out(msg->last_send_try, TRY_SEND_TIMEOUT)) {
 | 
			
		||||
            msg->receipt = -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        msg = msg->next;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tries to send all messages in the send queue in sequential order.
 | 
			
		||||
 * If a message fails to send the function will immediately return.
 | 
			
		||||
 */
 | 
			
		||||
void cqueue_try_send(ToxWindow *self, Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    struct chat_queue *q = self->chatwin->cqueue;
 | 
			
		||||
    struct cqueue_msg *msg = q->root;
 | 
			
		||||
 | 
			
		||||
    if (!msg)
 | 
			
		||||
    while (msg) {
 | 
			
		||||
        if (msg->receipt != -1) {
 | 
			
		||||
            // we can no longer try to send unsent messages until we get receipts for our previous sent
 | 
			
		||||
            // messages, but we continue to iterate the list, checking timestamps for any further
 | 
			
		||||
            // successfully sent messages that have not yet gotten a receipt.
 | 
			
		||||
            cqueue_check_timeouts(msg);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL))
 | 
			
		||||
        TOX_ERR_FRIEND_SEND_MESSAGE err;
 | 
			
		||||
        Tox_Message_Type type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
 | 
			
		||||
        uint32_t receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, &err);
 | 
			
		||||
 | 
			
		||||
        if (err != TOX_ERR_FRIEND_SEND_MESSAGE_OK) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    uint32_t receipt = 0;
 | 
			
		||||
 | 
			
		||||
    TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
 | 
			
		||||
    receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
 | 
			
		||||
 | 
			
		||||
    msg->last_send_try = get_unix_time();
 | 
			
		||||
        msg->receipt = receipt;
 | 
			
		||||
    return;
 | 
			
		||||
        msg->last_send_try = get_unix_time();
 | 
			
		||||
        msg = msg->next;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,9 +27,9 @@ struct cqueue_msg {
 | 
			
		||||
    char message[MAX_STR_SIZE];
 | 
			
		||||
    size_t len;
 | 
			
		||||
    int line_id;
 | 
			
		||||
    time_t last_send_try;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    uint32_t receipt;
 | 
			
		||||
    uint64_t last_send_try;
 | 
			
		||||
    int64_t receipt;
 | 
			
		||||
    struct cqueue_msg *next;
 | 
			
		||||
    struct cqueue_msg *prev;
 | 
			
		||||
};
 | 
			
		||||
@@ -40,12 +40,15 @@ struct chat_queue {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void cqueue_cleanup(struct chat_queue *q);
 | 
			
		||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id);
 | 
			
		||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id);
 | 
			
		||||
 | 
			
		||||
/* Tries to send the oldest unsent message in queue. */
 | 
			
		||||
/*
 | 
			
		||||
 * Tries to send all messages in the send queue in sequential order.
 | 
			
		||||
 * If a message fails to send the function will immediately return.
 | 
			
		||||
 */
 | 
			
		||||
void cqueue_try_send(ToxWindow *self, Tox *m);
 | 
			
		||||
 | 
			
		||||
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
 | 
			
		||||
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt);
 | 
			
		||||
 | 
			
		||||
#endif  /* #define MESSAGE_QUEUE_H */
 | 
			
		||||
#endif /* MESSAGE_QUEUE_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										433
									
								
								src/misc_tools.c
									
									
									
									
									
								
							
							
						
						
									
										433
									
								
								src/misc_tools.c
									
									
									
									
									
								
							@@ -20,94 +20,120 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "file_transfers.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
extern ToxWindow *prompt;
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
 | 
			
		||||
static uint64_t current_unix_time;
 | 
			
		||||
void clear_screen(void)
 | 
			
		||||
{
 | 
			
		||||
    printf("\033[2J\033[1;1H");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void hst_to_net(uint8_t *num, uint16_t numbytes)
 | 
			
		||||
{
 | 
			
		||||
#ifndef WORDS_BIGENDIAN
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
    uint8_t buff[numbytes];
 | 
			
		||||
    uint8_t *buff = malloc(numbytes);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < numbytes; ++i) {
 | 
			
		||||
    if (buff == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0; i < numbytes; ++i) {
 | 
			
		||||
        buff[i] = num[numbytes - i - 1];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(num, buff, numbytes);
 | 
			
		||||
    free(buff);
 | 
			
		||||
#endif
 | 
			
		||||
    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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Attempts to sleep the caller's thread for `usec` microseconds */
 | 
			
		||||
void sleep_thread(long int usec)
 | 
			
		||||
{
 | 
			
		||||
    struct timespec req;
 | 
			
		||||
    struct timespec rem;
 | 
			
		||||
 | 
			
		||||
    req.tv_sec = 0;
 | 
			
		||||
    req.tv_nsec = usec * 1000L;
 | 
			
		||||
 | 
			
		||||
    if (nanosleep(&req, &rem) == -1) {
 | 
			
		||||
        if (nanosleep(&rem, NULL) == -1) {
 | 
			
		||||
            fprintf(stderr, "nanosleep() returned -1\n");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get the current local time */
 | 
			
		||||
struct tm *get_time(void)
 | 
			
		||||
{
 | 
			
		||||
    struct tm *timeinfo;
 | 
			
		||||
    uint64_t t = get_unix_time();
 | 
			
		||||
    timeinfo = localtime((const time_t*) &t);
 | 
			
		||||
    time_t t = get_unix_time();
 | 
			
		||||
    timeinfo = localtime((const time_t *) &t);
 | 
			
		||||
    return timeinfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*Puts the current time in buf in the format of [HH:mm:ss] */
 | 
			
		||||
void get_time_str(char *buf, int bufsize)
 | 
			
		||||
/* Puts the current time in buf in the format of specified by the config */
 | 
			
		||||
void get_time_str(char *buf, size_t bufsize)
 | 
			
		||||
{
 | 
			
		||||
    if (buf == NULL || bufsize == 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *buf = 0;
 | 
			
		||||
 | 
			
		||||
    if (user_settings->timestamps == TIMESTAMPS_OFF) {
 | 
			
		||||
        buf[0] = '\0';
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *t = user_settings->timestamp_format;
 | 
			
		||||
    strftime(buf, bufsize, t, get_time());
 | 
			
		||||
 | 
			
		||||
    if (strftime(buf, bufsize, t, get_time()) == 0) {
 | 
			
		||||
        strftime(buf, bufsize, TIMESTAMP_DEFAULT, get_time());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
 | 
			
		||||
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
 | 
			
		||||
void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
 | 
			
		||||
{
 | 
			
		||||
    if (!secs)
 | 
			
		||||
    if (!secs) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long int seconds = secs % 60;
 | 
			
		||||
    long int minutes = (secs % 3600) / 60;
 | 
			
		||||
    long int hours = secs / 3600;
 | 
			
		||||
 | 
			
		||||
    if (!minutes && !hours)
 | 
			
		||||
    if (!minutes && !hours) {
 | 
			
		||||
        snprintf(buf, bufsize, "%.2ld", seconds);
 | 
			
		||||
    else if (!hours)
 | 
			
		||||
    } else if (!hours) {
 | 
			
		||||
        snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds);
 | 
			
		||||
    else
 | 
			
		||||
    } else {
 | 
			
		||||
        snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -119,11 +145,12 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
 | 
			
		||||
 */
 | 
			
		||||
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
 | 
			
		||||
{
 | 
			
		||||
    if (output_size == 0 || hex_len != output_size * 2)
 | 
			
		||||
    if (output_size == 0 || hex_len != output_size * 2) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < output_size; ++i) {
 | 
			
		||||
        sscanf(hex_string, "%2hhx", &output[i]);
 | 
			
		||||
        sscanf(hex_string, "%2hhx", (unsigned char *)&output[i]);
 | 
			
		||||
        hex_string += 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -132,19 +159,20 @@ int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size
 | 
			
		||||
 | 
			
		||||
int hex_string_to_bytes(char *buf, int size, const char *keystr)
 | 
			
		||||
{
 | 
			
		||||
    if (size % 2 != 0)
 | 
			
		||||
    if (size % 2 != 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i, res;
 | 
			
		||||
    const char *pos = keystr;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < size; ++i) {
 | 
			
		||||
        res = sscanf(pos, "%2hhx", &buf[i]);
 | 
			
		||||
    for (size_t i = 0; i < size; ++i) {
 | 
			
		||||
        int res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]);
 | 
			
		||||
        pos += 2;
 | 
			
		||||
 | 
			
		||||
        if (res == EOF || res < 1)
 | 
			
		||||
        if (res == EOF || res < 1) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -156,13 +184,31 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr)
 | 
			
		||||
 */
 | 
			
		||||
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))
 | 
			
		||||
    if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t i;
 | 
			
		||||
    for (size_t i = 0; i < TOX_ADDRESS_SIZE; ++i) {
 | 
			
		||||
        snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
 | 
			
		||||
        snprintf(&output[i*2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Converts a binary representation of a Tox public key into a string.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size)
 | 
			
		||||
{
 | 
			
		||||
    if (bin_pubkey_size != TOX_PUBLIC_KEY_SIZE || output_size < (TOX_PUBLIC_KEY_SIZE * 2 + 1)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) {
 | 
			
		||||
        snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_pubkey[i] & 0xff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -170,22 +216,35 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_
 | 
			
		||||
/* Returns 1 if the string is empty, 0 otherwise */
 | 
			
		||||
int string_is_empty(const char *string)
 | 
			
		||||
{
 | 
			
		||||
    if (!string)
 | 
			
		||||
    if (!string) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return string[0] == '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns 1 if the string is empty, 0 otherwise */
 | 
			
		||||
int wstring_is_empty(const wchar_t *string)
 | 
			
		||||
{
 | 
			
		||||
    if (!string) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return string[0] == L'\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* convert a multibyte string to a wide character string and puts in buf. */
 | 
			
		||||
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
 | 
			
		||||
{
 | 
			
		||||
    size_t len = mbstowcs(NULL, string, 0) + 1;
 | 
			
		||||
 | 
			
		||||
    if (n < len)
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
@@ -195,11 +254,13 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
 | 
			
		||||
{
 | 
			
		||||
    size_t len = wcstombs(NULL, string, 0) + 1;
 | 
			
		||||
 | 
			
		||||
    if (n < len)
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
@@ -210,42 +271,68 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
 | 
			
		||||
    return strcasecmp((const char *) str1, (const char *) str2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
 | 
			
		||||
      - cannot be empty
 | 
			
		||||
      - cannot start with a space
 | 
			
		||||
      - must not contain a forward slash (for logfile naming purposes)
 | 
			
		||||
      - must not contain contiguous spaces
 | 
			
		||||
      - must not contain a newline or tab seqeunce */
 | 
			
		||||
int valid_nick(const char *nick)
 | 
			
		||||
/* case-insensitive string compare function for use with qsort */
 | 
			
		||||
int qsort_ptr_char_array_helper(const void *str1, const void *str2)
 | 
			
		||||
{
 | 
			
		||||
    if (!nick[0] || nick[0] == ' ')
 | 
			
		||||
        return 0;
 | 
			
		||||
    return strcasecmp(*(char **)str1, *(char **)str2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
static const char invalid_chars[] = {'/', '\n', '\t', '\v', '\r', '\0'};
 | 
			
		||||
 | 
			
		||||
    for (i = 0; nick[i]; ++i) {
 | 
			
		||||
        if ((nick[i] == ' ' && nick[i + 1] == ' ')
 | 
			
		||||
            || nick[i] == '/'
 | 
			
		||||
            || nick[i] == '\n'
 | 
			
		||||
            || nick[i] == '\t'
 | 
			
		||||
            || nick[i] == '\v'
 | 
			
		||||
            || nick[i] == '\r')
 | 
			
		||||
/*
 | 
			
		||||
 * Helper function for `valid_nick()`.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns true if `ch` is not in the `invalid_chars` array.
 | 
			
		||||
 */
 | 
			
		||||
static bool is_valid_char(char ch)
 | 
			
		||||
{
 | 
			
		||||
    char tmp;
 | 
			
		||||
 | 
			
		||||
            return 0;
 | 
			
		||||
    for (size_t i = 0; (tmp = invalid_chars[i]); ++i) {
 | 
			
		||||
        if (tmp == ch) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns true if nick is valid.
 | 
			
		||||
 *
 | 
			
		||||
 * A valid toxic nick:
 | 
			
		||||
 * - cannot be empty
 | 
			
		||||
 * - cannot start with a space
 | 
			
		||||
 * - must not contain a forward slash (for logfile naming purposes)
 | 
			
		||||
 * - must not contain contiguous spaces
 | 
			
		||||
 * - must not contain a newline or tab seqeunce
 | 
			
		||||
 */
 | 
			
		||||
bool valid_nick(const char *nick)
 | 
			
		||||
{
 | 
			
		||||
    if (!nick[0] || nick[0] == ' ') {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; nick[i]; ++i) {
 | 
			
		||||
        char ch = nick[i];
 | 
			
		||||
 | 
			
		||||
        if ((ch == ' ' && nick[i + 1] == ' ') || !is_valid_char(ch)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
 | 
			
		||||
void filter_str(char *str, size_t len)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
    for (size_t i = 0; i < len; ++i) {
 | 
			
		||||
        char ch = str[i];
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < len; ++i) {
 | 
			
		||||
        if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0')
 | 
			
		||||
        if (!is_valid_char(ch) || str[i] == '\0') {
 | 
			
		||||
            str[i] = ' ';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* gets base file name from path or original file name if no path is supplied.
 | 
			
		||||
@@ -256,23 +343,27 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
 | 
			
		||||
    int len = strlen(pathname) - 1;
 | 
			
		||||
    char *path = strdup(pathname);
 | 
			
		||||
 | 
			
		||||
    if (path == NULL)
 | 
			
		||||
    if (path == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (len >= 0 && pathname[len] == '/')
 | 
			
		||||
    while (len >= 0 && pathname[len] == '/') {
 | 
			
		||||
        path[len--] = '\0';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char *finalname = strdup(path);
 | 
			
		||||
 | 
			
		||||
    if (finalname == NULL)
 | 
			
		||||
    if (finalname == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *basenm = strrchr(path, '/');
 | 
			
		||||
 | 
			
		||||
    if (basenm != NULL) {
 | 
			
		||||
        if (basenm[1])
 | 
			
		||||
        if (basenm[1]) {
 | 
			
		||||
            strcpy(finalname, &basenm[1]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(namebuf, bufsize, "%s", finalname);
 | 
			
		||||
    free(finalname);
 | 
			
		||||
@@ -288,13 +379,15 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
 | 
			
		||||
 */
 | 
			
		||||
size_t get_base_dir(const char *path, size_t path_len, char *dir)
 | 
			
		||||
{
 | 
			
		||||
    if (path_len == 0 || path == NULL)
 | 
			
		||||
    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)
 | 
			
		||||
    if (dir_len != 0 && dir_len < path_len) {
 | 
			
		||||
        ++dir_len;    /* Leave trailing slash */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(dir, path, dir_len);
 | 
			
		||||
    dir[dir_len] = '\0';
 | 
			
		||||
@@ -307,8 +400,9 @@ void str_to_lower(char *str)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; str[i]; ++i)
 | 
			
		||||
    for (i = 0; str[i]; ++i) {
 | 
			
		||||
        str[i] = tolower(str[i]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
 | 
			
		||||
@@ -316,71 +410,107 @@ void str_to_lower(char *str)
 | 
			
		||||
   Returns nick len */
 | 
			
		||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
 | 
			
		||||
{
 | 
			
		||||
    size_t len = tox_friend_get_name_size(m, friendnum, NULL);
 | 
			
		||||
    Tox_Err_Friend_Query err;
 | 
			
		||||
    size_t len = tox_friend_get_name_size(m, friendnum, &err);
 | 
			
		||||
 | 
			
		||||
    if (len == 0) {
 | 
			
		||||
        strcpy(buf, UNKNOWN_NAME);
 | 
			
		||||
        len = strlen(UNKNOWN_NAME);
 | 
			
		||||
    if (err != TOX_ERR_FRIEND_QUERY_OK) {
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    } else {
 | 
			
		||||
        tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL);
 | 
			
		||||
        if (!tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL)) {
 | 
			
		||||
            goto on_error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
 | 
			
		||||
    buf[len] = '\0';
 | 
			
		||||
    filter_str(buf, len);
 | 
			
		||||
    return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* same as get_nick_truncate but for groupchats */
 | 
			
		||||
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum)
 | 
			
		||||
{
 | 
			
		||||
    int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf);
 | 
			
		||||
 | 
			
		||||
    if (len == -1) {
 | 
			
		||||
on_error:
 | 
			
		||||
    strcpy(buf, UNKNOWN_NAME);
 | 
			
		||||
    len = strlen(UNKNOWN_NAME);
 | 
			
		||||
    buf[len] = '\0';
 | 
			
		||||
    return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* same as get_nick_truncate but for conferences */
 | 
			
		||||
int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum)
 | 
			
		||||
{
 | 
			
		||||
    Tox_Err_Conference_Peer_Query err;
 | 
			
		||||
    size_t len = tox_conference_peer_get_name_size(m, conferencenum, peernum, &err);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    } else {
 | 
			
		||||
        if (!tox_conference_peer_get_name(m, conferencenum, peernum, (uint8_t *) buf, NULL)) {
 | 
			
		||||
            goto on_error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
 | 
			
		||||
    buf[len] = '\0';
 | 
			
		||||
    filter_str(buf, len);
 | 
			
		||||
    return len;
 | 
			
		||||
 | 
			
		||||
on_error:
 | 
			
		||||
    strcpy(buf, UNKNOWN_NAME);
 | 
			
		||||
    len = strlen(UNKNOWN_NAME);
 | 
			
		||||
    buf[len] = '\0';
 | 
			
		||||
    return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* copies data to msg buffer.
 | 
			
		||||
/* copies data to msg buffer, removing return characters.
 | 
			
		||||
   returns length of msg, which will be no larger than size-1 */
 | 
			
		||||
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;
 | 
			
		||||
    size_t i;
 | 
			
		||||
    size_t j = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; (i < length) && (j < size - 1); ++i) {
 | 
			
		||||
        if (data[i] != '\r') {
 | 
			
		||||
            msg[j++] = data[i];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    msg[j] = '\0';
 | 
			
		||||
 | 
			
		||||
    return j;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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) {
 | 
			
		||||
        if (s[i] == ch)
 | 
			
		||||
        if (s[i] == ch) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns index of the last instance of ch in s starting at len.
 | 
			
		||||
   returns 0 if char not found (skips 0th index). */
 | 
			
		||||
   returns 0 if char not found or s is NULL (skips 0th index). */
 | 
			
		||||
int char_rfind(const char *s, char ch, int len)
 | 
			
		||||
{
 | 
			
		||||
    if (!s) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    for (i = len; i > 0; --i) {
 | 
			
		||||
        if (s[i] == ch)
 | 
			
		||||
        if (s[i] == ch) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
@@ -414,45 +544,59 @@ bool file_exists(const char *path)
 | 
			
		||||
    return stat(path, &s) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Checks the file type path points to and returns a File_Type enum value.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns FILE_TYPE_DIRECTORY if path points to a directory.
 | 
			
		||||
 * Returns FILE_TYPE_REGULAR if path points to a regular file.
 | 
			
		||||
 * Returns FILE_TYPE_OTHER on any other result, including an invalid path.
 | 
			
		||||
 */
 | 
			
		||||
File_Type file_type(const char *path)
 | 
			
		||||
{
 | 
			
		||||
    struct stat s;
 | 
			
		||||
 | 
			
		||||
    if (stat(path, &s) == -1) {
 | 
			
		||||
        return FILE_TYPE_OTHER;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (s.st_mode & S_IFMT) {
 | 
			
		||||
        case S_IFDIR:
 | 
			
		||||
            return FILE_TYPE_DIRECTORY;
 | 
			
		||||
 | 
			
		||||
        case S_IFREG:
 | 
			
		||||
            return FILE_TYPE_REGULAR;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            return FILE_TYPE_OTHER;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns file size. If file doesn't exist returns 0. */
 | 
			
		||||
off_t file_size(const char *path)
 | 
			
		||||
{
 | 
			
		||||
    struct stat st;
 | 
			
		||||
 | 
			
		||||
    if (stat(path, &st) == -1)
 | 
			
		||||
    if (stat(path, &st) == -1) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return st.st_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* compares the first size bytes of fp to signature.
 | 
			
		||||
   Returns 0 if they are the same, 1 if they differ, and -1 on error.
 | 
			
		||||
 | 
			
		||||
   On success this function will seek back to the beginning of fp */
 | 
			
		||||
int check_file_signature(const char *signature, size_t size, FILE *fp)
 | 
			
		||||
{
 | 
			
		||||
    char buf[size];
 | 
			
		||||
 | 
			
		||||
    if (fread(buf, size, 1, fp) != 1)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    int ret = memcmp(signature, buf, size);
 | 
			
		||||
 | 
			
		||||
    if (fseek(fp, 0L, SEEK_SET) == -1)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return ret == 0 ? 0 : 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* sets window title in tab bar. */
 | 
			
		||||
void set_window_title(ToxWindow *self, const char *title, int len)
 | 
			
		||||
{
 | 
			
		||||
    if (len <= 0 || !title) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char cpy[TOXIC_MAX_NAME_LENGTH + 1];
 | 
			
		||||
 | 
			
		||||
    if (self->is_groupchat)   /* keep groupnumber in title */
 | 
			
		||||
        snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
 | 
			
		||||
    else
 | 
			
		||||
    if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title for invites */
 | 
			
		||||
        snprintf(cpy, sizeof(cpy), "%u %s", self->num, title);
 | 
			
		||||
    } else {
 | 
			
		||||
        snprintf(cpy, sizeof(cpy), "%s", title);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (len > MAX_WINDOW_NAME_LENGTH) {
 | 
			
		||||
        strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");
 | 
			
		||||
@@ -461,3 +605,50 @@ void set_window_title(ToxWindow *self, const char *title, int len)
 | 
			
		||||
 | 
			
		||||
    snprintf(self->name, sizeof(self->name), "%s", cpy);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Frees all members of a pointer array plus `arr`.
 | 
			
		||||
 */
 | 
			
		||||
void free_ptr_array(void **arr)
 | 
			
		||||
{
 | 
			
		||||
    if (arr == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void **tmp = arr;
 | 
			
		||||
 | 
			
		||||
    while (*arr) {
 | 
			
		||||
        free(*arr);
 | 
			
		||||
        ++arr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(tmp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns a null terminated array of `length` pointers. Each pointer is allocated `bytes` bytes.
 | 
			
		||||
 * Returns NULL on failure.
 | 
			
		||||
 *
 | 
			
		||||
 * The caller is responsible for freeing the array with `free_ptr_array`.
 | 
			
		||||
 */
 | 
			
		||||
void **malloc_ptr_array(size_t length, size_t bytes)
 | 
			
		||||
{
 | 
			
		||||
    void **arr = malloc((length + 1) * sizeof(void *));
 | 
			
		||||
 | 
			
		||||
    if (arr == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < length; ++i) {
 | 
			
		||||
        arr[i] = malloc(bytes);
 | 
			
		||||
 | 
			
		||||
        if (arr[i] == NULL) {
 | 
			
		||||
            free_ptr_array(arr);
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    arr[length] = NULL;
 | 
			
		||||
 | 
			
		||||
    return arr;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								src/misc_tools.h
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								src/misc_tools.h
									
									
									
									
									
								
							@@ -24,8 +24,8 @@
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
#ifndef MIN
 | 
			
		||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
 | 
			
		||||
@@ -39,6 +39,17 @@
 | 
			
		||||
#define net_to_host(x, y) hst_to_net(x, y)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define UNUSED_VAR(x) ((void) x)
 | 
			
		||||
 | 
			
		||||
typedef enum File_Type {
 | 
			
		||||
    FILE_TYPE_REGULAR,
 | 
			
		||||
    FILE_TYPE_DIRECTORY,
 | 
			
		||||
    FILE_TYPE_OTHER,
 | 
			
		||||
} File_Type;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void clear_screen(void);
 | 
			
		||||
 | 
			
		||||
void hst_to_net(uint8_t *num, uint16_t numbytes);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -60,35 +71,45 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr);
 | 
			
		||||
 */
 | 
			
		||||
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);
 | 
			
		||||
/* Converts a binary representation of a Tox public key into a string.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size);
 | 
			
		||||
 | 
			
		||||
/* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */
 | 
			
		||||
void get_time_str(char *buf, int bufsize);
 | 
			
		||||
/* get the current unix time (not thread safe) */
 | 
			
		||||
time_t get_unix_time(void);
 | 
			
		||||
 | 
			
		||||
/* Puts the current time in buf in the format of specified by the config */
 | 
			
		||||
void get_time_str(char *buf, size_t bufsize);
 | 
			
		||||
 | 
			
		||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
 | 
			
		||||
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs);
 | 
			
		||||
void get_elapsed_time_str(char *buf, int bufsize, time_t secs);
 | 
			
		||||
 | 
			
		||||
/* get the current local time (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);
 | 
			
		||||
 | 
			
		||||
/* convert a multibyte string to a wide character string (must provide buffer) */
 | 
			
		||||
/* Same as above but for wide character strings */
 | 
			
		||||
int wstring_is_empty(const wchar_t *string);
 | 
			
		||||
 | 
			
		||||
/* converts 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);
 | 
			
		||||
 | 
			
		||||
/* converts wide character string into a multibyte string and puts in buf. */
 | 
			
		||||
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
 | 
			
		||||
 | 
			
		||||
/* convert a multibyte string to a wide character string and puts in buf) */
 | 
			
		||||
/* converts 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);
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
 | 
			
		||||
/* Attempts to sleep the caller's thread for `usec` microseconds */
 | 
			
		||||
void sleep_thread(long int usec);
 | 
			
		||||
 | 
			
		||||
/* Colours the window tab according to type. Beeps if is_beep is true */
 | 
			
		||||
void alert_window(ToxWindow *self, int type, bool is_beep);
 | 
			
		||||
@@ -96,13 +117,19 @@ void alert_window(ToxWindow *self, int type, bool is_beep);
 | 
			
		||||
/* case-insensitive string compare function for use with qsort */
 | 
			
		||||
int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
 | 
			
		||||
 | 
			
		||||
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
 | 
			
		||||
      - cannot be empty
 | 
			
		||||
      - cannot start with a space
 | 
			
		||||
      - must not contain a forward slash (for logfile naming purposes)
 | 
			
		||||
      - must not contain contiguous spaces
 | 
			
		||||
      - must not contain a newline or tab seqeunce */
 | 
			
		||||
int valid_nick(const char *nick);
 | 
			
		||||
/* case-insensitive string compare function for use with qsort */
 | 
			
		||||
int qsort_ptr_char_array_helper(const void *str1, const void *str2);
 | 
			
		||||
 | 
			
		||||
/* Returns true if nick is valid.
 | 
			
		||||
 *
 | 
			
		||||
 * A valid toxic nick:
 | 
			
		||||
 * - cannot be empty
 | 
			
		||||
 * - cannot start with a space
 | 
			
		||||
 * - must not contain a forward slash (for logfile naming purposes)
 | 
			
		||||
 * - must not contain contiguous spaces
 | 
			
		||||
 * - must not contain a newline or tab seqeunce
 | 
			
		||||
 */
 | 
			
		||||
bool valid_nick(const char *nick);
 | 
			
		||||
 | 
			
		||||
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
 | 
			
		||||
void filter_str(char *str, size_t len);
 | 
			
		||||
@@ -125,19 +152,19 @@ void str_to_lower(char *str);
 | 
			
		||||
   Returns nick len on success, -1 on failure */
 | 
			
		||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
 | 
			
		||||
 | 
			
		||||
/* same as get_nick_truncate but for groupchats */
 | 
			
		||||
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum);
 | 
			
		||||
/* same as get_nick_truncate but for conferences */
 | 
			
		||||
int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum);
 | 
			
		||||
 | 
			
		||||
/* copies data to msg buffer.
 | 
			
		||||
   returns length of msg, which will be no larger than size-1 */
 | 
			
		||||
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 */
 | 
			
		||||
@@ -146,16 +173,32 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes);
 | 
			
		||||
/* checks if a file exists. Returns true or false */
 | 
			
		||||
bool file_exists(const char *path);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Checks the file type path points to and returns a File_Type enum value.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns FILE_TYPE_DIRECTORY if path points to a directory.
 | 
			
		||||
 * Returns FILE_TYPE_REGULAR if path points to a regular file.
 | 
			
		||||
 * Returns FILE_TYPE_OTHER on any other result, including an invalid path.
 | 
			
		||||
 */
 | 
			
		||||
File_Type file_type(const char *path);
 | 
			
		||||
 | 
			
		||||
/* returns file size. If file doesn't exist returns 0. */
 | 
			
		||||
off_t file_size(const char *path);
 | 
			
		||||
 | 
			
		||||
/* compares the first size bytes of fp and signature.
 | 
			
		||||
   Returns 0 if they are the same, 1 if they differ, and -1 on error.
 | 
			
		||||
 | 
			
		||||
   On success this function will seek back to the beginning of fp */
 | 
			
		||||
int check_file_signature(const char *signature, size_t size, FILE *fp);
 | 
			
		||||
 | 
			
		||||
/* sets window title in tab bar. */
 | 
			
		||||
void set_window_title(ToxWindow *self, const char *title, int len);
 | 
			
		||||
 | 
			
		||||
#endif /* #define MISC_TOOLS_H */
 | 
			
		||||
/*
 | 
			
		||||
 * Frees all members of a pointer array plus `arr`.
 | 
			
		||||
 */
 | 
			
		||||
void free_ptr_array(void **arr);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns a null terminated array of `length` pointers. Each pointer is allocated `bytes` bytes.
 | 
			
		||||
 * Returns NULL on failure.
 | 
			
		||||
 *
 | 
			
		||||
 * The caller is responsible for freeing the array with `free_ptr_array`.
 | 
			
		||||
 */
 | 
			
		||||
void **malloc_ptr_array(size_t length, size_t bytes);
 | 
			
		||||
 | 
			
		||||
#endif /* MISC_TOOLS_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -20,20 +20,21 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/types.h> /* for u_char */
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
 | 
			
		||||
#include "configdir.h"
 | 
			
		||||
#include "curl_util.h"
 | 
			
		||||
#include "global_commands.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "global_commands.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "configdir.h"
 | 
			
		||||
 | 
			
		||||
extern struct arg_opts arg_opts;
 | 
			
		||||
extern struct Winthread Winthread;;
 | 
			
		||||
extern struct Winthread Winthread;
 | 
			
		||||
 | 
			
		||||
#define NAMESERVER_API_PATH "api"
 | 
			
		||||
#define SERVER_KEY_SIZE 32
 | 
			
		||||
@@ -41,10 +42,7 @@ extern struct Winthread Winthread;;
 | 
			
		||||
#define MAX_DOMAIN_SIZE 32
 | 
			
		||||
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
 | 
			
		||||
 | 
			
		||||
/* 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"
 | 
			
		||||
 | 
			
		||||
struct Nameservers {
 | 
			
		||||
static struct Nameservers {
 | 
			
		||||
    int     lines;
 | 
			
		||||
    char    names[MAX_SERVERS][MAX_DOMAIN_SIZE];
 | 
			
		||||
    char    keys[MAX_SERVERS][SERVER_KEY_SIZE];
 | 
			
		||||
@@ -56,8 +54,8 @@ static struct thread_data {
 | 
			
		||||
    char    id_bin[TOX_ADDRESS_SIZE];
 | 
			
		||||
    char    addr[MAX_STR_SIZE];
 | 
			
		||||
    char    msg[MAX_STR_SIZE];
 | 
			
		||||
    bool    busy;
 | 
			
		||||
    bool    disabled;
 | 
			
		||||
    volatile bool busy;
 | 
			
		||||
} t_data;
 | 
			
		||||
 | 
			
		||||
static struct lookup_thread {
 | 
			
		||||
@@ -65,6 +63,13 @@ static struct lookup_thread {
 | 
			
		||||
    pthread_attr_t attr;
 | 
			
		||||
} lookup_thread;
 | 
			
		||||
 | 
			
		||||
static void clear_thread_data(void)
 | 
			
		||||
{
 | 
			
		||||
    t_data = (struct thread_data) {
 | 
			
		||||
        0
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int lookup_error(ToxWindow *self, const char *errmsg, ...)
 | 
			
		||||
{
 | 
			
		||||
    char frmt_msg[MAX_STR_SIZE];
 | 
			
		||||
@@ -75,7 +80,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...)
 | 
			
		||||
    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);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg);
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
@@ -83,7 +88,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...)
 | 
			
		||||
 | 
			
		||||
static void kill_lookup_thread(void)
 | 
			
		||||
{
 | 
			
		||||
    memset(&t_data, 0, sizeof(struct thread_data));
 | 
			
		||||
    clear_thread_data();
 | 
			
		||||
    pthread_attr_destroy(&lookup_thread.attr);
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
@@ -99,42 +104,49 @@ static int load_nameserver_list(const char *path)
 | 
			
		||||
{
 | 
			
		||||
    FILE *fp = fopen(path, "r");
 | 
			
		||||
 | 
			
		||||
    if (fp == NULL)
 | 
			
		||||
    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)
 | 
			
		||||
        if (linelen < SERVER_KEY_SIZE * 2 + 5) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (line[linelen - 1] == '\n')
 | 
			
		||||
        if (line[linelen - 1] == '\n') {
 | 
			
		||||
            line[--linelen] = '\0';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const char *name = strtok(line, " ");
 | 
			
		||||
        const char *keystr = strtok(NULL, " ");
 | 
			
		||||
 | 
			
		||||
        if (name == NULL || keystr == NULL)
 | 
			
		||||
        if (name == NULL || keystr == NULL) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (strlen(keystr) != SERVER_KEY_SIZE * 2)
 | 
			
		||||
        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)
 | 
			
		||||
        if (res == -1) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ++Nameservers.lines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
 | 
			
		||||
    if (Nameservers.lines < 1)
 | 
			
		||||
    if (Nameservers.lines < 1) {
 | 
			
		||||
        return -3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -147,8 +159,9 @@ static int load_nameserver_list(const char *path)
 | 
			
		||||
 */
 | 
			
		||||
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)))
 | 
			
		||||
    if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH))) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char tmpaddr[MAX_STR_SIZE];
 | 
			
		||||
    char *tmpname = NULL;
 | 
			
		||||
@@ -158,8 +171,9 @@ static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *
 | 
			
		||||
    tmpname = strtok(tmpaddr, "@");
 | 
			
		||||
    tmpdom = strtok(NULL, "");
 | 
			
		||||
 | 
			
		||||
    if (tmpname == NULL || tmpdom == NULL)
 | 
			
		||||
    if (tmpname == NULL || tmpdom == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    str_to_lower(tmpdom);
 | 
			
		||||
    snprintf(namebuf, namebuf_sz, "%s", tmpname);
 | 
			
		||||
@@ -189,95 +203,35 @@ static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_s
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MAX_RECV_LOOKUP_DATA_SIZE 1024
 | 
			
		||||
 | 
			
		||||
/* Holds raw data received from name server */
 | 
			
		||||
struct Recv_Data {
 | 
			
		||||
    char data[MAX_RECV_LOOKUP_DATA_SIZE];
 | 
			
		||||
    size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer)
 | 
			
		||||
{
 | 
			
		||||
    struct Recv_Data *recv_data = (struct Recv_Data *) user_pointer;
 | 
			
		||||
    size_t real_size = size * nmemb;
 | 
			
		||||
 | 
			
		||||
    if (real_size >= MAX_RECV_LOOKUP_DATA_SIZE)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    memcpy(&recv_data->data, data, real_size);
 | 
			
		||||
    recv_data->size = real_size;
 | 
			
		||||
    recv_data->data[real_size] = '\0';
 | 
			
		||||
 | 
			
		||||
    return real_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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_Data *recv_data)
 | 
			
		||||
static int process_response(struct Recv_Curl_Data *recv_data)
 | 
			
		||||
{
 | 
			
		||||
    size_t prefix_size = strlen(ID_PREFIX);
 | 
			
		||||
 | 
			
		||||
    if (recv_data->size < TOX_ADDRESS_SIZE * 2 + prefix_size)
 | 
			
		||||
    if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char *IDstart = strstr(recv_data->data, ID_PREFIX);
 | 
			
		||||
 | 
			
		||||
    if (IDstart == NULL)
 | 
			
		||||
    if (IDstart == NULL) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size)
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Sets proxy info for given CURL handler.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success or if no proxy is set by the client.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
static int set_lookup_proxy(ToxWindow *self, 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) {
 | 
			
		||||
        lookup_error(self, "Unknown proxy error");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        lookup_error(self, "Failed to set proxy port (libcurl error %d)", ret);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        lookup_error(self, "Failed to set proxy type (libcurl error %d)", ret);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
 | 
			
		||||
 | 
			
		||||
    if (ret != CURLE_OK) {
 | 
			
		||||
        lookup_error(self, "Failed to set proxy (libcurl error %d)", ret);
 | 
			
		||||
    if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -286,6 +240,8 @@ static int set_lookup_proxy(ToxWindow *self, CURL *c_handle, const char *proxy_a
 | 
			
		||||
 | 
			
		||||
void *lookup_thread_func(void *data)
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(data);
 | 
			
		||||
 | 
			
		||||
    ToxWindow *self = t_data.self;
 | 
			
		||||
 | 
			
		||||
    char input_domain[MAX_STR_SIZE];
 | 
			
		||||
@@ -300,10 +256,11 @@ void *lookup_thread_func(void *data)
 | 
			
		||||
    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
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
@@ -315,26 +272,41 @@ void *lookup_thread_func(void *data)
 | 
			
		||||
        kill_lookup_thread();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct Recv_Data recv_data;
 | 
			
		||||
    memset(&recv_data, 0, sizeof(struct Recv_Data));
 | 
			
		||||
    struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data));
 | 
			
		||||
 | 
			
		||||
    if (recv_data == NULL) {
 | 
			
		||||
        lookup_error(self, "memory allocation error");
 | 
			
		||||
        kill_lookup_thread();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char post_data[MAX_STR_SIZE + 30];
 | 
			
		||||
 | 
			
		||||
    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, write_lookup_data);
 | 
			
		||||
    curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, (void *) &recv_data);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    if (set_lookup_proxy(self, c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type) == -1)
 | 
			
		||||
    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", proxy_ret);
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL);
 | 
			
		||||
 | 
			
		||||
@@ -372,7 +344,7 @@ void *lookup_thread_func(void *data)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (process_response(&recv_data) == -1) {
 | 
			
		||||
    if (process_response(recv_data) == -1) {
 | 
			
		||||
        lookup_error(self, "Bad response.");
 | 
			
		||||
        goto on_exit;
 | 
			
		||||
    }
 | 
			
		||||
@@ -382,6 +354,7 @@ void *lookup_thread_func(void *data)
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
on_exit:
 | 
			
		||||
    free(recv_data);
 | 
			
		||||
    curl_slist_free_all(headers);
 | 
			
		||||
    curl_easy_cleanup(c_handle);
 | 
			
		||||
    kill_lookup_thread();
 | 
			
		||||
@@ -392,12 +365,12 @@ on_exit:
 | 
			
		||||
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.");
 | 
			
		||||
        line_info_add(self, false, 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.");
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -409,22 +382,22 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr,
 | 
			
		||||
    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));
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
 | 
			
		||||
        clear_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");
 | 
			
		||||
        line_info_add(self, false, 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));
 | 
			
		||||
        clear_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");
 | 
			
		||||
        line_info_add(self, false, 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));
 | 
			
		||||
        clear_thread_data();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -437,9 +410,9 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr,
 | 
			
		||||
 * 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(void)
 | 
			
		||||
int name_lookup_init(int curl_init_status)
 | 
			
		||||
{
 | 
			
		||||
    if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
 | 
			
		||||
    if (curl_init_status != 0) {
 | 
			
		||||
        t_data.disabled = true;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
@@ -454,8 +427,3 @@ int name_lookup_init(void)
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void name_lookup_cleanup(void)
 | 
			
		||||
{
 | 
			
		||||
    curl_global_cleanup();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,7 @@
 | 
			
		||||
 * Returns 0 on success.
 | 
			
		||||
 * Returns -1 on failure.
 | 
			
		||||
 */
 | 
			
		||||
int name_lookup_init(void);
 | 
			
		||||
void name_lookup_cleanup(void);
 | 
			
		||||
int name_lookup_init(int curl_init_status);
 | 
			
		||||
 | 
			
		||||
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										488
									
								
								src/notify.c
									
									
									
									
									
								
							
							
						
						
									
										488
									
								
								src/notify.c
									
									
									
									
									
								
							@@ -20,51 +20,51 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "xtra.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "x11focus.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
 | 
			
		||||
#endif /* 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__ */
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
#include <AL/alut.h> /* freealut packet */
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
#endif /* defined(AUDIO) || defined(SOUND_NOTIFY) */
 | 
			
		||||
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
    #include <libnotify/notify.h>
 | 
			
		||||
#include <libnotify/notify.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define MAX_BOX_MSG_LEN 127
 | 
			
		||||
#define SOUNDS_SIZE 10
 | 
			
		||||
#define ACTIVE_NOTIFS_MAX 50
 | 
			
		||||
#define ACTIVE_NOTIFS_MAX 10
 | 
			
		||||
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
 | 
			
		||||
struct Control {
 | 
			
		||||
static struct Control {
 | 
			
		||||
    time_t cooldown;
 | 
			
		||||
    time_t notif_timeout;
 | 
			
		||||
 | 
			
		||||
@@ -75,25 +75,25 @@ 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};
 | 
			
		||||
 | 
			
		||||
struct _ActiveNotifications {
 | 
			
		||||
static struct _ActiveNotifications {
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    uint32_t source;
 | 
			
		||||
    uint32_t buffer;
 | 
			
		||||
    bool looping;
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
    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;
 | 
			
		||||
    time_t n_timeout;
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* BOX_NOTIFY */
 | 
			
		||||
} actives[ACTIVE_NOTIFS_MAX];
 | 
			
		||||
/**********************************************************************************/
 | 
			
		||||
/**********************************************************************************/
 | 
			
		||||
@@ -101,24 +101,40 @@ struct _ActiveNotifications {
 | 
			
		||||
/**********************************************************************************/
 | 
			
		||||
/**********************************************************************************/
 | 
			
		||||
 | 
			
		||||
static void clear_actives_index(size_t idx)
 | 
			
		||||
{
 | 
			
		||||
    if (actives[idx].id_indicator) {
 | 
			
		||||
        *actives[idx].id_indicator = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    actives[idx] = (struct _ActiveNotifications) {
 | 
			
		||||
        0
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* coloured tab notifications: primary notification type */
 | 
			
		||||
static void tab_notify(ToxWindow *self, uint64_t flags)
 | 
			
		||||
{
 | 
			
		||||
    if (self == NULL)
 | 
			
		||||
    if (self == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (flags & NT_WNDALERT_0)
 | 
			
		||||
    if (flags & NT_WNDALERT_0) {
 | 
			
		||||
        self->alert = WINDOW_ALERT_0;
 | 
			
		||||
    else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) )
 | 
			
		||||
    } else if ((flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0)) {
 | 
			
		||||
        self->alert = WINDOW_ALERT_1;
 | 
			
		||||
    else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) )
 | 
			
		||||
    } else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) {
 | 
			
		||||
        self->alert = WINDOW_ALERT_2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ++self->pending_messages;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool notifications_are_disabled(uint64_t flags)
 | 
			
		||||
{
 | 
			
		||||
    if (user_settings->alerts != ALERTS_ENABLED)
 | 
			
		||||
    if (user_settings->alerts != ALERTS_ENABLED) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
 | 
			
		||||
#ifdef X11
 | 
			
		||||
@@ -128,14 +144,14 @@ static bool notifications_are_disabled(uint64_t flags)
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void control_lock()
 | 
			
		||||
static void control_lock(void)
 | 
			
		||||
{
 | 
			
		||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
 | 
			
		||||
    pthread_mutex_lock(Control.poll_mutex);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void control_unlock()
 | 
			
		||||
static void control_unlock(void)
 | 
			
		||||
{
 | 
			
		||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
 | 
			
		||||
    pthread_mutex_unlock(Control.poll_mutex);
 | 
			
		||||
@@ -157,21 +173,25 @@ static bool device_opened = false;
 | 
			
		||||
time_t last_opened_update = 0;
 | 
			
		||||
 | 
			
		||||
/* Opens primary device. Returns true on succe*/
 | 
			
		||||
void m_open_device()
 | 
			
		||||
void m_open_device(void)
 | 
			
		||||
{
 | 
			
		||||
    last_opened_update = get_unix_time();
 | 
			
		||||
 | 
			
		||||
    if (device_opened) return;
 | 
			
		||||
    if (device_opened) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Blah error check */
 | 
			
		||||
    open_primary_device(output, &Control.device_idx, 48000, 20, 1);
 | 
			
		||||
    open_output_device(&Control.device_idx, 48000, 20, 1);
 | 
			
		||||
 | 
			
		||||
    device_opened = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void m_close_device()
 | 
			
		||||
void m_close_device(void)
 | 
			
		||||
{
 | 
			
		||||
    if (!device_opened) return;
 | 
			
		||||
    if (!device_opened) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close_device(output, Control.device_idx);
 | 
			
		||||
 | 
			
		||||
@@ -179,32 +199,37 @@ void m_close_device()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Terminate all sounds but wait for them to finish first */
 | 
			
		||||
void graceful_clear()
 | 
			
		||||
void graceful_clear(void)
 | 
			
		||||
{
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
    while (1) {
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
 | 
			
		||||
        for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
 | 
			
		||||
            if (actives[i].active) {
 | 
			
		||||
            #ifdef BOX_NOTIFY
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
 | 
			
		||||
                if (actives[i].box) {
 | 
			
		||||
                    GError* ignore;
 | 
			
		||||
                    GError *ignore;
 | 
			
		||||
                    notify_notification_close(actives[i].box, &ignore);
 | 
			
		||||
                    actives[i].box = NULL;
 | 
			
		||||
                }
 | 
			
		||||
            #endif
 | 
			
		||||
 | 
			
		||||
                if(actives[i].id_indicator)
 | 
			
		||||
#endif /* BOX_NOTIFY */
 | 
			
		||||
 | 
			
		||||
                if (actives[i].id_indicator) {
 | 
			
		||||
                    *actives[i].id_indicator = -1;    /* reset indicator value */
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if ( actives[i].looping ) {
 | 
			
		||||
                if (actives[i].looping) {
 | 
			
		||||
                    stop_sound(i);
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (!is_playing(actives[i].source))
 | 
			
		||||
                        memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
                    else break;
 | 
			
		||||
                    if (!is_playing(actives[i].source)) {
 | 
			
		||||
                        clear_actives_index(i);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -215,17 +240,17 @@ void graceful_clear()
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        usleep(1000);
 | 
			
		||||
        sleep_thread(1000L);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    control_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* do_playing(void* _p)
 | 
			
		||||
void *do_playing(void *_p)
 | 
			
		||||
{
 | 
			
		||||
    (void)_p;
 | 
			
		||||
    UNUSED_VAR(_p);
 | 
			
		||||
 | 
			
		||||
    while(true) {
 | 
			
		||||
    while (true) {
 | 
			
		||||
        control_lock();
 | 
			
		||||
 | 
			
		||||
        if (!Control.poll_active) {
 | 
			
		||||
@@ -234,46 +259,54 @@ void* do_playing(void* _p)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool has_looping = false;
 | 
			
		||||
        bool test_active_notify = false;
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
 | 
			
		||||
        for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
 | 
			
		||||
 | 
			
		||||
            if (actives[i].looping) has_looping = true;
 | 
			
		||||
            if (actives[i].looping) {
 | 
			
		||||
                has_looping = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (actives[i].active && !actives[i].looping
 | 
			
		||||
                #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)) {
 | 
			
		||||
                    /* Close */
 | 
			
		||||
                    alSourceStop(actives[i].source);
 | 
			
		||||
                    alDeleteSources(1, &actives[i].source);
 | 
			
		||||
                    alDeleteBuffers(1, &actives[i].buffer);
 | 
			
		||||
                    memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
                    clear_actives_index(i);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        #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)) {
 | 
			
		||||
                    /* stop source if not looping or playing, just terminate box */
 | 
			
		||||
                    alSourceStop(actives[i].source);
 | 
			
		||||
                    alDeleteSources(1, &actives[i].source);
 | 
			
		||||
                    alDeleteBuffers(1, &actives[i].buffer);
 | 
			
		||||
                    memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
                    clear_actives_index(i);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        #endif
 | 
			
		||||
 | 
			
		||||
#endif /* BOX_NOTIFY */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
 | 
			
		||||
@@ -281,19 +314,23 @@ void* do_playing(void* _p)
 | 
			
		||||
                (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
 | 
			
		||||
            m_close_device();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        has_looping = false;
 | 
			
		||||
 | 
			
		||||
        control_unlock();
 | 
			
		||||
        usleep(10000);
 | 
			
		||||
        sleep_thread(10000L);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 ) {
 | 
			
		||||
 | 
			
		||||
    for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; ++i);
 | 
			
		||||
 | 
			
		||||
    if (i == ACTIVE_NOTIFS_MAX) {
 | 
			
		||||
        return -1; /* Full */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -308,11 +345,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;
 | 
			
		||||
    UNUSED_VAR(_p);
 | 
			
		||||
 | 
			
		||||
    while(true) {
 | 
			
		||||
    while (true) {
 | 
			
		||||
        control_lock();
 | 
			
		||||
 | 
			
		||||
        if (!Control.poll_active) {
 | 
			
		||||
@@ -320,47 +357,64 @@ void* do_playing(void* _p)
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
 | 
			
		||||
            if (actives[i].box && time(NULL) >= actives[i].n_timeout)
 | 
			
		||||
            {
 | 
			
		||||
                GError* ignore;
 | 
			
		||||
        for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
 | 
			
		||||
            if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
 | 
			
		||||
                GError *ignore;
 | 
			
		||||
                notify_notification_close(actives[i].box, &ignore);
 | 
			
		||||
                actives[i].box = NULL;
 | 
			
		||||
                if(actives[i].id_indicator)
 | 
			
		||||
                    *actives[i].id_indicator = -1;    /* reset indicator value */
 | 
			
		||||
                clear_actives_index(i);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        control_unlock();
 | 
			
		||||
        usleep(10000);
 | 
			
		||||
        sleep_thread(10000L);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void graceful_clear()
 | 
			
		||||
void graceful_clear(void)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
 | 
			
		||||
    for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
 | 
			
		||||
        if (actives[i].box) {
 | 
			
		||||
            GError* ignore;
 | 
			
		||||
            GError *ignore;
 | 
			
		||||
            notify_notification_close(actives[i].box, &ignore);
 | 
			
		||||
            actives[i].box = NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (actives[i].id_indicator)
 | 
			
		||||
            *actives[i].id_indicator = -1;    /* reset indicator value */
 | 
			
		||||
 | 
			
		||||
        memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
        clear_actives_index(i);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    control_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
 | 
			
		||||
/* Kills all notifications for `id`. This must be called before freeing a ToxWindow. */
 | 
			
		||||
void kill_notifs(int id)
 | 
			
		||||
{
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
 | 
			
		||||
        if (!actives[i].id_indicator) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (*actives[i].id_indicator == id) {
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
 | 
			
		||||
            if (actives[i].box) {
 | 
			
		||||
                GError *ignore;
 | 
			
		||||
                notify_notification_close(actives[i].box, &ignore);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
#endif // BOX_NOTIFY
 | 
			
		||||
            clear_actives_index(i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    control_unlock();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**********************************************************************************/
 | 
			
		||||
/**********************************************************************************/
 | 
			
		||||
@@ -378,19 +432,21 @@ 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)
 | 
			
		||||
 | 
			
		||||
    if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Control.poll_active = 1;
 | 
			
		||||
    pthread_t thread;
 | 
			
		||||
 | 
			
		||||
    if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
 | 
			
		||||
    if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0) {
 | 
			
		||||
        pthread_mutex_destroy(Control.poll_mutex);
 | 
			
		||||
        Control.poll_active = 0;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */
 | 
			
		||||
    Control.cooldown = time(NULL) + login_cooldown;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -401,12 +457,12 @@ int init_notify(int login_cooldown, int notification_timeout)
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void terminate_notify()
 | 
			
		||||
void terminate_notify(void)
 | 
			
		||||
{
 | 
			
		||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
    if ( !Control.poll_active ) {
 | 
			
		||||
    if (!Control.poll_active) {
 | 
			
		||||
        control_unlock();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -415,11 +471,15 @@ void terminate_notify()
 | 
			
		||||
    control_unlock();
 | 
			
		||||
 | 
			
		||||
    graceful_clear();
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
 | 
			
		||||
 | 
			
		||||
    for (; i < SOUNDS_SIZE; ++i) {
 | 
			
		||||
        free(Control.sounds[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alutExit();
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
 | 
			
		||||
@@ -429,9 +489,11 @@ 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;
 | 
			
		||||
    if (sound == silent) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(Control.sounds[sound]);
 | 
			
		||||
 | 
			
		||||
@@ -457,10 +519,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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -471,10 +534,14 @@ int play_notify_sound(Notification notif, uint64_t flags)
 | 
			
		||||
{
 | 
			
		||||
    int rc = -1;
 | 
			
		||||
 | 
			
		||||
    if (flags & NT_BEEP) beep();
 | 
			
		||||
    else if (notif != silent) {
 | 
			
		||||
        if ( !Control.poll_active || !Control.sounds[notif] )
 | 
			
		||||
    if (flags & NT_BEEP) {
 | 
			
		||||
        beep();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (notif != silent) {
 | 
			
		||||
        if (!Control.poll_active || !Control.sounds[notif]) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0);
 | 
			
		||||
    }
 | 
			
		||||
@@ -482,73 +549,72 @@ int play_notify_sound(Notification notif, uint64_t flags)
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void stop_sound(int id)
 | 
			
		||||
{
 | 
			
		||||
    if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) {
 | 
			
		||||
    if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active) {
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
#endif /* BOX_NOTIFY */
 | 
			
		||||
 | 
			
		||||
        // alSourcei(actives[id].source, AL_LOOPING, false);
 | 
			
		||||
        alSourceStop(actives[id].source);
 | 
			
		||||
        alDeleteSources(1, &actives[id].source);
 | 
			
		||||
        alDeleteBuffers(1,&actives[id].buffer);
 | 
			
		||||
        memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
 | 
			
		||||
        alDeleteBuffers(1, &actives[id].buffer);
 | 
			
		||||
        clear_actives_index(id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
 | 
			
		||||
static int m_play_sound(Notification notif, uint64_t flags)
 | 
			
		||||
{
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    return play_notify_sound(notif, flags);
 | 
			
		||||
#else
 | 
			
		||||
    if (notif != silent)
 | 
			
		||||
 | 
			
		||||
    if (notif != silent) {
 | 
			
		||||
        beep();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
void m_notify_action(NotifyNotification *box, char *action, void* data)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
 | 
			
		||||
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator)
 | 
			
		||||
{
 | 
			
		||||
    tab_notify(self, flags);
 | 
			
		||||
 | 
			
		||||
    if (notifications_are_disabled(flags))
 | 
			
		||||
    if (notifications_are_disabled(flags)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int id = -1;
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
    if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY))
 | 
			
		||||
    if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY)) {
 | 
			
		||||
        id = m_play_sound(notif, flags);
 | 
			
		||||
    else if (flags & NT_ALWAYS)
 | 
			
		||||
    } else if (flags & NT_ALWAYS) {
 | 
			
		||||
        id = m_play_sound(notif, flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY)
 | 
			
		||||
 | 
			
		||||
    if (id == -1) {
 | 
			
		||||
        for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
 | 
			
		||||
        if ( id == ACTIVE_NOTIFS_MAX ) {
 | 
			
		||||
 | 
			
		||||
        if (id == ACTIVE_NOTIFS_MAX) {
 | 
			
		||||
            control_unlock();
 | 
			
		||||
            return -1; /* Full */
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) */
 | 
			
		||||
 | 
			
		||||
    if ( id_indicator && id != -1 ) {
 | 
			
		||||
    if (id_indicator && id != -1) {
 | 
			
		||||
        actives[id].id_indicator = id_indicator;
 | 
			
		||||
        *id_indicator = id;
 | 
			
		||||
    }
 | 
			
		||||
@@ -558,14 +624,18 @@ 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);
 | 
			
		||||
 | 
			
		||||
    if (notifications_are_disabled(flags))
 | 
			
		||||
    if (notifications_are_disabled(flags)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (id < 0 || id >= ACTIVE_NOTIFS_MAX) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
@@ -578,8 +648,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,14 +662,17 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
 | 
			
		||||
 | 
			
		||||
    return id;
 | 
			
		||||
#else
 | 
			
		||||
    if (notif != silent)
 | 
			
		||||
 | 
			
		||||
    if (notif != silent) {
 | 
			
		||||
        beep();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
#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,32 +686,46 @@ 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 ) {
 | 
			
		||||
 | 
			
		||||
        if (id == ACTIVE_NOTIFS_MAX) {
 | 
			
		||||
            control_unlock();
 | 
			
		||||
            return -1; /* Full */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        actives[id].active = 1;
 | 
			
		||||
        actives[id].id_indicator = id_indicator;
 | 
			
		||||
        if (id_indicator) *id_indicator = id;
 | 
			
		||||
 | 
			
		||||
        if (id_indicator) {
 | 
			
		||||
            *id_indicator = id;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
    if (id == -1)
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end (__ARGS__);
 | 
			
		||||
    if (strlen(title) > 23) {
 | 
			
		||||
        strcpy(actives[id].title + 20, "...");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
 | 
			
		||||
    va_list __ARGS__;
 | 
			
		||||
    va_start(__ARGS__, format);
 | 
			
		||||
    vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end(__ARGS__);
 | 
			
		||||
 | 
			
		||||
    if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) {
 | 
			
		||||
        strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
 | 
			
		||||
    actives[id].size++;
 | 
			
		||||
@@ -657,7 +743,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);
 | 
			
		||||
@@ -666,8 +752,9 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
 | 
			
		||||
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
 | 
			
		||||
    if (sound_notify2(self, notif, flags, id) == -1)
 | 
			
		||||
    if (sound_notify2(self, notif, flags, id) == -1) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
@@ -676,51 +763,55 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    va_list __ARGS__; va_start (__ARGS__, format);
 | 
			
		||||
    vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end (__ARGS__);
 | 
			
		||||
    va_list __ARGS__;
 | 
			
		||||
    va_start(__ARGS__, format);
 | 
			
		||||
    vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end(__ARGS__);
 | 
			
		||||
 | 
			
		||||
    if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
 | 
			
		||||
    if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) {
 | 
			
		||||
        strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    actives[id].size++;
 | 
			
		||||
    actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
 | 
			
		||||
 | 
			
		||||
    char formated[128 * 129] = {'\0'};
 | 
			
		||||
    char *formatted = calloc(1, sizeof(char) * ((MAX_BOX_MSG_LEN + 1) * (MAX_BOX_MSG_LEN + 2)));
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (; i <actives[id].size; i ++) {
 | 
			
		||||
        strcat(formated, actives[id].messages[i]);
 | 
			
		||||
        strcat(formated, "\n");
 | 
			
		||||
    for (size_t i = 0; i < actives[id].size; ++i) {
 | 
			
		||||
        strcat(formatted, actives[id].messages[i]);
 | 
			
		||||
        strcat(formatted, "\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    formated[strlen(formated) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
    notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
 | 
			
		||||
    notify_notification_update(actives[id].box, actives[id].title, formatted, NULL);
 | 
			
		||||
    notify_notification_show(actives[id].box, NULL);
 | 
			
		||||
 | 
			
		||||
    free(formatted);
 | 
			
		||||
 | 
			
		||||
    control_unlock();
 | 
			
		||||
 | 
			
		||||
    return id;
 | 
			
		||||
#else
 | 
			
		||||
    return sound_notify2(self, notif, flags, id);
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* BOX_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
    if (notifications_are_disabled(flags))
 | 
			
		||||
    if (notifications_are_disabled(flags)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
    int id;
 | 
			
		||||
 | 
			
		||||
    for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
 | 
			
		||||
    if ( id == ACTIVE_NOTIFS_MAX ) {
 | 
			
		||||
 | 
			
		||||
    if (id == ACTIVE_NOTIFS_MAX) {
 | 
			
		||||
        control_unlock();
 | 
			
		||||
        return -1; /* Full */
 | 
			
		||||
    }
 | 
			
		||||
@@ -731,14 +822,19 @@ 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);
 | 
			
		||||
    vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end (__ARGS__);
 | 
			
		||||
    if (strlen(title) > 23) {
 | 
			
		||||
        strcpy(actives[id].title + 20, "...");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
 | 
			
		||||
    va_list __ARGS__;
 | 
			
		||||
    va_start(__ARGS__, format);
 | 
			
		||||
    vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end(__ARGS__);
 | 
			
		||||
 | 
			
		||||
    if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) {
 | 
			
		||||
        strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    actives[id].active = 1;
 | 
			
		||||
    actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
 | 
			
		||||
@@ -754,52 +850,54 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
 | 
			
		||||
    return id;
 | 
			
		||||
#else
 | 
			
		||||
    return -1;
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* BOX_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
    if (notifications_are_disabled(flags))
 | 
			
		||||
    if (notifications_are_disabled(flags)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef BOX_NOTIFY
 | 
			
		||||
    control_lock();
 | 
			
		||||
 | 
			
		||||
    if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) {
 | 
			
		||||
    if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) {
 | 
			
		||||
        control_unlock();
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    va_list __ARGS__; va_start (__ARGS__, format);
 | 
			
		||||
    vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end (__ARGS__);
 | 
			
		||||
    va_list __ARGS__;
 | 
			
		||||
    va_start(__ARGS__, format);
 | 
			
		||||
    vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
 | 
			
		||||
    va_end(__ARGS__);
 | 
			
		||||
 | 
			
		||||
    if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
 | 
			
		||||
    if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) {
 | 
			
		||||
        strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    actives[id].size ++;
 | 
			
		||||
    actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
 | 
			
		||||
 | 
			
		||||
    char formated[128 * 129] = {'\0'};
 | 
			
		||||
    char *formatted = calloc(1, sizeof(char) * ((MAX_BOX_MSG_LEN + 1) * (MAX_BOX_MSG_LEN + 2)));
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (; i <actives[id].size; i ++) {
 | 
			
		||||
        strcat(formated, actives[id].messages[i]);
 | 
			
		||||
        strcat(formated, "\n");
 | 
			
		||||
    for (size_t i = 0; i < actives[id].size; ++i) {
 | 
			
		||||
        strcat(formatted, actives[id].messages[i]);
 | 
			
		||||
        strcat(formatted, "\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    formated[strlen(formated) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
    notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
 | 
			
		||||
    notify_notification_update(actives[id].box, actives[id].title, formatted, NULL);
 | 
			
		||||
    notify_notification_show(actives[id].box, NULL);
 | 
			
		||||
 | 
			
		||||
    free(formatted);
 | 
			
		||||
 | 
			
		||||
    control_unlock();
 | 
			
		||||
 | 
			
		||||
    return id;
 | 
			
		||||
#else
 | 
			
		||||
    return -1;
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* BOX_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								src/notify.h
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								src/notify.h
									
									
									
									
									
								
							@@ -23,11 +23,10 @@
 | 
			
		||||
#ifndef NOTIFY_H
 | 
			
		||||
#define NOTIFY_H
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
typedef enum _Notification
 | 
			
		||||
{
 | 
			
		||||
typedef enum _Notification {
 | 
			
		||||
    silent = -1,
 | 
			
		||||
    notif_error,
 | 
			
		||||
    self_log_in,
 | 
			
		||||
@@ -61,20 +60,24 @@ typedef enum _Flags {
 | 
			
		||||
} Flags;
 | 
			
		||||
 | 
			
		||||
int init_notify(int login_cooldown, int notification_timeout);
 | 
			
		||||
void terminate_notify();
 | 
			
		||||
void terminate_notify(void);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
/* Kills all notifications for `id`. This must be called before freeing a ToxWindow. */
 | 
			
		||||
void kill_notifs(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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -36,13 +36,16 @@
 | 
			
		||||
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;
 | 
			
		||||
@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();
 | 
			
		||||
void osx_video_release(void);
 | 
			
		||||
/* Start device */
 | 
			
		||||
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height);
 | 
			
		||||
/* Stop device */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								src/osx_video.m
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								src/osx_video.m
									
									
									
									
									
								
							@@ -44,19 +44,19 @@
 | 
			
		||||
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;
 | 
			
		||||
    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;
 | 
			
		||||
    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;
 | 
			
		||||
    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)
 | 
			
		||||
@@ -65,9 +65,10 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
    uint8_t *p;
 | 
			
		||||
    uint8_t r, g, b;
 | 
			
		||||
 | 
			
		||||
    for(y = 0; y != height; y += 2) {
 | 
			
		||||
    for (y = 0; y != height; y += 2) {
 | 
			
		||||
        p = rgb;
 | 
			
		||||
        for(x = 0; x != width; x++) {
 | 
			
		||||
 | 
			
		||||
        for (x = 0; x != width; x++) {
 | 
			
		||||
            b = *rgb++;
 | 
			
		||||
            g = *rgb++;
 | 
			
		||||
            r = *rgb++;
 | 
			
		||||
@@ -76,7 +77,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
            *plane_y++ = rgb_to_y(r, g, b);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for(x = 0; x != width / 2; x++) {
 | 
			
		||||
        for (x = 0; x != width / 2; x++) {
 | 
			
		||||
            b = *rgb++;
 | 
			
		||||
            g = *rgb++;
 | 
			
		||||
            r = *rgb++;
 | 
			
		||||
@@ -91,9 +92,12 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
 | 
			
		||||
            *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++;
 | 
			
		||||
            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);
 | 
			
		||||
@@ -122,28 +126,35 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
    BOOL _shouldMangleDimensions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (instancetype)initWithDeviceNames: (char **)device_names AmtDevices: (int *)size {
 | 
			
		||||
- (instancetype)initWithDeviceNames:
 | 
			
		||||
    (char **)device_names AmtDevices:
 | 
			
		||||
    (int *)size
 | 
			
		||||
{
 | 
			
		||||
    _session = [[AVCaptureSession alloc] init];
 | 
			
		||||
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < [devices count]; ++i) {
 | 
			
		||||
        AVCaptureDevice *device = [devices objectAtIndex: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]);
 | 
			
		||||
        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 )
 | 
			
		||||
    if (i <= 0) {
 | 
			
		||||
        return nil;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *size = i;
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)dealloc {
 | 
			
		||||
- (void)dealloc
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_destroy(&_frameLock);
 | 
			
		||||
    [_session release];
 | 
			
		||||
    [_linkerVideo release];
 | 
			
		||||
@@ -151,22 +162,26 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
    [super dealloc];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (int)openVideoDeviceIndex: (uint32_t)device_idx Width: (uint16_t *)width Height: (uint16_t *)height {
 | 
			
		||||
- (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];
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
 | 
			
		||||
    AVCaptureDevice *device = [devices objectAtIndex: device_idx];
 | 
			
		||||
    NSError *error = NULL;
 | 
			
		||||
    AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
 | 
			
		||||
    AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
 | 
			
		||||
 | 
			
		||||
    if ( error != NULL ) {
 | 
			
		||||
    if (error != NULL) {
 | 
			
		||||
        [input release];
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [_session beginConfiguration];
 | 
			
		||||
    [_session addInput:input];
 | 
			
		||||
    [_session addInput: input];
 | 
			
		||||
    //_session.sessionPreset = AVCaptureSessionPreset640x480;
 | 
			
		||||
    //*width = 640;
 | 
			
		||||
    //*height = 480;
 | 
			
		||||
@@ -176,9 +191,10 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
    [device release];
 | 
			
		||||
 | 
			
		||||
    /* Obtain device resolution */
 | 
			
		||||
    AVCaptureInputPort *port = [input.ports objectAtIndex:0];
 | 
			
		||||
    AVCaptureInputPort *port = [input.ports objectAtIndex: 0];
 | 
			
		||||
    CMFormatDescriptionRef format_description = port.formatDescription;
 | 
			
		||||
    if ( format_description ) {
 | 
			
		||||
 | 
			
		||||
    if (format_description) {
 | 
			
		||||
        CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
 | 
			
		||||
        *width = dimensions.width;
 | 
			
		||||
        *height = dimensions.height;
 | 
			
		||||
@@ -188,36 +204,47 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
 | 
			
		||||
    [_linkerVideo setSampleBufferDelegate:self queue:_processingQueue];
 | 
			
		||||
    [_linkerVideo setSampleBufferDelegate: self queue: _processingQueue];
 | 
			
		||||
 | 
			
		||||
    // TODO possibly get a better pixel format
 | 
			
		||||
    if (_shouldMangleDimensions) {
 | 
			
		||||
        [_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
 | 
			
		||||
        [_linkerVideo setVideoSettings: @ {
 | 
			
		||||
             (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
 | 
			
		||||
             (id)kCVPixelBufferWidthKey: @640,
 | 
			
		||||
                                         (id)kCVPixelBufferHeightKey: @480}];
 | 
			
		||||
             (id)kCVPixelBufferHeightKey: @480
 | 
			
		||||
                     }];
 | 
			
		||||
    } else {
 | 
			
		||||
        [_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
 | 
			
		||||
        [_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
 | 
			
		||||
    }
 | 
			
		||||
    [_session addOutput:_linkerVideo];
 | 
			
		||||
 | 
			
		||||
    [_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];
 | 
			
		||||
- (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];
 | 
			
		||||
    AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
 | 
			
		||||
    [_session stopRunning];
 | 
			
		||||
    [_session removeOutput:_linkerVideo];
 | 
			
		||||
    [_session removeInput:input];
 | 
			
		||||
    [_session removeOutput: _linkerVideo];
 | 
			
		||||
    [_session removeInput: input];
 | 
			
		||||
    [_linkerVideo release];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
 | 
			
		||||
- (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 {
 | 
			
		||||
@@ -228,10 +255,17 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
        // 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 {
 | 
			
		||||
- (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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -240,6 +274,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
    CFRetain(_currentFrame);
 | 
			
		||||
 | 
			
		||||
    CFTypeID imageType = CFGetTypeID(_currentFrame);
 | 
			
		||||
 | 
			
		||||
    if (imageType == CVPixelBufferGetTypeID()) {
 | 
			
		||||
        // TODO maybe handle other formats
 | 
			
		||||
        bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height);
 | 
			
		||||
@@ -263,14 +298,15 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
 | 
			
		||||
/*
 | 
			
		||||
 * C-interface for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
static OSXVideo* _OSXVideo = nil;
 | 
			
		||||
static OSXVideo *_OSXVideo = nil;
 | 
			
		||||
 | 
			
		||||
int osx_video_init(char **device_names, int *size)
 | 
			
		||||
{
 | 
			
		||||
    _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size];
 | 
			
		||||
 | 
			
		||||
    if ( _OSXVideo == nil )
 | 
			
		||||
    if (_OSXVideo == nil) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -283,8 +319,9 @@ void osx_video_release()
 | 
			
		||||
 | 
			
		||||
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height)
 | 
			
		||||
{
 | 
			
		||||
    if ( _OSXVideo == nil )
 | 
			
		||||
    if (_OSXVideo == nil) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height];
 | 
			
		||||
}
 | 
			
		||||
@@ -296,8 +333,9 @@ void osx_video_close_device(uint32_t 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 )
 | 
			
		||||
    if (_OSXVideo == nil) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										470
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										470
									
								
								src/prompt.c
									
									
									
									
									
								
							@@ -28,20 +28,20 @@
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <wchar.h>
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "prompt.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "toxic_strings.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "input.h"
 | 
			
		||||
#include "help.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "autocomplete.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "friendlist.h"
 | 
			
		||||
#include "help.h"
 | 
			
		||||
#include "input.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "prompt.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "toxic_strings.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
extern ToxWindow *prompt;
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
@@ -49,49 +49,50 @@ extern struct Winthread Winthread;
 | 
			
		||||
 | 
			
		||||
extern FriendsList Friends;
 | 
			
		||||
FriendRequests FrndRequests;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 22
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 20
 | 
			
		||||
#else
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 18
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Array of global command names used for tab completion. */
 | 
			
		||||
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
 | 
			
		||||
    { "/accept"     },
 | 
			
		||||
    { "/add"        },
 | 
			
		||||
    { "/avatar"     },
 | 
			
		||||
    { "/clear"      },
 | 
			
		||||
    { "/connect"    },
 | 
			
		||||
    { "/decline"    },
 | 
			
		||||
    { "/exit"       },
 | 
			
		||||
    { "/group"      },
 | 
			
		||||
    { "/help"       },
 | 
			
		||||
    { "/log"        },
 | 
			
		||||
    { "/myid"       },
 | 
			
		||||
    { "/myqr"       },
 | 
			
		||||
    { "/nick"       },
 | 
			
		||||
    { "/note"       },
 | 
			
		||||
    { "/nospam"     },
 | 
			
		||||
    { "/quit"       },
 | 
			
		||||
    { "/requests"   },
 | 
			
		||||
    { "/status"     },
 | 
			
		||||
static const char *glob_cmd_list[] = {
 | 
			
		||||
    "/accept",
 | 
			
		||||
    "/add",
 | 
			
		||||
    "/avatar",
 | 
			
		||||
    "/clear",
 | 
			
		||||
    "/connect",
 | 
			
		||||
    "/decline",
 | 
			
		||||
    "/exit",
 | 
			
		||||
    "/conference",
 | 
			
		||||
    "/help",
 | 
			
		||||
    "/log",
 | 
			
		||||
    "/myid",
 | 
			
		||||
#ifdef QRCODE
 | 
			
		||||
    "/myqr",
 | 
			
		||||
#endif /* QRCODE */
 | 
			
		||||
    "/nick",
 | 
			
		||||
    "/note",
 | 
			
		||||
    "/nospam",
 | 
			
		||||
    "/quit",
 | 
			
		||||
    "/requests",
 | 
			
		||||
    "/status",
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
    { "/lsdev"       },
 | 
			
		||||
    { "/sdev"        },
 | 
			
		||||
    "/lsdev",
 | 
			
		||||
    "/sdev",
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
 | 
			
		||||
    { "/lsvdev"      },
 | 
			
		||||
    { "/svdev"       },
 | 
			
		||||
    "/lsvdev",
 | 
			
		||||
    "/svdev",
 | 
			
		||||
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
 | 
			
		||||
    "/run",
 | 
			
		||||
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void kill_prompt_window(ToxWindow *self)
 | 
			
		||||
@@ -115,8 +116,10 @@ void kill_prompt_window(ToxWindow *self)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* callback: Updates own connection status in prompt statusbar */
 | 
			
		||||
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata)
 | 
			
		||||
void on_self_connection_status(Tox *m, Tox_Connection connection_status, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(m);
 | 
			
		||||
    UNUSED_VAR(userdata);
 | 
			
		||||
    StatusBar *statusbar = prompt->stb;
 | 
			
		||||
    statusbar->connection = connection_status;
 | 
			
		||||
}
 | 
			
		||||
@@ -137,26 +140,35 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms
 | 
			
		||||
    size_t len = strlen(statusbar->statusmsg);
 | 
			
		||||
    statusbar->statusmsg_len = len;
 | 
			
		||||
 | 
			
		||||
    TOX_ERR_SET_INFO err;
 | 
			
		||||
    tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err);
 | 
			
		||||
    Tox_Err_Set_Info err;
 | 
			
		||||
    tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err);
 | 
			
		||||
 | 
			
		||||
    if (err != TOX_ERR_SET_INFO_OK)
 | 
			
		||||
        line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
 | 
			
		||||
    if (err != TOX_ERR_SET_INFO_OK) {
 | 
			
		||||
        line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Updates own status in prompt statusbar */
 | 
			
		||||
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status)
 | 
			
		||||
void prompt_update_status(ToxWindow *prompt, Tox_User_Status status)
 | 
			
		||||
{
 | 
			
		||||
    StatusBar *statusbar = prompt->stb;
 | 
			
		||||
    statusbar->status = status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns our own connection status */
 | 
			
		||||
Tox_Connection prompt_selfConnectionStatus(void)
 | 
			
		||||
{
 | 
			
		||||
    StatusBar *statusbar = prompt->stb;
 | 
			
		||||
    return statusbar->connection;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Adds friend request to pending friend requests.
 | 
			
		||||
   Returns request number on success, -1 if queue is full. */
 | 
			
		||||
static int add_friend_request(const char *public_key, const char *data)
 | 
			
		||||
{
 | 
			
		||||
    if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS)
 | 
			
		||||
    if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
@@ -166,8 +178,9 @@ static int add_friend_request(const char *public_key, const char *data)
 | 
			
		||||
            memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE);
 | 
			
		||||
            snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data);
 | 
			
		||||
 | 
			
		||||
            if (i == FrndRequests.max_idx)
 | 
			
		||||
            if (i == FrndRequests.max_idx) {
 | 
			
		||||
                ++FrndRequests.max_idx;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ++FrndRequests.num_requests;
 | 
			
		||||
 | 
			
		||||
@@ -178,7 +191,10 @@ static int add_friend_request(const char *public_key, const char *data)
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
/*
 | 
			
		||||
 * Return true if input is recognized by handler
 | 
			
		||||
 */
 | 
			
		||||
static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
@@ -186,40 +202,64 @@ 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 || y2 <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
    UNUSED_VAR(y);
 | 
			
		||||
 | 
			
		||||
    if (x2 <= 0 || y2 <= 0) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ctx->pastemode && key == '\r') {
 | 
			
		||||
        key = '\n';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* ignore non-menu related input if active */
 | 
			
		||||
    if (self->help->active) {
 | 
			
		||||
        help_onKey(self, key);
 | 
			
		||||
        return;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ltr) {    /* char is printable */
 | 
			
		||||
        input_new_char(self, key, x, y, x2, y2);
 | 
			
		||||
        return;
 | 
			
		||||
    if (ltr || key == '\n') {    /* char is printable */
 | 
			
		||||
        input_new_char(self, key, x, x2);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (line_info_onKey(self, key))
 | 
			
		||||
        return;
 | 
			
		||||
    if (line_info_onKey(self, key)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    input_handle(self, key, x, y, x2, y2);
 | 
			
		||||
    if (input_handle(self, key, x, x2)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int input_ret = false;
 | 
			
		||||
 | 
			
		||||
    if (key == '\t') {    /* TAB key: auto-completes command */
 | 
			
		||||
        input_ret = true;
 | 
			
		||||
 | 
			
		||||
        if (ctx->len > 1 && ctx->line[0] == '/') {
 | 
			
		||||
            int diff = -1;
 | 
			
		||||
 | 
			
		||||
            if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0)
 | 
			
		||||
            if (wcsncmp(ctx->line, L"/avatar ", wcslen(L"/avatar ")) == 0) {
 | 
			
		||||
                diff = dir_match(self, m, ctx->line, L"/avatar");
 | 
			
		||||
            else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0){
 | 
			
		||||
                const char status_cmd_list[3][8] = {
 | 
			
		||||
                  {"online"},
 | 
			
		||||
                  {"away"},
 | 
			
		||||
                  {"busy"},
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
            else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) {
 | 
			
		||||
                diff = dir_match(self, m, ctx->line, L"/run");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
            else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
 | 
			
		||||
                const char *status_cmd_list[] = {
 | 
			
		||||
                    "online",
 | 
			
		||||
                    "away",
 | 
			
		||||
                    "busy",
 | 
			
		||||
                };
 | 
			
		||||
                diff = complete_line(self, status_cmd_list, 3, 8);
 | 
			
		||||
            } else
 | 
			
		||||
                diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
 | 
			
		||||
                diff = complete_line(self, status_cmd_list, sizeof(status_cmd_list) / sizeof(char *));
 | 
			
		||||
            } else {
 | 
			
		||||
                diff = complete_line(self, glob_cmd_list, sizeof(glob_cmd_list) / sizeof(char *));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (diff != -1) {
 | 
			
		||||
                if (x + diff > x2 - 1) {
 | 
			
		||||
@@ -232,33 +272,42 @@ 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') {
 | 
			
		||||
        input_ret = true;
 | 
			
		||||
 | 
			
		||||
        rm_trailing_spaces_buf(ctx);
 | 
			
		||||
 | 
			
		||||
        char line[MAX_STR_SIZE] = {0};
 | 
			
		||||
 | 
			
		||||
        if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
 | 
			
		||||
            memset(&line, 0, sizeof(line));
 | 
			
		||||
 | 
			
		||||
        if (!string_is_empty(line))
 | 
			
		||||
        if (!wstring_is_empty(ctx->line)) {
 | 
			
		||||
            add_line_to_hist(ctx);
 | 
			
		||||
            wstrsubst(ctx->line, L'¶', L'\n');
 | 
			
		||||
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
 | 
			
		||||
            char line[MAX_STR_SIZE];
 | 
			
		||||
 | 
			
		||||
            if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
 | 
			
		||||
                line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
 | 
			
		||||
            } else {
 | 
			
		||||
                line_info_add(self, false, 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);
 | 
			
		||||
        wmove(self->window, y2, 0);
 | 
			
		||||
        reset_buf(ctx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return input_ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void prompt_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    int x2;
 | 
			
		||||
    int y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
@@ -268,68 +317,95 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
 | 
			
		||||
    wclear(ctx->linewin);
 | 
			
		||||
 | 
			
		||||
    curs_set(1);
 | 
			
		||||
    if (ctx->len > 0) {
 | 
			
		||||
        mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ctx->len > 0)
 | 
			
		||||
        mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
 | 
			
		||||
    mvwhline(ctx->linewin, 0, ctx->len, ' ', x2 - ctx->len);
 | 
			
		||||
 | 
			
		||||
    curs_set(1);
 | 
			
		||||
 | 
			
		||||
    StatusBar *statusbar = self->stb;
 | 
			
		||||
 | 
			
		||||
    mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
 | 
			
		||||
    wmove(statusbar->topline, 0, 0);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
    TOX_CONNECTION connection = statusbar->connection;
 | 
			
		||||
    Tox_Connection connection = statusbar->connection;
 | 
			
		||||
    Tox_User_Status status = statusbar->status;
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    if (connection != TOX_CONNECTION_NONE) {
 | 
			
		||||
        int colour = MAGENTA;
 | 
			
		||||
        const char *status_text = "ERROR";
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
        TOX_USER_STATUS status = statusbar->status;
 | 
			
		||||
        pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
        switch (status) {
 | 
			
		||||
            case TOX_USER_STATUS_NONE:
 | 
			
		||||
                status_text = "Online";
 | 
			
		||||
                colour = GREEN;
 | 
			
		||||
                colour = STATUS_ONLINE;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case TOX_USER_STATUS_AWAY:
 | 
			
		||||
                status_text = "Away";
 | 
			
		||||
                colour = YELLOW;
 | 
			
		||||
                colour = STATUS_AWAY;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case TOX_USER_STATUS_BUSY:
 | 
			
		||||
                status_text = "Busy";
 | 
			
		||||
                colour = RED;
 | 
			
		||||
                colour = STATUS_BUSY;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
 | 
			
		||||
        wprintw(statusbar->topline, " [%s]", status_text);
 | 
			
		||||
        wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
        wprintw(statusbar->topline, " [");
 | 
			
		||||
        wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, A_BOLD | COLOR_PAIR(colour));
 | 
			
		||||
        wprintw(statusbar->topline, "%s", status_text);
 | 
			
		||||
        wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(colour));
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
        wprintw(statusbar->topline, "]");
 | 
			
		||||
        wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, A_BOLD);
 | 
			
		||||
        pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
        wprintw(statusbar->topline, " %s", statusbar->nick);
 | 
			
		||||
        pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
        wattroff(statusbar->topline, A_BOLD);
 | 
			
		||||
    } else {
 | 
			
		||||
        wprintw(statusbar->topline, " [Offline]");
 | 
			
		||||
        wattron(statusbar->topline, A_BOLD);
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
        wprintw(statusbar->topline, " [");
 | 
			
		||||
        wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
 | 
			
		||||
        wprintw(statusbar->topline, "Offline");
 | 
			
		||||
        wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
        wprintw(statusbar->topline, "]");
 | 
			
		||||
        wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
        wprintw(statusbar->topline, " %s", statusbar->nick);
 | 
			
		||||
        pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
        wattroff(statusbar->topline, A_BOLD);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int s_y;
 | 
			
		||||
    int s_x;
 | 
			
		||||
    getyx(statusbar->topline, s_y, s_x);
 | 
			
		||||
 | 
			
		||||
    mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x);
 | 
			
		||||
    wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
 | 
			
		||||
 | 
			
		||||
    /* Reset statusbar->statusmsg on window resize */
 | 
			
		||||
    if (x2 != self->x) {
 | 
			
		||||
        char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
 | 
			
		||||
 | 
			
		||||
        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,97 +425,112 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
 | 
			
		||||
        statusbar->statusmsg_len = maxlen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (statusbar->statusmsg[0])
 | 
			
		||||
        wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
 | 
			
		||||
    if (statusbar->statusmsg[0]) {
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
        wprintw(statusbar->topline, " | ");
 | 
			
		||||
        wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
 | 
			
		||||
 | 
			
		||||
        wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
 | 
			
		||||
        wprintw(statusbar->topline, "%s", statusbar->statusmsg);
 | 
			
		||||
        wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
    mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
 | 
			
		||||
 | 
			
		||||
    int y, x;
 | 
			
		||||
    int y;
 | 
			
		||||
    int x;
 | 
			
		||||
    getyx(self->window, y, x);
 | 
			
		||||
    (void) x;
 | 
			
		||||
 | 
			
		||||
    UNUSED_VAR(x);
 | 
			
		||||
 | 
			
		||||
    int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
 | 
			
		||||
    wmove(self->window, y + 1, new_x);
 | 
			
		||||
    wmove(self->window, y, new_x);
 | 
			
		||||
 | 
			
		||||
    wrefresh(self->window);
 | 
			
		||||
    draw_window_bar(self);
 | 
			
		||||
 | 
			
		||||
    if (self->help->active)
 | 
			
		||||
    wnoutrefresh(self->window);
 | 
			
		||||
 | 
			
		||||
    if (self->help->active) {
 | 
			
		||||
        help_onDraw(self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
 | 
			
		||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum, Tox_Connection connection_status)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    char nick[TOX_MAX_NAME_LENGTH] = {0};    /* stop removing this initiation */
 | 
			
		||||
    get_nick_truncate(m, nick, friendnum);
 | 
			
		||||
 | 
			
		||||
    if (!nick[0])
 | 
			
		||||
    if (!nick[0]) {
 | 
			
		||||
        snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    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);
 | 
			
		||||
        line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg);
 | 
			
		||||
        write_to_log(msg, nick, ctx->log, true);
 | 
			
		||||
 | 
			
		||||
        if (self->active_box != -1)
 | 
			
		||||
        if (self->active_box != -1) {
 | 
			
		||||
            box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
 | 
			
		||||
                        "%s has come online", nick );
 | 
			
		||||
        else
 | 
			
		||||
                        "%s has come online", nick);
 | 
			
		||||
        } else {
 | 
			
		||||
            box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
 | 
			
		||||
                       "Toxic", "%s has come online", nick );
 | 
			
		||||
                       "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);
 | 
			
		||||
        line_info_add(self, true, nick, NULL, DISCONNECTION, 0, RED, msg);
 | 
			
		||||
        write_to_log(msg, nick, ctx->log, true);
 | 
			
		||||
 | 
			
		||||
        if (self->active_box != -1)
 | 
			
		||||
        if (self->active_box != -1) {
 | 
			
		||||
            box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
 | 
			
		||||
                        "%s has gone offline", nick );
 | 
			
		||||
        else
 | 
			
		||||
                        "%s has gone offline", nick);
 | 
			
		||||
        } else {
 | 
			
		||||
            box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
 | 
			
		||||
                       "Toxic", "%s has gone offline", nick );
 | 
			
		||||
                       "Toxic", "%s has gone offline", nick);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length)
 | 
			
		||||
{
 | 
			
		||||
    UNUSED_VAR(m);
 | 
			
		||||
    UNUSED_VAR(length);
 | 
			
		||||
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    char timefrmt[TIME_STR_SIZE];
 | 
			
		||||
    get_time_str(timefrmt, sizeof(timefrmt));
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
 | 
			
		||||
    line_info_add(self, true, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
 | 
			
		||||
    write_to_log("Friend request with the message '%s'", "", ctx->log, true);
 | 
			
		||||
 | 
			
		||||
    int n = add_friend_request(key, data);
 | 
			
		||||
 | 
			
		||||
    if (n == -1) {
 | 
			
		||||
        const char *errmsg = "Friend request queue is full. Discarding request.";
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n);
 | 
			
		||||
    sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void prompt_init_statusbar(ToxWindow *self, Tox *m)
 | 
			
		||||
void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run)
 | 
			
		||||
{
 | 
			
		||||
    int x2, y2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0) {
 | 
			
		||||
        exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    (void) y2;
 | 
			
		||||
    UNUSED_VAR(y2);
 | 
			
		||||
 | 
			
		||||
    /* Init statusbar info */
 | 
			
		||||
    StatusBar *statusbar = self->stb;
 | 
			
		||||
@@ -455,12 +546,12 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
 | 
			
		||||
    size_t s_len = tox_self_get_status_message_size(m);
 | 
			
		||||
    tox_self_get_status_message(m, (uint8_t *) statusmsg);
 | 
			
		||||
 | 
			
		||||
    TOX_USER_STATUS status = tox_self_get_status(m);
 | 
			
		||||
    Tox_User_Status status = tox_self_get_status(m);
 | 
			
		||||
 | 
			
		||||
    nick[n_len] = '\0';
 | 
			
		||||
    statusmsg[s_len] = '\0';
 | 
			
		||||
 | 
			
		||||
    if (s_len == 0 || !strncmp(statusmsg, "Toxing on Toxic", strlen("Toxing on Toxic"))) {
 | 
			
		||||
    if (first_time_run) {
 | 
			
		||||
        snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic");
 | 
			
		||||
        s_len = strlen(statusmsg);
 | 
			
		||||
        statusmsg[s_len] = '\0';
 | 
			
		||||
@@ -471,90 +562,113 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
 | 
			
		||||
    prompt_update_nick(prompt, nick);
 | 
			
		||||
 | 
			
		||||
    /* Init statusbar subwindow */
 | 
			
		||||
    statusbar->topline = subwin(self->window, 2, x2, 0, 0);
 | 
			
		||||
    statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_welcome_msg(ToxWindow *self)
 | 
			
		||||
{
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "    _____ _____  _____ ____ ");
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|");
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    ");
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ ");
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____| v." TOXICVER);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "    _____ _____  _____ ____ ");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    ");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ ");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____| v." TOXICVER);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
 | 
			
		||||
    const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client.";
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
 | 
			
		||||
    const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messaging client.";
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 1, CYAN, msg);
 | 
			
		||||
    msg = "Type \"/help\" for assistance. Further help may be found via the man page.";
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 1, CYAN, msg);
 | 
			
		||||
    line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name)
 | 
			
		||||
{
 | 
			
		||||
    ChatContext *ctx = self->chatwin;
 | 
			
		||||
 | 
			
		||||
    char myid[TOX_ADDRESS_SIZE];
 | 
			
		||||
    tox_self_get_address(m, (uint8_t *) myid);
 | 
			
		||||
 | 
			
		||||
    if (log_init(ctx->log, self->name, myid, NULL, LOG_TYPE_PROMPT) != 0) {
 | 
			
		||||
        line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (user_settings->autolog == AUTOLOG_ON) {
 | 
			
		||||
        if (log_enable(ctx->log) == -1) {
 | 
			
		||||
            line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Failed to enable log.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void prompt_onInit(ToxWindow *self, Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    curs_set(1);
 | 
			
		||||
    int y2, x2;
 | 
			
		||||
 | 
			
		||||
    int y2;
 | 
			
		||||
    int x2;
 | 
			
		||||
    getmaxyx(self->window, y2, x2);
 | 
			
		||||
 | 
			
		||||
    if (y2 <= 0 || x2 <= 0)
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
 | 
			
		||||
    self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0);
 | 
			
		||||
    ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0);
 | 
			
		||||
 | 
			
		||||
    ctx->log = calloc(1, sizeof(struct chatlog));
 | 
			
		||||
    ctx->hst = calloc(1, sizeof(struct history));
 | 
			
		||||
 | 
			
		||||
    if (ctx->log == NULL || ctx->hst == NULL)
 | 
			
		||||
    if (ctx->log == NULL || ctx->hst == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_init(ctx->hst);
 | 
			
		||||
 | 
			
		||||
    if (user_settings->autolog == AUTOLOG_ON) {
 | 
			
		||||
        char myid[TOX_ADDRESS_SIZE];
 | 
			
		||||
        tox_self_get_address(m, (uint8_t *) myid);
 | 
			
		||||
 | 
			
		||||
        if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1)
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
 | 
			
		||||
    }
 | 
			
		||||
    prompt_init_log(self, m, self->name);
 | 
			
		||||
 | 
			
		||||
    scrollok(ctx->history, 0);
 | 
			
		||||
    wmove(self->window, y2 - CURS_Y_OFFSET, 0);
 | 
			
		||||
 | 
			
		||||
    if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON)
 | 
			
		||||
    if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON) {
 | 
			
		||||
        print_welcome_msg(self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ToxWindow new_prompt(void)
 | 
			
		||||
ToxWindow *new_prompt(void)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow ret;
 | 
			
		||||
    memset(&ret, 0, sizeof(ret));
 | 
			
		||||
    ToxWindow *ret = calloc(1, sizeof(ToxWindow));
 | 
			
		||||
 | 
			
		||||
    ret.num = -1;
 | 
			
		||||
    ret.active = true;
 | 
			
		||||
    ret.is_prompt = true;
 | 
			
		||||
    if (ret == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret.onKey = &prompt_onKey;
 | 
			
		||||
    ret.onDraw = &prompt_onDraw;
 | 
			
		||||
    ret.onInit = &prompt_onInit;
 | 
			
		||||
    ret.onConnectionChange = &prompt_onConnectionChange;
 | 
			
		||||
    ret.onFriendRequest = &prompt_onFriendRequest;
 | 
			
		||||
    ret->num = -1;
 | 
			
		||||
    ret->type = WINDOW_TYPE_PROMPT;
 | 
			
		||||
 | 
			
		||||
    strcpy(ret.name, "home");
 | 
			
		||||
    ret->onKey = &prompt_onKey;
 | 
			
		||||
    ret->onDraw = &prompt_onDraw;
 | 
			
		||||
    ret->onInit = &prompt_onInit;
 | 
			
		||||
    ret->onConnectionChange = &prompt_onConnectionChange;
 | 
			
		||||
    ret->onFriendRequest = &prompt_onFriendRequest;
 | 
			
		||||
 | 
			
		||||
    strcpy(ret->name, "Home");
 | 
			
		||||
 | 
			
		||||
    ChatContext *chatwin = calloc(1, sizeof(ChatContext));
 | 
			
		||||
    StatusBar *stb = calloc(1, sizeof(StatusBar));
 | 
			
		||||
    Help *help = calloc(1, sizeof(Help));
 | 
			
		||||
 | 
			
		||||
    if (stb == NULL || chatwin == NULL || help == NULL)
 | 
			
		||||
    if (stb == NULL || chatwin == NULL || help == NULL) {
 | 
			
		||||
        exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret.chatwin = chatwin;
 | 
			
		||||
    ret.stb = stb;
 | 
			
		||||
    ret.help = help;
 | 
			
		||||
    ret->chatwin = chatwin;
 | 
			
		||||
    ret->stb = stb;
 | 
			
		||||
    ret->help = help;
 | 
			
		||||
 | 
			
		||||
    ret.active_box = -1;
 | 
			
		||||
    ret->active_box = -1;
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								src/prompt.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/prompt.h
									
									
									
									
									
								
							@@ -26,31 +26,34 @@
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_FRIEND_REQUESTS 32
 | 
			
		||||
#define MAX_FRIEND_REQUESTS 20
 | 
			
		||||
 | 
			
		||||
struct friend_request {
 | 
			
		||||
    bool active;
 | 
			
		||||
    char msg[MAX_STR_SIZE];
 | 
			
		||||
    char msg[TOX_MAX_FRIEND_REQUEST_LENGTH + 1];
 | 
			
		||||
    uint8_t key[TOX_PUBLIC_KEY_SIZE];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
typedef struct FriendRequests {
 | 
			
		||||
    int max_idx;
 | 
			
		||||
    int num_requests;
 | 
			
		||||
    struct friend_request request[MAX_FRIEND_REQUESTS];
 | 
			
		||||
} FriendRequests;
 | 
			
		||||
 | 
			
		||||
ToxWindow new_prompt(void);
 | 
			
		||||
ToxWindow *new_prompt(void);
 | 
			
		||||
 | 
			
		||||
void prep_prompt_win(void);
 | 
			
		||||
void prompt_init_statusbar(ToxWindow *self, Tox *m);
 | 
			
		||||
void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run);
 | 
			
		||||
void prompt_update_nick(ToxWindow *prompt, const char *nick);
 | 
			
		||||
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg);
 | 
			
		||||
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status);
 | 
			
		||||
void prompt_update_status(ToxWindow *prompt, Tox_User_Status status);
 | 
			
		||||
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
 | 
			
		||||
void kill_prompt_window(ToxWindow *self);
 | 
			
		||||
 | 
			
		||||
/* callback: Updates own connection status in prompt statusbar */
 | 
			
		||||
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata);
 | 
			
		||||
void on_self_connection_status(Tox *m, Tox_Connection connection_status, void *userdata);
 | 
			
		||||
 | 
			
		||||
/* Returns our own connection status */
 | 
			
		||||
Tox_Connection prompt_selfConnectionStatus(void);
 | 
			
		||||
 | 
			
		||||
#endif /* end of include guard: PROMPT_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										365
									
								
								src/python_api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								src/python_api.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,365 @@
 | 
			
		||||
/*  python_api.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
#include <Python.h>
 | 
			
		||||
#include "api.h"
 | 
			
		||||
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
 | 
			
		||||
extern Tox       *user_tox;
 | 
			
		||||
 | 
			
		||||
static struct python_registered_func {
 | 
			
		||||
    char     *name;
 | 
			
		||||
    char     *help;
 | 
			
		||||
    PyObject *callback;
 | 
			
		||||
    struct python_registered_func *next;
 | 
			
		||||
} python_commands = {0};
 | 
			
		||||
 | 
			
		||||
static PyObject *python_api_display(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    const char *msg;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "s", &msg)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    api_display(msg);
 | 
			
		||||
    return Py_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *python_api_get_nick(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    char     *name;
 | 
			
		||||
    PyObject *ret;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "")) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    name = api_get_nick();
 | 
			
		||||
 | 
			
		||||
    if (name == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret  = Py_BuildValue("s", name);
 | 
			
		||||
    free(name);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *python_api_get_status(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    PyObject        *ret = NULL;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "")) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (api_get_status()) {
 | 
			
		||||
        case TOX_USER_STATUS_NONE:
 | 
			
		||||
            ret = Py_BuildValue("s", "online");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TOX_USER_STATUS_AWAY:
 | 
			
		||||
            ret = Py_BuildValue("s", "away");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TOX_USER_STATUS_BUSY:
 | 
			
		||||
            ret = Py_BuildValue("s", "busy");
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *python_api_get_status_message(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    char     *status;
 | 
			
		||||
    PyObject *ret;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "")) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    status = api_get_status_message();
 | 
			
		||||
 | 
			
		||||
    if (status == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret    = Py_BuildValue("s", status);
 | 
			
		||||
    free(status);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    FriendsList  friends;
 | 
			
		||||
    char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "")) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    friends = api_get_friendslist();
 | 
			
		||||
    PyObject *ret = PyList_New(0);
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < friends.num_friends; i++) {
 | 
			
		||||
        for (size_t ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) {
 | 
			
		||||
            snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0';
 | 
			
		||||
        PyObject *cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
 | 
			
		||||
        PyList_Append(ret, cur);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *python_api_send(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    const char *msg;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "s", &msg)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    api_send(msg);
 | 
			
		||||
    return Py_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *python_api_execute(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    int         mode;
 | 
			
		||||
    const char *command;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "si", &command, &mode)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    api_execute(command, mode);
 | 
			
		||||
    return Py_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *python_api_register(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    struct python_registered_func *cur;
 | 
			
		||||
    size_t      command_len, help_len;
 | 
			
		||||
    const char *command, *help;
 | 
			
		||||
    PyObject   *callback;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "ssO:register_command", &command, &help, &callback)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!PyCallable_Check(callback)) {
 | 
			
		||||
        PyErr_SetString(PyExc_TypeError, "Calback parameter must be callable");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (command[0] != '/') {
 | 
			
		||||
        PyErr_SetString(PyExc_TypeError, "Command must be prefixed with a '/'");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (cur = &python_commands; ; cur = cur->next) {
 | 
			
		||||
        if (cur->name != NULL && !strcmp(command, cur->name)) {
 | 
			
		||||
            Py_XDECREF(cur->callback);
 | 
			
		||||
            Py_XINCREF(callback);
 | 
			
		||||
            cur->callback = callback;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cur->next == NULL) {
 | 
			
		||||
            Py_XINCREF(callback);
 | 
			
		||||
            cur->next = malloc(sizeof(struct python_registered_func));
 | 
			
		||||
 | 
			
		||||
            if (cur->next == NULL) {
 | 
			
		||||
                return PyErr_NoMemory();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            command_len     = strlen(command);
 | 
			
		||||
            cur->next->name = malloc(command_len + 1);
 | 
			
		||||
 | 
			
		||||
            if (cur->next->name == NULL) {
 | 
			
		||||
                return PyErr_NoMemory();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            strncpy(cur->next->name, command, command_len + 1);
 | 
			
		||||
            help_len        = strlen(help);
 | 
			
		||||
            cur->next->help = malloc(help_len + 1);
 | 
			
		||||
 | 
			
		||||
            if (cur->next->help == NULL) {
 | 
			
		||||
                return PyErr_NoMemory();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            strncpy(cur->next->help, help, help_len + 1);
 | 
			
		||||
            cur->next->callback = callback;
 | 
			
		||||
            cur->next->next     = NULL;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Py_INCREF(Py_None);
 | 
			
		||||
    return Py_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyMethodDef ToxicApiMethods[] = {
 | 
			
		||||
    {"display",            python_api_display,            METH_VARARGS, "Display a message to the current prompt"},
 | 
			
		||||
    {"get_nick",           python_api_get_nick,           METH_VARARGS, "Return the user's current nickname"},
 | 
			
		||||
    {"get_status",         python_api_get_status,         METH_VARARGS, "Returns the user's current status"},
 | 
			
		||||
    {"get_status_message", python_api_get_status_message, METH_VARARGS, "Return the user's current status message"},
 | 
			
		||||
    {"get_all_friends",    python_api_get_all_friends,    METH_VARARGS, "Return all of the user's friends"},
 | 
			
		||||
    {"send",               python_api_send,               METH_VARARGS, "Send the message to the current user"},
 | 
			
		||||
    {"execute",            python_api_execute,            METH_VARARGS, "Execute a command like `/nick`"},
 | 
			
		||||
    {"register",           python_api_register,           METH_VARARGS, "Register a command like `/nick` to a Python function"},
 | 
			
		||||
    {NULL,                 NULL,                          0,            NULL},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct PyModuleDef toxic_api_module = {
 | 
			
		||||
    PyModuleDef_HEAD_INIT,
 | 
			
		||||
    "toxic_api",
 | 
			
		||||
    NULL,
 | 
			
		||||
    -1,
 | 
			
		||||
    ToxicApiMethods
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyMODINIT_FUNC PyInit_toxic_api(void)
 | 
			
		||||
{
 | 
			
		||||
    PyObject *m = PyModule_Create(&toxic_api_module);
 | 
			
		||||
    PyObject *global_command_const    = Py_BuildValue("i", GLOBAL_COMMAND_MODE);
 | 
			
		||||
    PyObject *chat_command_const      = Py_BuildValue("i", CHAT_COMMAND_MODE);
 | 
			
		||||
    PyObject *conference_command_const = Py_BuildValue("i", CONFERENCE_COMMAND_MODE);
 | 
			
		||||
    PyObject_SetAttrString(m, "GLOBAL_COMMAND",    global_command_const);
 | 
			
		||||
    PyObject_SetAttrString(m, "CHAT_COMMAND",      chat_command_const);
 | 
			
		||||
    PyObject_SetAttrString(m, "CONFERENCE_COMMAND", conference_command_const);
 | 
			
		||||
    Py_DECREF(global_command_const);
 | 
			
		||||
    Py_DECREF(chat_command_const);
 | 
			
		||||
    Py_DECREF(conference_command_const);
 | 
			
		||||
    return m;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void terminate_python(void)
 | 
			
		||||
{
 | 
			
		||||
    if (python_commands.name != NULL) {
 | 
			
		||||
        free(python_commands.name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct python_registered_func *cur = NULL;
 | 
			
		||||
 | 
			
		||||
    for (cur = python_commands.next; cur != NULL;) {
 | 
			
		||||
        struct python_registered_func *old = cur;
 | 
			
		||||
        cur = cur->next;
 | 
			
		||||
        free(old->name);
 | 
			
		||||
        free(old);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Py_Finalize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void init_python(Tox *m)
 | 
			
		||||
{
 | 
			
		||||
    user_tox = m;
 | 
			
		||||
    PyImport_AppendInittab("toxic_api", PyInit_toxic_api);
 | 
			
		||||
    Py_Initialize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void run_python(FILE *fp, char *path)
 | 
			
		||||
{
 | 
			
		||||
    PyRun_SimpleFile(fp, path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int do_python_command(int num_args, char (*args)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    PyObject *callback_args, *args_strings;
 | 
			
		||||
    struct python_registered_func *cur;
 | 
			
		||||
 | 
			
		||||
    for (cur = &python_commands; cur != NULL; cur = cur->next) {
 | 
			
		||||
        if (cur->name == NULL) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!strcmp(args[0], cur->name)) {
 | 
			
		||||
            args_strings = PyList_New(0);
 | 
			
		||||
 | 
			
		||||
            for (i = 1; i < num_args; i++) {
 | 
			
		||||
                PyList_Append(args_strings, Py_BuildValue("s", args[i]));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            callback_args = PyTuple_Pack(1, args_strings);
 | 
			
		||||
 | 
			
		||||
            if (PyObject_CallObject(cur->callback, callback_args) == NULL) {
 | 
			
		||||
                api_display("Exception raised in callback function");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int python_num_registered_handlers(void)
 | 
			
		||||
{
 | 
			
		||||
    int n = 0;
 | 
			
		||||
    struct python_registered_func *cur;
 | 
			
		||||
 | 
			
		||||
    for (cur = &python_commands; cur != NULL; cur = cur->next) {
 | 
			
		||||
        if (cur->name != NULL) {
 | 
			
		||||
            n++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int python_help_max_width(void)
 | 
			
		||||
{
 | 
			
		||||
    size_t tmp;
 | 
			
		||||
    int    max = 0;
 | 
			
		||||
    struct python_registered_func *cur;
 | 
			
		||||
 | 
			
		||||
    for (cur = &python_commands; cur != NULL; cur = cur->next) {
 | 
			
		||||
        if (cur->name != NULL) {
 | 
			
		||||
            tmp = strlen(cur->help);
 | 
			
		||||
            max = tmp > max ? tmp : max;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    max = max > 50 ? 50 : max;
 | 
			
		||||
    return 37 + max;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void python_draw_handler_help(WINDOW *win)
 | 
			
		||||
{
 | 
			
		||||
    struct python_registered_func *cur;
 | 
			
		||||
 | 
			
		||||
    for (cur = &python_commands; cur != NULL; cur = cur->next) {
 | 
			
		||||
        if (cur->name != NULL) {
 | 
			
		||||
            wprintw(win, "  %-29s: %.50s\n", cur->name, cur->help);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*  xtra.h
 | 
			
		||||
/*  python_api.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
@@ -20,22 +20,20 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef XTRA_H
 | 
			
		||||
#define XTRA_H
 | 
			
		||||
#ifndef PYTHON_API_H
 | 
			
		||||
#define PYTHON_API_H
 | 
			
		||||
 | 
			
		||||
/* NOTE: If no xlib present don't compile */
 | 
			
		||||
#ifdef PYTHON
 | 
			
		||||
#include <Python.h>
 | 
			
		||||
#endif /* PYTHON */
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    DT_plain,
 | 
			
		||||
    DT_file_list
 | 
			
		||||
} 
 | 
			
		||||
DropType;
 | 
			
		||||
PyMODINIT_FUNC PyInit_toxic_api(void);
 | 
			
		||||
void terminate_python(void);
 | 
			
		||||
void init_python(Tox *m);
 | 
			
		||||
void run_python(FILE *fp, char *path);
 | 
			
		||||
int do_python_command(int num_args, char (*args)[MAX_STR_SIZE]);
 | 
			
		||||
int python_num_registered_handlers(void);
 | 
			
		||||
int python_help_max_width(void);
 | 
			
		||||
void python_draw_handler_help(WINDOW *win);
 | 
			
		||||
 | 
			
		||||
typedef void (*drop_callback) (const char*, DropType);
 | 
			
		||||
 | 
			
		||||
int               init_xtra(drop_callback d);
 | 
			
		||||
void              terminate_xtra();
 | 
			
		||||
long unsigned int focused_window_id();
 | 
			
		||||
int               is_focused(); /* returns bool */
 | 
			
		||||
 | 
			
		||||
#endif /* XTRA_H */
 | 
			
		||||
#endif /* PYTHON_API_H */
 | 
			
		||||
							
								
								
									
										167
									
								
								src/qr_code.c
									
									
									
									
									
								
							
							
						
						
									
										167
									
								
								src/qr_code.c
									
									
									
									
									
								
							@@ -20,45 +20,62 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#ifdef QRCODE
 | 
			
		||||
 | 
			
		||||
#include <qrencode.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "qr_code.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 to the given file stream.
 | 
			
		||||
/* 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(const char *tox_id, FILE *fp)
 | 
			
		||||
int ID_to_QRcode_txt(const char *tox_id, const char *outfile)
 | 
			
		||||
{
 | 
			
		||||
    if (fp == NULL)
 | 
			
		||||
    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)
 | 
			
		||||
    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)
 | 
			
		||||
    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)
 | 
			
		||||
        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;
 | 
			
		||||
@@ -67,23 +84,147 @@ int ID_to_QRcode(const char *tox_id, FILE *fp)
 | 
			
		||||
            bool x = row_1[j] & 1;
 | 
			
		||||
            bool y = (i + 1) < width ? (row_2[j] & 1) : false;
 | 
			
		||||
 | 
			
		||||
            if (x && y)
 | 
			
		||||
            if (x && y) {
 | 
			
		||||
                fprintf(fp, " ");
 | 
			
		||||
            else if (x)
 | 
			
		||||
            } else if (x) {
 | 
			
		||||
                fprintf(fp, "%s", CHAR_2);
 | 
			
		||||
            else if (y)
 | 
			
		||||
            } else if (y) {
 | 
			
		||||
                fprintf(fp, "%s", CHAR_3);
 | 
			
		||||
            else
 | 
			
		||||
            } else {
 | 
			
		||||
                fprintf(fp, "%s", CHAR_1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (j = 0; j < BORDER_LEN; ++j)
 | 
			
		||||
        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 = malloc(row_size);
 | 
			
		||||
 | 
			
		||||
    if (row == NULL) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        QRcode_free(qr_obj);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    if (png_ptr == NULL) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        free(row);
 | 
			
		||||
        QRcode_free(qr_obj);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    info_ptr = png_create_info_struct(png_ptr);
 | 
			
		||||
 | 
			
		||||
    if (info_ptr == NULL) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        free(row);
 | 
			
		||||
        QRcode_free(qr_obj);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (setjmp(png_jmpbuf(png_ptr))) {
 | 
			
		||||
        fclose(fp);
 | 
			
		||||
        free(row);
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(row);
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
 | 
			
		||||
    png_write_end(png_ptr, info_ptr);
 | 
			
		||||
    png_destroy_write_struct(&png_ptr, &info_ptr);
 | 
			
		||||
 | 
			
		||||
    QRcode_free(qr_obj);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif /* QRPNG */
 | 
			
		||||
 | 
			
		||||
#endif /* QRCODE */
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user