mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-10-26 18:56:47 +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/toxic | ||||||
| build/*.o | build/*.o | ||||||
| build/*.d | 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 | # Installation | ||||||
| * [Dependencies](#deps) | * [Dependencies](#dependencies) | ||||||
|   * [OS X Notes](#deps_osx) |   * [OS X Notes](#os-x-notes) | ||||||
| * [Compiling](#compiling) | * [Compiling](#compiling) | ||||||
|   * [Documentation](#docs) |   * [Documentation](#documentation) | ||||||
| * [Notes](#notes) | * [Notes](#notes) | ||||||
|   * [Compilation variables](#comp_vars) |   * [Compilation variables](#compilation-variables) | ||||||
|   * [Packaging](#packaging) |   * [Environment variables](#environment-variables) | ||||||
|  |  | ||||||
| <a name="deps" /> |  | ||||||
| ## Dependencies | ## Dependencies | ||||||
| | Name                                                 | Needed by                  | Debian package      | | | Name                                                 | Needed by                  | Debian package      | | ||||||
| |------------------------------------------------------|----------------------------|---------------------| | |------------------------------------------------------|----------------------------|---------------------| | ||||||
| | [Tox Core](https://github.com/irungentoo/toxcore)    | BASE                       | *None*              | | | [Tox Core](https://github.com/toktok/c-toxcore)      | BASE                       | *None*              | | ||||||
| | [NCurses](https://www.gnu.org/software/ncurses)      | BASE                       | libncursesw5-dev    | | | [NCurses](https://www.gnu.org/software/ncurses)      | BASE                       | libncursesw5-dev    | | ||||||
| | [LibConfig](http://www.hyperrealm.com/libconfig)     | BASE                       | libconfig-dev       | | | [LibConfig](http://www.hyperrealm.com/libconfig)     | BASE                       | libconfig-dev       | | ||||||
| | [GNUmake](https://www.gnu.org/software/make)         | BASE                       | make                | | | [GNUmake](https://www.gnu.org/software/make)         | BASE                       | make                | | ||||||
| | [libcurl](http://curl.haxx.se/)                      | BASE                       | libcurl4-openssl-dev| | | [libcurl](http://curl.haxx.se/)                      | BASE                       | libcurl4-openssl-dev| | ||||||
| | [libqrencode](https://fukuchi.org/works/qrencode/)   | BASE                       | libqrencode-dev     | | | [libqrencode](https://fukuchi.org/works/qrencode/)   | QRCODE                     | libqrencode-dev     | | ||||||
| | [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO                      | *None*              | |  | ||||||
| | [OpenAL](http://openal.org)                          | AUDIO, SOUND NOTIFICATIONS | libopenal-dev       | | | [OpenAL](http://openal.org)                          | AUDIO, SOUND NOTIFICATIONS | libopenal-dev       | | ||||||
| | [OpenALUT](http://openal.org)                        | SOUND NOTIFICATIONS        | libalut-dev         | | | [OpenALUT](http://openal.org)                        | SOUND NOTIFICATIONS        | libalut-dev         | | ||||||
| | [LibNotify](https://developer.gnome.org/libnotify)   | DESKTOP NOTIFICATIONS      | libnotify-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            | | | [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 | #### OS X Notes | ||||||
| Using [Homebrew](http://brew.sh): | Using [Homebrew](http://brew.sh): | ||||||
| ``` | ``` | ||||||
| brew install openal-soft freealut libconfig | brew install curl qrencode openal-soft freealut libconfig libpng | ||||||
| brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb | brew install --HEAD 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 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. | You can omit `libnotify` if you intend to build without desktop notifications enabled. | ||||||
|  |  | ||||||
| <a name="Compiling"> |  | ||||||
| ## Compiling | ## Compiling | ||||||
| ``` | ``` | ||||||
| make PREFIX="/where/to/install" | make | ||||||
| sudo make install PREFIX="/where/to/install" | sudo make install | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| <a name="docs" /> |  | ||||||
| #### Documentation | #### Documentation | ||||||
| Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br /> | 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 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. | **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 | ## Notes | ||||||
|  |  | ||||||
| <a name="comp_vars" /> |  | ||||||
| #### Compilation variables | #### Compilation variables | ||||||
| * You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""` | * You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables | ||||||
| * You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones) | * Default compile options can be overridden by using special variables: | ||||||
| * Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables: |   * `DISABLE_X11=1` → Disable X11 support (needed for focus tracking) | ||||||
|   * `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking) |   * `DISABLE_AV=1` → Disable audio call support | ||||||
|   * `DISABLE_AV=1` → build toxic without audio call support |   * `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support | ||||||
|   * `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support |   * `DISABLE_QRCODE` → Disable QR exporting support | ||||||
|   * `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications 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" /> | * `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg") | ||||||
| #### 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") |  | ||||||
|  |  | ||||||
|  | #### 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. | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								Makefile
									
									
									
									
									
								
							| @@ -3,39 +3,52 @@ CFG_DIR = $(BASE_DIR)/cfg | |||||||
|  |  | ||||||
| -include $(CFG_DIR)/global_vars.mk | -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 += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | ||||||
| CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | ||||||
| CFLAGS += $(USER_CFLAGS) | CFLAGS += ${USER_CFLAGS} | ||||||
| LDFLAGS = $(USER_LDFLAGS) | LDFLAGS ?= | ||||||
|  | LDFLAGS += ${USER_LDFLAGS} | ||||||
|  |  | ||||||
| OBJ = chat.o chat_commands.o configdir.o execute.o file_transfers.o notify.o | OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o | ||||||
| OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o | OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o conference.o help.o input.o | ||||||
| OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o | OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o | ||||||
| OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.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 | # Check on wich system we are running | ||||||
| UNAME_S = $(shell uname -s) | UNAME_S = $(shell uname -s) | ||||||
| ifeq ($(UNAME_S), Linux) | ifeq ($(UNAME_S), Linux) | ||||||
|     -include $(CFG_DIR)/systems/Linux.mk | LDFLAGS += -ldl -lrt | ||||||
| endif |  | ||||||
| ifeq ($(UNAME_S), FreeBSD) |  | ||||||
|     -include $(CFG_DIR)/systems/FreeBSD.mk |  | ||||||
| endif |  | ||||||
| ifeq ($(UNAME_S), DragonFly) |  | ||||||
|     -include $(CFG_DIR)/systems/FreeBSD.mk |  | ||||||
| endif | endif | ||||||
| ifeq ($(UNAME_S), OpenBSD) | 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 | endif | ||||||
| ifeq ($(UNAME_S), Darwin) | ifeq ($(UNAME_S), Darwin) | ||||||
|     -include $(CFG_DIR)/systems/Darwin.mk |     -include $(CFG_DIR)/systems/Darwin.mk | ||||||
| endif | endif | ||||||
| ifeq ($(UNAME_S), Solaris) |  | ||||||
|     -include $(CFG_DIR)/systems/Solaris.mk |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| # Check on which platform we are running | # Check on which platform we are running | ||||||
| UNAME_M = $(shell uname -m) | UNAME_M = $(shell uname -m) | ||||||
|   | |||||||
| @@ -3,13 +3,12 @@ | |||||||
|        src="https://scan.coverity.com/projects/4975/badge.svg"/> |        src="https://scan.coverity.com/projects/4975/badge.svg"/> | ||||||
| </a> | </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 | ## Installation | ||||||
| [Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br /> | [See the install instructions](/INSTALL.md) | ||||||
| [Compile it yourself](/INSTALL.md) |  | ||||||
|  |  | ||||||
| ## Settings | ## 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. | 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 | # Variables for audio call support | ||||||
| AUDIO_LIBS = libtoxav openal | AUDIO_LIBS = openal | ||||||
| AUDIO_CFLAGS = -DAUDIO | AUDIO_CFLAGS = -DAUDIO | ||||||
| ifneq (, $(findstring audio_device.o, $(OBJ))) | ifneq (, $(findstring audio_device.o, $(OBJ))) | ||||||
|     AUDIO_OBJ = audio_call.o |     AUDIO_OBJ = audio_call.o | ||||||
| @@ -8,13 +8,14 @@ else | |||||||
| endif | endif | ||||||
|  |  | ||||||
| # Check if we can build audio support | # 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) | ifneq ($(CHECK_AUDIO_LIBS), error) | ||||||
|     LIBS += $(AUDIO_LIBS) |     LIBS += $(AUDIO_LIBS) | ||||||
|  |     LDFLAGS += -lm | ||||||
|     CFLAGS += $(AUDIO_CFLAGS) |     CFLAGS += $(AUDIO_CFLAGS) | ||||||
|     OBJ += $(AUDIO_OBJ) |     OBJ += $(AUDIO_OBJ) | ||||||
| else ifneq ($(MAKECMDGOALS), clean) | 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 -- Toxic will be compiled without audio support) | ||||||
|     $(warning WARNING -- You need these libraries for audio support) |     $(warning WARNING -- You need these libraries for audio support) | ||||||
|     $(warning WARNING -- $(MISSING_AUDIO_LIBS)) |     $(warning WARNING -- $(MISSING_AUDIO_LIBS)) | ||||||
|   | |||||||
| @@ -1,19 +1,19 @@ | |||||||
| CHECKS_DIR = $(CFG_DIR)/checks | CHECKS_DIR = $(CFG_DIR)/checks | ||||||
|  |  | ||||||
| # Check if we want build X11 support | # 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) | ifneq ($(X11), disabled) | ||||||
|     -include $(CHECKS_DIR)/x11.mk |     -include $(CHECKS_DIR)/x11.mk | ||||||
| endif | endif | ||||||
|  |  | ||||||
| # Check if we want build audio support | # Check if we want build audio support | ||||||
| AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | AUDIO := $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||||
| ifneq ($(AUDIO), disabled) | ifneq ($(AUDIO), disabled) | ||||||
|     -include $(CHECKS_DIR)/audio.mk |     -include $(CHECKS_DIR)/audio.mk | ||||||
| endif | endif | ||||||
|  |  | ||||||
| # Check if we want build video support | # 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 ($(X11), disabled) | ||||||
| ifneq ($(AUDIO), disabled) | ifneq ($(AUDIO), disabled) | ||||||
| ifneq ($(VIDEO), disabled) | ifneq ($(VIDEO), disabled) | ||||||
| @@ -23,24 +23,42 @@ endif | |||||||
| endif | endif | ||||||
|  |  | ||||||
| # Check if we want build sound notifications support | # 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) | ifneq ($(SND_NOTIFY), disabled) | ||||||
|     -include $(CHECKS_DIR)/sound_notifications.mk |     -include $(CHECKS_DIR)/sound_notifications.mk | ||||||
| endif | endif | ||||||
|  |  | ||||||
| # Check if we want build desktop notifications support | # 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) | ifneq ($(DESK_NOTIFY), disabled) | ||||||
|     -include $(CHECKS_DIR)/desktop_notifications.mk |     -include $(CHECKS_DIR)/desktop_notifications.mk | ||||||
| endif | 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 if we can build Toxic | ||||||
| CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") | CHECK_LIBS := $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") | ||||||
| ifneq ($(CHECK_LIBS), error) | ifneq ($(CHECK_LIBS), error) | ||||||
|     CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) |     CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) | ||||||
|     LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS)) |     LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS)) | ||||||
| else ifneq ($(MAKECMDGOALS), clean) | 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 -- Cannot compile Toxic) | ||||||
|     $(warning ERROR -- You need these libraries) |     $(warning ERROR -- You need these libraries) | ||||||
|     $(warning ERROR -- $(MISSING_LIBS)) |     $(warning ERROR -- $(MISSING_LIBS)) | ||||||
|   | |||||||
| @@ -3,12 +3,12 @@ DESK_NOTIFY_LIBS = libnotify | |||||||
| DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY | DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY | ||||||
|  |  | ||||||
| # Check if we can build desktop notifications support | # 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) | ifneq ($(CHECK_DESK_NOTIFY_LIBS), error) | ||||||
|     LIBS += $(DESK_NOTIFY_LIBS) |     LIBS += $(DESK_NOTIFY_LIBS) | ||||||
|     CFLAGS += $(DESK_NOTIFY_CFLAGS) |     CFLAGS += $(DESK_NOTIFY_CFLAGS) | ||||||
| else ifneq ($(MAKECMDGOALS), clean) | 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 -- Toxic will be compiled without desktop notifications support) | ||||||
|     $(warning WARNING -- You need these libraries for desktop notifications support) |     $(warning WARNING -- You need these libraries for desktop notifications support) | ||||||
|     $(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS)) |     $(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 | # Variables for video call support | ||||||
| VIDEO_LIBS = libtoxav vpx x11 | VIDEO_LIBS = openal vpx x11 | ||||||
| VIDEO_CFLAGS = -DVIDEO | VIDEO_CFLAGS = -DVIDEO | ||||||
| ifneq (, $(findstring video_device.o, $(OBJ))) | ifneq (, $(findstring video_device.o, $(OBJ))) | ||||||
|     VIDEO_OBJ = video_call.o |     VIDEO_OBJ = video_call.o | ||||||
| @@ -8,13 +8,13 @@ else | |||||||
| endif | endif | ||||||
|  |  | ||||||
| # Check if we can build video support | # 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) | ifneq ($(CHECK_VIDEO_LIBS), error) | ||||||
|     LIBS += $(VIDEO_LIBS) |     LIBS += $(VIDEO_LIBS) | ||||||
|     CFLAGS += $(VIDEO_CFLAGS) |     CFLAGS += $(VIDEO_CFLAGS) | ||||||
|     OBJ += $(VIDEO_OBJ) |     OBJ += $(VIDEO_OBJ) | ||||||
| else ifneq ($(MAKECMDGOALS), clean) | 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 -- Toxic will be compiled without video support) | ||||||
|     $(warning WARNING -- You will need these libraries for video support) |     $(warning WARNING -- You will need these libraries for video support) | ||||||
|     $(warning WARNING -- $(MISSING_VIDEO_LIBS)) |     $(warning WARNING -- $(MISSING_VIDEO_LIBS)) | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| # Variables for X11 support | # Variables for X11 support | ||||||
| X11_LIBS = x11 | X11_LIBS = x11 | ||||||
| X11_CFLAGS = -DX11 | X11_CFLAGS = -DX11 | ||||||
| X11_OBJ = xtra.o | X11_OBJ = x11focus.o | ||||||
|  |  | ||||||
| # Check if we can build X11 support | # Check if we can build X11 support | ||||||
| CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error") | CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error") | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| # Version | # Version | ||||||
| TOXIC_VERSION = 0.7.0 | TOXIC_VERSION = 0.10.0 | ||||||
| REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | ||||||
| ifneq (, $(findstring error, $(REV))) | ifneq (, $(findstring error, $(REV))) | ||||||
|     VERSION = $(TOXIC_VERSION) |     VERSION = $(TOXIC_VERSION) | ||||||
| @@ -16,17 +16,17 @@ MISC_DIR = $(BASE_DIR)/misc | |||||||
|  |  | ||||||
| # Project files | # Project files | ||||||
| MANFILES = toxic.1 toxic.conf.5 | MANFILES = toxic.1 toxic.conf.5 | ||||||
| DATAFILES = DHTnodes nameservers toxic.conf.example | DATAFILES = nameservers toxic.conf.example | ||||||
| DESKFILE = toxic.desktop | DESKFILE = toxic.desktop | ||||||
| SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav | SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav | ||||||
| SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav | SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav | ||||||
| SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav | SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav | ||||||
|  |  | ||||||
| # Install directories | # Install directories | ||||||
| PREFIX = /usr/local | PREFIX ?= /usr/local | ||||||
| BINDIR = $(PREFIX)/bin | BINDIR = $(PREFIX)/bin | ||||||
| DATADIR = $(PREFIX)/share/toxic | DATADIR = $(PREFIX)/share/toxic | ||||||
| MANDIR = $(PREFIX)/share/man | MANDIR ?= $(PREFIX)/share/man | ||||||
| APPDIR = $(PREFIX)/share/applications | APPDIR = $(PREFIX)/share/applications | ||||||
|  |  | ||||||
| # Platform tools | # Platform tools | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/o | |||||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | LIBS := $(filter-out ncursesw, $(LIBS)) | ||||||
|  |  | ||||||
| # OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw. | # OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw. | ||||||
| LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -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 | CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g | ||||||
|  |  | ||||||
| OSX_LIBRARIES = -lobjc -lresolv | 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_AV:             Set to \"1\" to force building without audio call support" | ||||||
| 	@echo "  DISABLE_SOUND_NOTIFY:   Set to \"1\" to force building without sound notification support" | 	@echo "  DISABLE_SOUND_NOTIFY:   Set to \"1\" to force building without sound notification support" | ||||||
| 	@echo "  DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" | 	@echo "  DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" | ||||||
|  | 	@echo "  DISABLE_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_CFLAGS:            Add custom flags to default CFLAGS" | ||||||
| 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | ||||||
| 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | ||||||
| 	@echo "  DESTDIR:                Specify a directory where to store installed files (mainly for packaging purpose)" | 	@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 | .PHONY: help | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ install: $(BUILD_DIR)/toxic | |||||||
| 		if [ ! -e "$(DOC_DIR)/$$f" ]; then \ | 		if [ ! -e "$(DOC_DIR)/$$f" ]; then \ | ||||||
| 			continue ;\ | 			continue ;\ | ||||||
| 		fi ;\ | 		fi ;\ | ||||||
| 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\ | 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\ | ||||||
| 		file=$$section/$$f ;\ | 		file=$$section/$$f ;\ | ||||||
| 		mkdir -p $$section ;\ | 		mkdir -p $$section ;\ | ||||||
| 		install -m 0644 $(DOC_DIR)/$$f $$file ;\ | 		install -m 0644 $(DOC_DIR)/$$f $$file ;\ | ||||||
| @@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic | |||||||
| 		mv temp_file $$file ;\ | 		mv temp_file $$file ;\ | ||||||
| 		sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ | 		sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ | ||||||
| 		mv temp_file $$file ;\ | 		mv temp_file $$file ;\ | ||||||
| 		gzip -f -9 $$file ;\ | 		gzip -f -n -9 $$file ;\ | ||||||
| 	done | 	done | ||||||
|  |  | ||||||
| .PHONY: install | .PHONY: install | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ uninstall: | |||||||
| 	 | 	 | ||||||
| 	@echo "Removing man pages" | 	@echo "Removing man pages" | ||||||
| 	@for f in $(MANFILES) ; do \ | 	@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 ;\ | 		file=$$section/$$f ;\ | ||||||
| 		rm -f $$file $$file.gz ;\ | 		rm -f $$file $$file.gz ;\ | ||||||
| 	done | 	done | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								doc/toxic.1
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								doc/toxic.1
									
									
									
									
									
								
							| @@ -1,13 +1,13 @@ | |||||||
| '\" t | '\" t | ||||||
| .\"     Title: toxic | .\"     Title: toxic | ||||||
| .\"    Author: [see the "AUTHORS" section] | .\"    Author: [see the "AUTHORS" section] | ||||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||||
| .\"      Date: 2015-07-08 | .\"      Date: 2020-05-04 | ||||||
| .\"    Manual: Toxic Manual | .\"    Manual: Toxic Manual | ||||||
| .\"    Source: toxic __VERSION__ | .\"    Source: toxic __VERSION__ | ||||||
| .\"  Language: English | .\"  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 | .\" * Define some portability stuff | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| @@ -70,7 +70,7 @@ Encrypt an unencrypted data file\&. An error will occur if this option is used w | |||||||
| Use specified | Use specified | ||||||
| \fIdata\-file\fR | \fIdata\-file\fR | ||||||
| instead of | instead of | ||||||
| \fI~/\&.config/tox/data\fR | \fI~/\&.config/tox/toxic_profile\&.tox\fR | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \-h, \-\-help | \-h, \-\-help | ||||||
| @@ -78,12 +78,17 @@ instead of | |||||||
| Show help message | Show help message | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
|  | \-l, \-\-logging | ||||||
|  | .RS 4 | ||||||
|  | Enable toxcore logging to stderr | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
| \-n, \-\-nodes nodes\-file | \-n, \-\-nodes nodes\-file | ||||||
| .RS 4 | .RS 4 | ||||||
| Use specified | Use specified | ||||||
| \fInodes\-file\fR | \fInodes\-file\fR | ||||||
| for DHT bootstrap nodes, instead of | for DHT bootstrap nodes instead of | ||||||
| \fI__DATADIR__/DHTnodes\fR | \fI~/\&.config/tox/DHTnodes\&.json\fR | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \-o, \-\-noconnect | \-o, \-\-noconnect | ||||||
| @@ -122,12 +127,14 @@ Unencrypt a data file\&. A warning will appear if this option is used with a dat | |||||||
| .RE | .RE | ||||||
| .SH "FILES" | .SH "FILES" | ||||||
| .PP | .PP | ||||||
| __DATADIR__/DHTnodes | ~/\&.config/tox/DHTnodes\&.json | ||||||
| .RS 4 | .RS 4 | ||||||
| Default list of DHT bootstrap nodes\&. | Default location for list of DHT bootstrap nodes (list obtained from | ||||||
|  | https://nodes\&.tox\&.chat)\&. This list is automatically updated\&. See | ||||||
|  | \fBtoxic\&.conf\fR(5) for details on controlling the update frequency\&. | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| ~/\&.config/tox/data | ~/\&.config/tox/toxic_profile\&.tox | ||||||
| .RS 4 | .RS 4 | ||||||
| Savestate which contains your personal info (nickname, Tox ID, contacts, etc) | Savestate which contains your personal info (nickname, Tox ID, contacts, etc) | ||||||
| .RE | .RE | ||||||
| @@ -144,7 +151,11 @@ Configuration example\&. | |||||||
| .RE | .RE | ||||||
| .SH "BUGS" | .SH "BUGS" | ||||||
| .sp | .sp | ||||||
| Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&. | \-Unicode characters with a width larger than 1 column may cause strange behaviour\&. | ||||||
|  | .sp | ||||||
|  | \-Text occasionally fails to auto\-scroll to the bottom\&. | ||||||
|  | .sp | ||||||
|  | \-Screen flickering sometimes occurs on certain terminals\&. | ||||||
| .SH "AUTHORS" | .SH "AUTHORS" | ||||||
| .sp | .sp | ||||||
| JFreegman <JFreegman@gmail\&.com> | JFreegman <JFreegman@gmail\&.com> | ||||||
| @@ -153,6 +164,6 @@ JFreegman <JFreegman@gmail\&.com> | |||||||
| \fBtoxic\&.conf\fR(5) | \fBtoxic\&.conf\fR(5) | ||||||
| .SH "LINKS" | .SH "LINKS" | ||||||
| .sp | .sp | ||||||
| Project page: https://github\&.com/Tox/toxic | Project page: https://github\&.com/JFreegman/toxic | ||||||
| .sp | .sp | ||||||
| IRC channel: chat\&.freenode\&.net#tox | IRC channel: chat\&.freenode\&.net#tox | ||||||
|   | |||||||
| @@ -35,14 +35,16 @@ OPTIONS | |||||||
|     is used with an encrypted data file. |     is used with an encrypted data file. | ||||||
|  |  | ||||||
| -f, --file data-file:: | -f, --file data-file:: | ||||||
|     Use specified 'data-file' instead of '~/.config/tox/data' |     Use specified 'data-file' instead of '~/.config/tox/toxic_profile.tox' | ||||||
|  |  | ||||||
| -h, --help:: | -h, --help:: | ||||||
|     Show help message |     Show help message | ||||||
|  |  | ||||||
|  | -l, --logging:: | ||||||
|  |     Enable toxcore logging to stderr | ||||||
|  |  | ||||||
| -n, --nodes nodes-file:: | -n, --nodes nodes-file:: | ||||||
|     Use specified 'nodes-file' for DHT bootstrap nodes, instead of |     Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json' | ||||||
|     '{datadir}/DHTnodes' |  | ||||||
|  |  | ||||||
| -o, --noconnect:: | -o, --noconnect:: | ||||||
|     Do not connect to the DHT network |     Do not connect to the DHT network | ||||||
| @@ -68,10 +70,11 @@ OPTIONS | |||||||
|  |  | ||||||
| FILES | FILES | ||||||
| ----- | ----- | ||||||
| {datadir}/DHTnodes:: | ~/.config/tox/DHTnodes.json:: | ||||||
|     Default list of DHT bootstrap nodes. |     Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat). | ||||||
|  |     This list is automatically updated. See *toxic.conf*(5) for details on controlling the update frequency. | ||||||
|  |  | ||||||
| ~/.config/tox/data:: | ~/.config/tox/toxic_profile.tox:: | ||||||
|     Savestate which contains your personal info (nickname, Tox ID, contacts, |     Savestate which contains your personal info (nickname, Tox ID, contacts, | ||||||
|     etc) |     etc) | ||||||
|  |  | ||||||
| @@ -83,9 +86,12 @@ FILES | |||||||
|  |  | ||||||
| BUGS | BUGS | ||||||
| ---- | ---- | ||||||
| Unicode characters with a width larger than 1 column may cause strange | -Unicode characters with a width larger than 1 column may cause strange | ||||||
| behaviour. Expect more bugs and bad behaviour: this software is in a | behaviour. | ||||||
| pre-alpha stage. |  | ||||||
|  | -Text occasionally fails to auto-scroll to the bottom. | ||||||
|  |  | ||||||
|  | -Screen flickering sometimes occurs on certain terminals. | ||||||
|  |  | ||||||
| AUTHORS | AUTHORS | ||||||
| ------- | ------- | ||||||
| @@ -97,6 +103,6 @@ SEE ALSO | |||||||
|  |  | ||||||
| LINKS | LINKS | ||||||
| ----- | ----- | ||||||
| Project page: <https://github.com/Tox/toxic> | Project page: <https://github.com/JFreegman/toxic> | ||||||
|  |  | ||||||
| IRC channel: chat.freenode.net#tox | IRC channel: chat.freenode.net#tox | ||||||
|   | |||||||
							
								
								
									
										126
									
								
								doc/toxic.conf.5
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								doc/toxic.conf.5
									
									
									
									
									
								
							| @@ -1,13 +1,13 @@ | |||||||
| '\" t | '\" t | ||||||
| .\"     Title: toxic.conf | .\"     Title: toxic.conf | ||||||
| .\"    Author: [see the "AUTHORS" section] | .\"    Author: [see the "AUTHORS" section] | ||||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||||
| .\"      Date: 2015-03-28 | .\"      Date: 2020-11-18 | ||||||
| .\"    Manual: Toxic Manual | .\"    Manual: Toxic Manual | ||||||
| .\"    Source: toxic __VERSION__ | .\"    Source: toxic __VERSION__ | ||||||
| .\"  Language: English | .\"  Language: English | ||||||
| .\" | .\" | ||||||
| .TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual" | .TH "TOXIC\&.CONF" "5" "2020\-11\-18" "toxic __VERSION__" "Toxic Manual" | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| .\" * Define some portability stuff | .\" * Define some portability stuff | ||||||
| .\" ----------------------------------------------------------------- | .\" ----------------------------------------------------------------- | ||||||
| @@ -85,7 +85,7 @@ Time format string for logging enclosed by double quotes\&. See | |||||||
| .PP | .PP | ||||||
| \fBalerts\fR | \fBalerts\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Enable or disable terminal alerts on events\&. true or false | Enable or disable acoustic alerts on events\&. true or false | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \fBnative_colors\fR | \fBnative_colors\fR | ||||||
| @@ -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 | Select between native terminal colors and toxic color theme\&. true or false | ||||||
| .RE | .RE | ||||||
| .PP | .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 | \fBautolog\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Enable or disable autologging\&. true or false | 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 | Show welcome message on startup\&. true or false | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
|  | \fBshow_connection_msg\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable friend connection change notifications\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBnodelist_update_freq\fR | ||||||
|  | .RS 4 | ||||||
|  | How often in days to update the DHT nodes list\&. (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 | \fBhistory_size\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Maximum lines for chat window history\&. Integer value\&. (for example: 700) | Maximum lines for chat window history\&. Integer value\&. (for example: 700) | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
|  | \fBnotification_timeout\fR | ||||||
|  | .RS 4 | ||||||
|  | Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000) | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
| \fBline_join\fR | \fBline_join\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&. | 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 | \fBmplex_away_note\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&. | Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&. | ||||||
|  | .sp | ||||||
|  | .if n \{\ | ||||||
|  | .RS 4 | ||||||
|  | .\} | ||||||
|  | .nf | ||||||
|  | The following options control whether to output a terminal bell on certain events\&. | ||||||
|  | Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&. | ||||||
|  | These options don\*(Aqt affect the "alerts" option\&. | ||||||
|  | .fi | ||||||
|  | .if n \{\ | ||||||
|  | .RE | ||||||
|  | .\} | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBbell_on_message\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable the terminal bell when receiving a message\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBbell_on_filetrans\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable the terminal bell when receiving a filetransfer\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBbell_on_filetrans_accept\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable the terminal bell when a filetransfer was accepted\&. true or false | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
|  | \fBbell_on_invite\fR | ||||||
|  | .RS 4 | ||||||
|  | Enable/Disable the terminal bell when receiving a group/call invite\&. true or false | ||||||
| .RE | .RE | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| @@ -165,9 +237,24 @@ Audio output device\&. Integer value\&. Number corresponds to | |||||||
| /lsdev out | /lsdev out | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \fBVAD_treshold\fR | \fBVAD_threshold\fR | ||||||
| .RS 4 | .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 | ||||||
| .RE | .RE | ||||||
| .PP | .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) | Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB) | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
|  | \fBautorun_path\fR | ||||||
|  | .RS 4 | ||||||
|  | Path for any scripts that should be run on startup | ||||||
|  | .RE | ||||||
|  | .PP | ||||||
| \fBchatlogs_path\fR | \fBchatlogs_path\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. | Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. | ||||||
| .RE | .RE | ||||||
|  | .PP | ||||||
|  | \fBpassword_eval\fR | ||||||
|  | .RS 4 | ||||||
|  | Replace password prompt by running this command and using its output as the password\&. | ||||||
|  | .RE | ||||||
| .RE | .RE | ||||||
| .PP | .PP | ||||||
| \fBsounds\fR | \fBsounds\fR | ||||||
| @@ -289,20 +386,15 @@ Key combination to scroll half page down\&. | |||||||
| Key combination to scroll to page bottom\&. | Key combination to scroll to page bottom\&. | ||||||
| .RE | .RE | ||||||
| .PP | .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 | \fBtoggle_peerlist\fR | ||||||
| .RS 4 | .RS 4 | ||||||
| Toggle the peer list on and off\&. | Toggle the peer list on and off\&. | ||||||
| .RE | .RE | ||||||
|  | .PP | ||||||
|  | \fBtoggle_paste_mode\fR | ||||||
|  | .RS 4 | ||||||
|  | Toggle treating linebreaks as enter key press\&. | ||||||
|  | .RE | ||||||
| .RE | .RE | ||||||
| .SH "FILES" | .SH "FILES" | ||||||
| .PP | .PP | ||||||
| @@ -320,7 +412,7 @@ Configuration example\&. | |||||||
| \fBtoxic\fR(1) | \fBtoxic\fR(1) | ||||||
| .SH "RESOURCES" | .SH "RESOURCES" | ||||||
| .sp | .sp | ||||||
| Project page: https://github\&.com/Tox/toxic | Project page: https://github\&.com/JFreegman/toxic | ||||||
| .sp | .sp | ||||||
| IRC channel: chat\&.freenode\&.net#tox | IRC channel: chat\&.freenode\&.net#tox | ||||||
| .SH "AUTHORS" | .SH "AUTHORS" | ||||||
|   | |||||||
| @@ -55,11 +55,23 @@ OPTIONS | |||||||
|         See *date*(1) |         See *date*(1) | ||||||
|  |  | ||||||
|     *alerts*;; |     *alerts*;; | ||||||
|         Enable or disable terminal alerts on events. true or false |         Enable or disable acoustic alerts on events. true or false | ||||||
|  |  | ||||||
|     *native_colors*;; |     *native_colors*;; | ||||||
|         Select between native terminal colors and toxic color theme. true or false |         Select between native terminal colors and toxic color theme. true or false | ||||||
|  |  | ||||||
|  |     *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*;; |     *autolog*;; | ||||||
|         Enable or disable autologging. true or false |         Enable or disable autologging. true or false | ||||||
|  |  | ||||||
| @@ -72,9 +84,21 @@ OPTIONS | |||||||
|     *show_welcome_msg*;; |     *show_welcome_msg*;; | ||||||
|         Show welcome message on startup. true or false |         Show welcome message on startup. true or false | ||||||
|  |  | ||||||
|  |     *show_connection_msg*;; | ||||||
|  |         Enable friend connection change notifications. true or false | ||||||
|  |  | ||||||
|  |     *nodelist_update_freq*;; | ||||||
|  |         How often in days to update the DHT nodes list. (integer; 0 to disable) | ||||||
|  |  | ||||||
|  |     *autosave_freq*;; | ||||||
|  |         How often in seconds to auto-save the Tox data file. (integer; 0 to disable) | ||||||
|  |  | ||||||
|     *history_size*;; |     *history_size*;; | ||||||
|         Maximum lines for chat window history. Integer value. (for example: 700) |         Maximum lines for chat window history. Integer value. (for example: 700) | ||||||
|  |  | ||||||
|  |     *notification_timeout*;; | ||||||
|  |         Time in milliseconds to display a notification. Integer value. (for example: 3000) | ||||||
|  |  | ||||||
|     *line_join*;; |     *line_join*;; | ||||||
|         Indicator for when someone connects or joins a group. |         Indicator for when someone connects or joins a group. | ||||||
|         Three characters max for line_ settings. |         Three characters max for line_ settings. | ||||||
| @@ -97,6 +121,23 @@ OPTIONS | |||||||
|         detach. When attaching, the status message is set back to the original |         detach. When attaching, the status message is set back to the original | ||||||
|         value. |         value. | ||||||
|  |  | ||||||
|  |     The following options control whether to output a terminal bell on certain events. | ||||||
|  |     Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window. | ||||||
|  |     These options don't affect the "alerts" option. | ||||||
|  |  | ||||||
|  |     *bell_on_message*;; | ||||||
|  |         Enable/Disable the terminal bell when receiving a message. true or false | ||||||
|  |  | ||||||
|  |     *bell_on_filetrans*;; | ||||||
|  |         Enable/Disable the terminal bell when receiving a filetransfer. true or false | ||||||
|  |  | ||||||
|  |     *bell_on_filetrans_accept*;; | ||||||
|  |         Enable/Disable the terminal bell when a filetransfer was accepted. true or false | ||||||
|  |  | ||||||
|  |     *bell_on_invite*;; | ||||||
|  |         Enable/Disable the terminal bell when receiving a group/call invite. true or false | ||||||
|  |  | ||||||
|  |  | ||||||
| *audio*:: | *audio*:: | ||||||
|     Configuration related to audio devices. |     Configuration related to audio devices. | ||||||
|  |  | ||||||
| @@ -106,9 +147,18 @@ OPTIONS | |||||||
|     *output_device*;; |     *output_device*;; | ||||||
|         Audio output device. Integer value. Number corresponds to `/lsdev out` |         Audio output device. Integer value. Number corresponds to `/lsdev out` | ||||||
|  |  | ||||||
|     *VAD_treshold*;; |     *VAD_threshold*;; | ||||||
|         Voice Activity Detection treshold.  Float value. Recommended values are |         Voice Activity Detection threshold.  Float value. Recommended values are | ||||||
|         around 40.0 |         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*:: | *tox*:: | ||||||
|     Configuration related to paths. |     Configuration related to paths. | ||||||
| @@ -120,9 +170,16 @@ OPTIONS | |||||||
|     *avatar_path*;; |     *avatar_path*;; | ||||||
|         Path for your avatar (file must be a .png and cannot exceed 16.3 KiB) |         Path for your avatar (file must be a .png and cannot exceed 16.3 KiB) | ||||||
|  |  | ||||||
|  |     *autorun_path*;; | ||||||
|  |         Path for any scripts that should be run on startup | ||||||
|  |  | ||||||
|     *chatlogs_path*;; |     *chatlogs_path*;; | ||||||
|         Default path for chatlogs. String value. Absolute path for chatlog files. |         Default path for chatlogs. String value. Absolute path for chatlog files. | ||||||
|  |  | ||||||
|  |     *password_eval*;; | ||||||
|  |         Replace password prompt by running this command and using its output as | ||||||
|  | 	the password. | ||||||
|  |  | ||||||
| *sounds*:: | *sounds*:: | ||||||
|     Configuration related to notification sounds. |     Configuration related to notification sounds. | ||||||
|     Special value "silent" can be used to disable a specific notification. + |     Special value "silent" can be used to disable a specific notification. + | ||||||
| @@ -185,15 +242,12 @@ OPTIONS | |||||||
|     *page_bottom*;; |     *page_bottom*;; | ||||||
|         Key combination to scroll to 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_peerlist*;; | ||||||
|         Toggle the peer list on and off. |         Toggle the peer list on and off. | ||||||
|  |  | ||||||
|  |     *toggle_paste_mode*;; | ||||||
|  |         Toggle treating linebreaks as enter key press. | ||||||
|  |  | ||||||
|  |  | ||||||
| FILES | FILES | ||||||
| ----- | ----- | ||||||
| @@ -211,7 +265,7 @@ SEE ALSO | |||||||
|  |  | ||||||
| RESOURCES | RESOURCES | ||||||
| --------- | --------- | ||||||
| Project page: <https://github.com/Tox/toxic> | Project page: <https://github.com/JFreegman/toxic> | ||||||
|  |  | ||||||
| IRC channel:  chat.freenode.net#tox | IRC channel:  chat.freenode.net#tox | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,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 | toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,20 +5,44 @@ ui = { | |||||||
|   // true to enable timestamps, false to disable |   // true to enable timestamps, false to disable | ||||||
|   timestamps=true; |   timestamps=true; | ||||||
|  |  | ||||||
|   // true to enable terminal alerts on messages, false to disable |   // true to enable acoustic alerts on messages, false to disable | ||||||
|   alerts=true; |   alerts=true; | ||||||
|  |  | ||||||
|  |   // Output a bell when receiving a message (see manpage) | ||||||
|  |   bell_on_message=true; | ||||||
|  |  | ||||||
|  |   // Output a bell when receiving a filetransfer (see manpage) | ||||||
|  |   bell_on_filetrans=true; | ||||||
|  |  | ||||||
|  |   // Don't output a bell when a filetransfer was accepted (see manpage) | ||||||
|  |   bell_on_filetrans_accept=false; | ||||||
|  |  | ||||||
|  |   // Output a bell when receiving a group/call invite (see manpage) | ||||||
|  |   bell_on_invite=true; | ||||||
|  |  | ||||||
|   // true to use native terminal colours, false to use toxic default colour theme |   // true to use native terminal colours, false to use toxic default colour theme | ||||||
|   native_colors=false; |   native_colors=false; | ||||||
|  |  | ||||||
|  |   // 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 |   // true to enable autologging, false to disable | ||||||
|   autolog=false; |   autolog=false; | ||||||
|  |  | ||||||
|   // 24 or 12 hour time |   // 24 or 12 hour time | ||||||
|   time_format=24; |   time_format=24; | ||||||
|  |  | ||||||
|   // timestamp format string according to date/strftime format. Overrides time_format setting |   // Timestamp format string according to date/strftime format. Overrides time_format setting | ||||||
|   timestamp_format="%H:%M:%S"; |   timestamp_format="%H:%M"; | ||||||
|  |  | ||||||
|   // true to show you when others are typing a message in 1-on-1 chats |   // true to show you when others are typing a message in 1-on-1 chats | ||||||
|   show_typing_other=true; |   show_typing_other=true; | ||||||
| @@ -29,20 +53,32 @@ ui = { | |||||||
|   // true to show the welcome message on startup |   // true to show the welcome message on startup | ||||||
|   show_welcome_msg=true; |   show_welcome_msg=true; | ||||||
|  |  | ||||||
|  |   // true to show friend connection change messages on the home screen | ||||||
|  |   show_connection_msg=true; | ||||||
|  |  | ||||||
|  |   // How often in days to update the DHT nodes list. (0 to disable updates) | ||||||
|  |   nodeslist_update_freq=7; | ||||||
|  |  | ||||||
|  |   // 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 |   // maximum lines for chat window history | ||||||
|   history_size=700; |   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="-->"; |   line_join="-->"; | ||||||
|  |  | ||||||
|   // Indicator for display when someone disconnects or leaves a group. |   // Indicator for display when someone disconnects or leaves a group | ||||||
|   line_quit="<--"; |   line_quit="<--"; | ||||||
|  |  | ||||||
|   // Indicator for alert messages. |   // Indicator for alert messages. | ||||||
|   line_alert="-!-"; |   line_alert="-!-"; | ||||||
|  |  | ||||||
|   // Indicator for normal messages. |   // Indicator for normal messages. | ||||||
|   line_normal="---"; |   line_normal="-"; | ||||||
|  |  | ||||||
|   // true to change status based on screen/tmux attach/detach, false to disable |   // true to change status based on screen/tmux attach/detach, false to disable | ||||||
|   mplex_away=true; |   mplex_away=true; | ||||||
| @@ -58,8 +94,17 @@ audio = { | |||||||
|   // preferred audio output device; numbers correspond to /lsdev out |   // preferred audio output device; numbers correspond to /lsdev out | ||||||
|   output_device=0; |   output_device=0; | ||||||
|  |  | ||||||
|   // default VAD treshold; float (recommended values are around 40) |   // default VAD threshold; float (recommended values are 1.0-40.0) | ||||||
|   VAD_treshold=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 = { | tox = { | ||||||
| @@ -69,6 +114,9 @@ tox = { | |||||||
|   // Path for your avatar (file must be a .png and cannot exceed 64 KiB) |   // Path for your avatar (file must be a .png and cannot exceed 64 KiB) | ||||||
|   // avatar_path="/home/USERNAME/Pictures/youravatar.png"; |   // avatar_path="/home/USERNAME/Pictures/youravatar.png"; | ||||||
|  |  | ||||||
|  |   // Path for scripts that should be run on startup | ||||||
|  |   // autorun_path="/home/USERNAME/toxic_scripts/"; | ||||||
|  |  | ||||||
|   // Path for chatlogs |   // Path for chatlogs | ||||||
|   // chatlogs_path="/home/USERNAME/toxic_chatlogs/"; |   // chatlogs_path="/home/USERNAME/toxic_chatlogs/"; | ||||||
| }; | }; | ||||||
| @@ -86,7 +134,7 @@ sounds = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| // Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive) | // Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive) | ||||||
| // Note: All printable keys register as input | // Note: Ctrl+M does not work | ||||||
| keys = { | keys = { | ||||||
|   next_tab="Ctrl+P"; |   next_tab="Ctrl+P"; | ||||||
|   prev_tab="Ctrl+O"; |   prev_tab="Ctrl+O"; | ||||||
| @@ -95,8 +143,6 @@ keys = { | |||||||
|   half_page_up="Ctrl+F"; |   half_page_up="Ctrl+F"; | ||||||
|   half_page_down="Ctrl+V"; |   half_page_down="Ctrl+V"; | ||||||
|   page_bottom="Ctrl+H"; |   page_bottom="Ctrl+H"; | ||||||
|   peer_list_up="Ctrl+["; |   toggle_peerlist="Ctrl+B"; | ||||||
|   peer_list_down="Ctrl+]"; |   toggle_paste_mode="Ctrl+T"; | ||||||
|   toggle_peerlist="Ctrl+b"; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										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 */ | ||||||
							
								
								
									
										967
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										967
									
								
								src/audio_call.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -27,9 +27,7 @@ | |||||||
|  |  | ||||||
| #include "audio_device.h" | #include "audio_device.h" | ||||||
|  |  | ||||||
| #define MAX_CALLS 10 | typedef enum AudioError { | ||||||
|  |  | ||||||
| typedef enum _AudioError { |  | ||||||
|     ae_None = 0, |     ae_None = 0, | ||||||
|     ae_StartingCaptureDevice = 1 << 0, |     ae_StartingCaptureDevice = 1 << 0, | ||||||
|     ae_StartingOutputDevice = 1 << 1, |     ae_StartingOutputDevice = 1 << 1, | ||||||
| @@ -37,7 +35,7 @@ typedef enum _AudioError { | |||||||
| } AudioError; | } AudioError; | ||||||
|  |  | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
| typedef enum _VideoError { | typedef enum VideoError { | ||||||
|     ve_None = 0, |     ve_None = 0, | ||||||
|     ve_StartingCaptureDevice = 1 << 0, |     ve_StartingCaptureDevice = 1 << 0, | ||||||
|     ve_StartingOutputDevice = 1 << 1, |     ve_StartingOutputDevice = 1 << 1, | ||||||
| @@ -46,14 +44,27 @@ typedef enum _VideoError { | |||||||
|  |  | ||||||
| #endif /* VIDEO */ | #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 { | typedef struct Call { | ||||||
|     pthread_t ttid; /* Transmission thread id */ |     CallStatus status; | ||||||
|     bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */ |     uint32_t state; /* ToxAV call state, valid when `status == cs_Active` */ | ||||||
|     uint32_t in_idx, out_idx; /* Audio Index */ |     uint32_t in_idx, out_idx; /* Audio device index, or -1 if not open */ | ||||||
| #ifdef VIDEO |     uint32_t audio_bit_rate; /* Bit rate for sending audio */ | ||||||
|     uint32_t vin_idx, vout_idx; /* Video Index */ |  | ||||||
| #endif /* VIDEO */ |     uint32_t vin_idx, vout_idx; /* Video device index, or -1 if not open */ | ||||||
|     pthread_mutex_t mutex; |     uint32_t video_width, video_height; | ||||||
|  |     uint32_t video_bit_rate; /* Bit rate for sending video; 0 for no video */ | ||||||
| } Call; | } Call; | ||||||
|  |  | ||||||
| struct CallControl { | struct CallControl { | ||||||
| @@ -65,29 +76,34 @@ struct CallControl { | |||||||
|     ToxAV *av; |     ToxAV *av; | ||||||
|     ToxWindow *prompt; |     ToxWindow *prompt; | ||||||
|  |  | ||||||
|     Call calls[MAX_CALLS]; |     Call *calls; | ||||||
|     uint32_t call_state; |     uint32_t max_calls; | ||||||
|     bool pending_call; |  | ||||||
|     bool audio_enabled; |     bool audio_enabled; | ||||||
|     bool video_enabled; |     bool video_enabled; | ||||||
|  |  | ||||||
|     uint32_t audio_bit_rate; |  | ||||||
|     int32_t audio_frame_duration; |     int32_t audio_frame_duration; | ||||||
|     uint32_t audio_sample_rate; |     uint32_t audio_sample_rate; | ||||||
|     uint8_t audio_channels; |     uint8_t audio_channels; | ||||||
|  |     uint32_t default_audio_bit_rate; | ||||||
|  |  | ||||||
|     uint32_t video_bit_rate; |  | ||||||
|     int32_t video_frame_duration; |     int32_t video_frame_duration; | ||||||
|  |     uint32_t default_video_width, default_video_height; | ||||||
|  |     uint32_t default_video_bit_rate; | ||||||
|  | }; | ||||||
|  |  | ||||||
| } CallControl; | extern struct CallControl CallControl; | ||||||
|  |  | ||||||
| struct CallControl CallControl; |  | ||||||
|  |  | ||||||
| /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | ||||||
| ToxAV *init_audio(ToxWindow *self, Tox *tox); | ToxAV *init_audio(ToxWindow *self, Tox *tox); | ||||||
| void terminate_audio(); | void terminate_audio(void); | ||||||
| int start_transmission(ToxWindow *self, Call *call); |  | ||||||
| int stop_transmission(Call *call, uint32_t friend_number); | bool init_call(Call *call); | ||||||
|  |  | ||||||
|  | void place_call(ToxWindow *self); | ||||||
| void stop_current_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 */ | #endif /* AUDIO_CALL_H */ | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -30,8 +30,9 @@ | |||||||
| #define AUDIO_DEVICE_H | #define AUDIO_DEVICE_H | ||||||
|  |  | ||||||
| #define OPENAL_BUFS 5 | #define OPENAL_BUFS 5 | ||||||
|  | #define MAX_OPENAL_DEVICES 32 | ||||||
| #define MAX_DEVICES 32 | #define MAX_DEVICES 32 | ||||||
| #include <inttypes.h> |  | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| typedef enum DeviceType { | typedef enum DeviceType { | ||||||
| @@ -55,37 +56,42 @@ typedef enum 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(void); | ||||||
| DeviceError init_devices(ToxAV* av); |  | ||||||
| #else |  | ||||||
| DeviceError init_devices(); |  | ||||||
| #endif /* AUDIO */ |  | ||||||
|  |  | ||||||
| DeviceError terminate_devices(); | void get_al_device_names(void); | ||||||
|  | DeviceError terminate_devices(void); | ||||||
| /* Callback handles ready data from INPUT device */ |  | ||||||
| DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD); |  | ||||||
| void* get_device_callback_data(uint32_t device_idx); |  | ||||||
|  |  | ||||||
| /* toggle device mute */ | /* toggle device mute */ | ||||||
| DeviceError device_mute(DeviceType type, uint32_t device_idx); | DeviceError device_mute(DeviceType type, uint32_t device_idx); | ||||||
|  |  | ||||||
| #ifdef AUDIO | bool device_is_muted(DeviceType type, uint32_t device_idx); | ||||||
| DeviceError device_set_VAD_treshold(uint32_t device_idx, float value); |  | ||||||
| #endif | 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 */ | /* 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 */ | /* Stop device */ | ||||||
| DeviceError close_device(DeviceType type, uint32_t device_idx); | DeviceError close_device(DeviceType type, uint32_t device_idx); | ||||||
|  |  | ||||||
| /* Write data to device */ | /* Write data to output device */ | ||||||
| DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels); | DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels, | ||||||
|  |                       uint32_t sample_rate); | ||||||
|  |  | ||||||
| void print_devices(ToxWindow* self, DeviceType type); | /* return current input volume as float in range 0.0-100.0 */ | ||||||
| void get_primary_device_name(DeviceType type, char *buf, int size); | float get_input_volume(void); | ||||||
|  |  | ||||||
|  | void print_al_devices(ToxWindow *self, DeviceType type); | ||||||
|  |  | ||||||
| DeviceError selection_valid(DeviceType type, int32_t selection); | DeviceError selection_valid(DeviceType type, int32_t selection); | ||||||
| #endif /* AUDIO_DEVICE_H */ | #endif /* AUDIO_DEVICE_H */ | ||||||
|   | |||||||
| @@ -20,110 +20,120 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <limits.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <limits.h> |  | ||||||
|  |  | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <sys/dir.h> | #include <sys/dir.h> | ||||||
| #else | #else | ||||||
| #include <dirent.h> | #include <dirent.h> | ||||||
| #endif /* ifdef __APPLE__ */ | #endif /* __APPLE__ */ | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "line_info.h" |  | ||||||
| #include "execute.h" |  | ||||||
| #include "configdir.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); |         execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const char *L = (char *) list; |     for (size_t i = 0; i < n_matches; ++i) { | ||||||
|     int 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, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]); |  | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");   /* formatting */ |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* puts match in match buffer. if more than one match, add first n chars that are identical. | /* puts match in match buffer. if more than one match, add first n chars that are identical. | ||||||
|    e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */ |  * e.g. if matches contains: [foo, foobar, foe] we put fo in match. | ||||||
| static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n) |  * | ||||||
|  |  * Returns the length of the match. | ||||||
|  |  */ | ||||||
|  | static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, const char **matches, size_t n_items, | ||||||
|  |                             size_t max_size) | ||||||
| { | { | ||||||
|     if (n == 1) { |     UNUSED_VAR(self); | ||||||
|         strcpy(match, matches[0]); |  | ||||||
|         return; |     if (n_items == 1) { | ||||||
|  |         return snprintf(match, match_sz, "%s", matches[0]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int i; |     for (size_t i = 0; i < max_size; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_STR_SIZE; ++i) { |  | ||||||
|         char ch1 = matches[0][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]; |             char ch2 = matches[j][i]; | ||||||
|  |  | ||||||
|             if (ch1 != ch2 || !ch1) { |             if (ch1 != ch2 || !ch1) { | ||||||
|                 strcpy(match, matches[0]); |                 snprintf(match, match_sz, "%s", matches[0]); | ||||||
|                 match[i] = '\0'; |                 match[i] = '\0'; | ||||||
|                 return; |                 return i; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     strcpy(match, matches[0]); |     return snprintf(match, match_sz, "%s", matches[0]); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* looks for all instances in list that begin with the last entered word in line according to pos, | /* | ||||||
|    then fills line with the complete word. e.g. "Hello jo" would complete the line |  * Looks for all instances in list that begin with the last entered word in line according to pos, | ||||||
|    with "Hello john". If multiple matches, prints out all the matches and semi-completes line. |  * 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. |  * `list` is a pointer to `n_items` strings. Each string in the list must be <= MAX_STR_SIZE. | ||||||
|  |  * | ||||||
|    Returns the difference between the old len and new len of line on success, -1 if error */ |  * dir_search should be true if the line being completed is a file path. | ||||||
| int complete_line(ToxWindow *self, const void *list, int n_items, int size) |  * | ||||||
|  |  * 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; |     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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ctx->len >= MAX_STR_SIZE) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const char *L = (char *) list; |  | ||||||
|     const char *endchrs = " "; |     const char *endchrs = " "; | ||||||
|     char ubuf[MAX_STR_SIZE]; |     char ubuf[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|     /* work with multibyte string copy of buf for simplicity */ |     /* 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; |         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 */ |     /* isolate substring from space behind pos to pos */ | ||||||
|     char tmp[MAX_STR_SIZE]; |     char tmp[MAX_STR_SIZE]; | ||||||
|     snprintf(tmp, sizeof(tmp), "%s", ubuf); |     memcpy(tmp, ubuf, ctx->pos); | ||||||
|     tmp[ctx->pos] = '\0'; |     tmp[ctx->pos] = 0; | ||||||
|  |  | ||||||
|     const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' '); |     const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' '); | ||||||
|     char *sub = calloc(1, strlen(ubuf) + 1); |     char *sub = calloc(1, strlen(ubuf) + 1); | ||||||
|  |  | ||||||
|     if (sub == NULL) |     if (sub == NULL) { | ||||||
|         exit_toxic_err("failed in complete_line", FATALERR_MEMORY); |         exit_toxic_err("failed in complete_line_helper", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!s && !dir_search) { |     if (!s && !dir_search) { | ||||||
|         strcpy(sub, tmp); |         strcpy(sub, tmp); | ||||||
|  |  | ||||||
|         if (sub[0] != '/') |         if (sub[0] != '/') { | ||||||
|             endchrs = ": "; |             endchrs = ": "; | ||||||
|  |         } | ||||||
|     } else if (s) { |     } else if (s) { | ||||||
|         strcpy(sub, &s[1]); |         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 sub_len = strlen(sub); | ||||||
|             int si = char_rfind(sub, '/', sub_len); |             int si = char_rfind(sub, '/', sub_len); | ||||||
|  |  | ||||||
|             if (si || *sub == '/') |             if (si || *sub == '/') { | ||||||
|                 memmove(sub, &sub[si + 1], sub_len - si); |                 memmove(sub, &sub[si + 1], sub_len - si); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!sub[0]) { |     if (!sub[0] && !(dir_search && n_items == 1)) { | ||||||
|         free(sub); |         free(sub); | ||||||
|         return -1; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int s_len = strlen(sub); |     int s_len = strlen(sub); | ||||||
|     int n_matches = 0; |     size_t n_matches = 0; | ||||||
|     char matches[n_items][MAX_STR_SIZE]; |  | ||||||
|     int i = 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 */ |     /* put all list matches in matches array */ | ||||||
|     for (i = 0; i < n_items; ++i) { |     for (size_t i = 0; i < n_items; ++i) { | ||||||
|         char str[MAX_CMDNAME_SIZE + 1]; |         if (strncasecmp(list[i], sub, s_len) == 0) { | ||||||
|         snprintf(str, sizeof(str), "%s", &L[i * size]); |             snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]); | ||||||
|  |         } | ||||||
|         if (strncasecmp(str, sub, s_len) == 0) |  | ||||||
|             strcpy(matches[n_matches++], str); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     free(sub); |     free(sub); | ||||||
|  |  | ||||||
|     if (!n_matches) |     if (!n_matches) { | ||||||
|  |         free_ptr_array((void **) matches); | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!dir_search && n_matches > 1) |     if (!dir_search && n_matches > 1) { | ||||||
|         print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); |         print_ac_matches(self, NULL, matches, n_matches); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char match[MAX_STR_SIZE]; |     char match[MAX_STR_SIZE]; | ||||||
|     get_str_match(self, match, matches, n_matches); |     size_t match_len = get_str_match(self, match, sizeof(match), (const char **) matches, n_matches, MAX_STR_SIZE); | ||||||
|     size_t match_len = strlen(match); |  | ||||||
|  |     free_ptr_array((void **) matches); | ||||||
|  |  | ||||||
|  |     if (match_len == 0) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (dir_search) { |     if (dir_search) { | ||||||
|         if (n_matches == 1) |         if (n_matches == 1) { | ||||||
|             endchrs = char_rfind(match, '.', match_len) ? "\"" : "/"; |             endchrs = char_rfind(match, '.', match_len) ? "" : "/"; | ||||||
|         else |         } else { | ||||||
|             endchrs = ""; |             endchrs = ""; | ||||||
|  |         } | ||||||
|     } else if (n_matches > 1) { |     } else if (n_matches > 1) { | ||||||
|         endchrs = ""; |         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 strt = ctx->pos - s_len; | ||||||
|     int diff = match_len - s_len + n_endchrs; |     int diff = match_len - s_len + n_endchrs; | ||||||
|  |  | ||||||
|     if (ctx->len + diff >= MAX_STR_SIZE) |     if (ctx->len + diff >= MAX_STR_SIZE) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char tmpend[MAX_STR_SIZE]; |     char tmpend[MAX_STR_SIZE]; | ||||||
|     snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]); |     snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]); | ||||||
|  |  | ||||||
|     if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) |     if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     strcpy(&ubuf[strt], match); |     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], endchrs); | ||||||
|     strcpy(&ubuf[strt + match_len + n_endchrs], tmpend); |     strcpy(&ubuf[strt + match_len + n_endchrs], tmpend); | ||||||
|  |  | ||||||
|     /* convert to widechar and copy back to original buf */ |     /* convert to widechar and copy back to original buf */ | ||||||
|     wchar_t newbuf[MAX_STR_SIZE]; |     wchar_t newbuf[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|     if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) |     if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     wcscpy(ctx->line, newbuf); |     wcscpy(ctx->line, newbuf); | ||||||
|  |  | ||||||
| @@ -208,54 +259,82 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | |||||||
|     return diff; |     return diff; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* transforms a tab complete starting with the shorthand "~" into the full home directory.*/ | int complete_line(ToxWindow *self, const char **list, size_t n_items) | ||||||
| static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen) | { | ||||||
|  |     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; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     char homedir[MAX_STR_SIZE] = {0}; |     char homedir[MAX_STR_SIZE] = {0}; | ||||||
|     get_home_dir(homedir, sizeof(homedir)); |     get_home_dir(homedir, sizeof(homedir)); | ||||||
|  |  | ||||||
|     char newline[MAX_STR_SIZE]; |     char newline[MAX_STR_SIZE + 1]; | ||||||
|     snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1); |     snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1); | ||||||
|     snprintf(path, pathsize, "%s", &newline[cmdlen]); |     snprintf(path, pathsize, "%s", &newline[cmdlen - 1]); | ||||||
|  |  | ||||||
|     wchar_t wline[MAX_STR_SIZE]; |     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; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int newlen = wcslen(wline); |     int newlen = wcslen(wline); | ||||||
|  |  | ||||||
|     if (ctx->len + newlen >= MAX_STR_SIZE) |     if (ctx->len + newlen >= MAX_STR_SIZE) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     wmemcpy(ctx->line, wline, newlen + 1); |     wmemcpy(ctx->line, wline, newlen + 1); | ||||||
|     ctx->pos = newlen; |     ctx->pos = newlen; | ||||||
|     ctx->len = ctx->pos; |     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 strncmp(s, p, p_len) == 0 && strcmp(".", s) != 0 && strcmp("..", s) != 0; | ||||||
|     return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ | } | ||||||
| #define MAX_DIRS 512 |  | ||||||
|  |  | ||||||
|  | /* 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) | int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||||
| { | { | ||||||
|     char b_path[MAX_STR_SIZE]; |     char b_path[MAX_STR_SIZE + 1]; | ||||||
|     char b_name[MAX_STR_SIZE]; |     char b_name[MAX_STR_SIZE + 1]; | ||||||
|     char b_cmd[MAX_STR_SIZE]; |     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; |         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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (b_path[0] == '~') |     if (b_path[0] == '~') { | ||||||
|         complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2); |         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)); |     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[0] = '.'; | ||||||
|         b_path[1] = '\0'; |         b_path[1] = '\0'; | ||||||
|     } else if (!si && b_path[0] != '/') {    /* look for matches in pwd */ |     } else if (!si && b_path[0] != '/') {    /* look for matches in pwd */ | ||||||
|         char tmp[MAX_STR_SIZE]; |         memmove(b_path + 1, b_path, sizeof(b_path) - 1); | ||||||
|         snprintf(tmp, sizeof(tmp), ".%s", b_path); |         b_path[0] = '.'; | ||||||
|         snprintf(b_path, sizeof(b_path), "%s", tmp); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); |     snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); | ||||||
|     b_path[si + 1] = '\0'; |     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); |     DIR *dp = opendir(b_path); | ||||||
|  |  | ||||||
|     if (dp == NULL) |     if (dp == NULL) { | ||||||
|         return -1; |         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; |     struct dirent *entry; | ||||||
|  |  | ||||||
|     int dircount = 0; |     int dircount = 0; | ||||||
|  |  | ||||||
|     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { |     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { | ||||||
|         if (strncmp(entry->d_name, b_name, b_name_len) == 0 |         if (is_partial_match(entry->d_name, b_name, b_name_len)) { | ||||||
|                                 && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { |             snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name); | ||||||
|             snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name); |  | ||||||
|             ++dircount; |             ++dircount; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     closedir(dp); |     closedir(dp); | ||||||
|  |  | ||||||
|     if (dircount == 0) |     if (dircount == 0) { | ||||||
|  |         free_ptr_array((void **) dirnames); | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (dircount > 1) { |     if (dircount > 1) { | ||||||
|         qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr); |         qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper); | ||||||
|         print_matches(self, m, dirnames, dircount, NAME_MAX); |         print_ac_matches(self, m, dirnames, dircount); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return complete_line(self, dirnames, dircount, NAME_MAX); |     int ret = complete_path(self, (const char **) dirnames, dircount); | ||||||
|  |  | ||||||
|  |     free_ptr_array((void **) dirnames); | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,20 +23,30 @@ | |||||||
| #ifndef AUTOCOMPLETE_H | #ifndef AUTOCOMPLETE_H | ||||||
| #define AUTOCOMPLETE_H | #define AUTOCOMPLETE_H | ||||||
|  |  | ||||||
| /* looks for all instances in list that begin with the last entered word in line according to pos, | #include "windows.h" | ||||||
|    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. |  * 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 */ | /* Attempts to match /command "<incomplete-dir>" line to matching directories. | ||||||
| int complete_line(ToxWindow *self, const void *list, int n_items, int size); |  * If there is only one match the line is auto-completed. | ||||||
|  |  * | ||||||
| /*  attempts to match /command "<incomplete-dir>" line to matching directories. |  * Returns the diff between old len and new len of ctx->line on success. | ||||||
|  |  * Returns -1 if no matches or more than one match. | ||||||
|     if only one match, auto-complete line. |  */ | ||||||
|     return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ |  | ||||||
| int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd); | 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 <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #include "misc_tools.h" | #include "avatars.h" | ||||||
| #include "file_transfers.h" | #include "file_transfers.h" | ||||||
| #include "friendlist.h" | #include "friendlist.h" | ||||||
| #include "avatars.h" | #include "misc_tools.h" | ||||||
|  |  | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
|  |  | ||||||
| @@ -39,39 +39,76 @@ static struct Avatar { | |||||||
|     off_t size; |     off_t size; | ||||||
| } Avatar; | } 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) | 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 0 on success. | ||||||
|  * Returns -1 on failure. |  * 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; |     Tox_Err_File_Send err; | ||||||
|     uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, |     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); |                                         NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); | ||||||
|     if (Avatar.size == 0) |  | ||||||
|  |     if (Avatar.size == 0) { | ||||||
|         return 0; |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FILE_SEND_OK) { |     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; |         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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ft->file = fopen(Avatar.path, "r"); |     ft->file = fopen(Avatar.path, "r"); | ||||||
|  |  | ||||||
|     if (ft->file == NULL) |     if (ft->file == NULL) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name); |     snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name); | ||||||
|     ft->file_size = Avatar.size; |     ft->file_size = Avatar.size; | ||||||
| @@ -82,13 +119,12 @@ int avatar_send(Tox *m, uint32_t friendnum) | |||||||
| /* Sends avatar to all friends */ | /* Sends avatar to all friends */ | ||||||
| static void avatar_send_all(Tox *m) | static void avatar_send_all(Tox *m) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < Friends.max_idx; ++i) { | ||||||
|  |         if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) { | ||||||
|     for (i = 0; i < Friends.max_idx; ++i) { |  | ||||||
|         if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) |  | ||||||
|             avatar_send(m, Friends.list[i].num); |             avatar_send(m, Friends.list[i].num); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Sets avatar to path and sends it to all online contacts. | /* 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) | 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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     FILE *fp = fopen(path, "rb"); |     FILE *fp = fopen(path, "rb"); | ||||||
|  |  | ||||||
|     if (fp == NULL) |     if (fp == NULL) { | ||||||
|         return -1; |         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) { |     if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) { | ||||||
|         fclose(fp); |         fclose(fp); | ||||||
| @@ -116,8 +154,9 @@ int avatar_set(Tox *m, const char *path, size_t path_len) | |||||||
|  |  | ||||||
|     off_t size = file_size(path); |     off_t size = file_size(path); | ||||||
|  |  | ||||||
|     if (size == 0 || size > MAX_AVATAR_FILE_SIZE) |     if (size == 0 || size > MAX_AVATAR_FILE_SIZE) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     get_file_name(Avatar.name, sizeof(Avatar.name), path); |     get_file_name(Avatar.name, sizeof(Avatar.name), path); | ||||||
|     Avatar.name_len = strlen(Avatar.name); |     Avatar.name_len = strlen(Avatar.name); | ||||||
| @@ -141,7 +180,14 @@ void avatar_unset(Tox *m) | |||||||
|     avatar_send_all(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) { |     switch (control) { | ||||||
|         case TOX_FILE_CONTROL_RESUME: |         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) { |             } else if (ft->state == FILE_TRANSFER_PAUSED) { | ||||||
|                 ft->state = FILE_TRANSFER_STARTED; |                 ft->state = FILE_TRANSFER_STARTED; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_FILE_CONTROL_PAUSE: |         case TOX_FILE_CONTROL_PAUSE: | ||||||
| @@ -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) | 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; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (length == 0) { |     if (length == 0) { | ||||||
|         close_file_transfer(NULL, m, ft, -1, NULL, silent); |         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; |         ft->position = position; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t send_data[length]; |     uint8_t *send_data = malloc(length); | ||||||
|     size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file); |  | ||||||
|  |  | ||||||
|     if (send_length != length) { |     if (send_data == NULL) { | ||||||
|         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); |         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     TOX_ERR_FILE_SEND_CHUNK err; |     size_t send_length = fread(send_data, 1, length, ft->file); | ||||||
|     tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err); |  | ||||||
|  |  | ||||||
|     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); |         fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ft->position += send_length; |     ft->position += send_length; | ||||||
|     ft->last_keep_alive = get_unix_time(); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,6 +23,8 @@ | |||||||
| #ifndef AVATARS_H | #ifndef AVATARS_H | ||||||
| #define AVATARS_H | #define AVATARS_H | ||||||
|  |  | ||||||
|  | #include "file_transfers.h" | ||||||
|  |  | ||||||
| #define MAX_AVATAR_FILE_SIZE 65536 | #define MAX_AVATAR_FILE_SIZE 65536 | ||||||
|  |  | ||||||
| /* Sends avatar to friendnum. | /* 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 avatar_unset(Tox *m); | ||||||
|  |  | ||||||
| void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length); | 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 */ | #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 */ | ||||||
							
								
								
									
										1081
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										1081
									
								
								src/chat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -23,13 +23,13 @@ | |||||||
| #ifndef CHAT_H | #ifndef CHAT_H | ||||||
| #define CHAT_H | #define CHAT_H | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| /* set CTRL to -1 if we don't want to send a control signal. | /* 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 */ |    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 chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL); | ||||||
| void kill_chat_window(ToxWindow *self, Tox *m); | 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 */ | #endif /* end of include guard: CHAT_H */ | ||||||
|   | |||||||
| @@ -23,23 +23,25 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.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 "toxic.h" | ||||||
| #include "windows.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 ToxWindow *prompt; | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
|  |  | ||||||
| void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 2) { |     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; |         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); |     long int idx = strtol(argv[2], NULL, 10); | ||||||
|  |  | ||||||
|     if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { |     if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -60,17 +62,17 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | |||||||
|     } else if (strcasecmp(inoutstr, "out") == 0) { |     } else if (strcasecmp(inoutstr, "out") == 0) { | ||||||
|         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); |         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); | ||||||
|     } else { |     } 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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!ft) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) { |     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; |         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); |     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) { |     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; |         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) { |     if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (tox_invite_friend(m, self->num, groupnum) == -1) { |     Tox_Err_Conference_Invite err; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group."); |  | ||||||
|  |     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; |         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) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *groupkey = Friends.list[self->num].group_invite.key; |     const char *conferencekey = Friends.list[self->num].conference_invite.key; | ||||||
|     uint16_t length = Friends.list[self->num].group_invite.length; |     uint16_t length = Friends.list[self->num].conference_invite.length; | ||||||
|     uint8_t type = Friends.list[self->num].group_invite.type; |     uint8_t type = Friends.list[self->num].conference_invite.type; | ||||||
|  |  | ||||||
|     if (!Friends.list[self->num].group_invite.pending) { |     if (!Friends.list[self->num].conference_invite.pending) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int groupnum = -1; |     uint32_t conferencenum; | ||||||
|  |  | ||||||
|     if (type == TOX_GROUPCHAT_TYPE_TEXT) |     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||||
|         groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length); |         Tox_Err_Conference_Join err; | ||||||
| /*#ifdef AUDIO |         conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err); | ||||||
|     else |  | ||||||
|         groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length, |  | ||||||
|                                            NULL, NULL); |  | ||||||
| #endif*/ |  | ||||||
|  |  | ||||||
|     if (groupnum == -1) { |         if (err != TOX_ERR_CONFERENCE_JOIN_OK) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize."); |             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; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     if (init_groupchat_win(prompt, m, groupnum, type) == -1) { | #else | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||||
|         tox_del_groupchat(m, groupnum); |         return; | ||||||
|  | #endif | ||||||
|  |     } else { | ||||||
|  |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type); | ||||||
|         return; |         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]) | void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long int idx = strtol(argv[1], NULL, 10); |     long int idx = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { |     if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); |     struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||||
|  |  | ||||||
|     if (!ft) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ft->state != FILE_TRANSFER_PENDING) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ((ft->file = fopen(ft->file_path, "a")) == NULL) { |     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); |         close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     TOX_ERR_FILE_CONTROL err; |     Tox_Err_File_Control err; | ||||||
|     tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &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; |         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 */ |     /* prep progress bar line */ | ||||||
|     char progline[MAX_STR_SIZE]; |     char progline[MAX_STR_SIZE]; | ||||||
|     init_progress_bar(progline); |     init_progress_bar(progline); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||||
|  |  | ||||||
|     ft->line_id = self->chatwin->hst->line_end->id + 2; |     ft->line_id = self->chatwin->hst->line_end->id + 2; | ||||||
|     ft->state = FILE_TRANSFER_STARTED; |     ft->state = FILE_TRANSFER_STARTED; | ||||||
| @@ -190,65 +226,61 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     return; |     return; | ||||||
|  |  | ||||||
| on_recv_error: | on_recv_error: | ||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: |         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED: |         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; |             return; | ||||||
|  |  | ||||||
|         case TOX_ERR_FILE_CONTROL_NOT_FOUND: |         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; |             return; | ||||||
|  |  | ||||||
|         case TOX_ERR_FILE_CONTROL_SENDQ: |         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; |             return; | ||||||
|  |  | ||||||
|         default: |         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; |             return; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     const char *errmsg = NULL; |     const char *errmsg = NULL; | ||||||
|  |  | ||||||
|     if (argc < 1) { |     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; |         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]; |     char path[MAX_STR_SIZE]; | ||||||
|     snprintf(path, sizeof(path), "%s", &argv[1][1]); |     snprintf(path, sizeof(path), "%s", argv[1]); | ||||||
|     int path_len = strlen(path) - 1; |     int path_len = strlen(path); | ||||||
|     path[path_len] = '\0'; |  | ||||||
|  |  | ||||||
|     if (path_len >= MAX_STR_SIZE) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     FILE *file_to_send = fopen(path, "r"); |     FILE *file_to_send = fopen(path, "r"); | ||||||
|  |  | ||||||
|     if (file_to_send == NULL) { |     if (file_to_send == NULL) { | ||||||
|         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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     off_t filesize = file_size(path); |     off_t filesize = file_size(path); | ||||||
|  |  | ||||||
|     if (filesize == 0) { |     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); |         fclose(file_to_send); | ||||||
|         return; |         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]; |     char file_name[TOX_MAX_FILENAME_LENGTH]; | ||||||
|     size_t namelen = get_file_name(file_name, sizeof(file_name), path); |     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, |     uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL, | ||||||
|                                      (uint8_t *) file_name, namelen, &err); |                                      (uint8_t *) file_name, namelen, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FILE_SEND_OK) |     if (err != TOX_ERR_FILE_SEND_OK) { | ||||||
|         goto on_send_error; |         goto on_send_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA); |     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]; |     char sizestr[32]; | ||||||
|     bytes_convert_str(sizestr, sizeof(sizestr), filesize); |     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; |     return; | ||||||
|  |  | ||||||
| on_send_error: | on_send_error: | ||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: |         case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: | ||||||
|             errmsg = "File transfer failed: Invalid friend."; |             errmsg = "File transfer failed: Invalid friend."; | ||||||
| @@ -304,7 +338,7 @@ on_send_error: | |||||||
|             break; |             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); |     tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); | ||||||
|     fclose(file_to_send); |     fclose(file_to_send); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,12 +23,12 @@ | |||||||
| #ifndef CHAT_COMMANDS_H | #ifndef CHAT_COMMANDS_H | ||||||
| #define CHAT_COMMANDS_H | #define CHAT_COMMANDS_H | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.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_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_conference_invite(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_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_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  |  | ||||||
| @@ -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_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_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #ifdef VIDEO | #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_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 /* 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 <errno.h> | ||||||
| #include <unistd.h> |  | ||||||
| #include <pwd.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 "configdir.h" | ||||||
| #include "misc_tools.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) | void get_home_dir(char *home, int size) | ||||||
| { | { | ||||||
|     struct passwd pwd; |     struct passwd pwd; | ||||||
| @@ -48,8 +48,9 @@ void get_home_dir(char *home, int size) | |||||||
|     } else { |     } else { | ||||||
|         hmstr = getenv("HOME"); |         hmstr = getenv("HOME"); | ||||||
|  |  | ||||||
|         if (hmstr == NULL) |         if (hmstr == NULL) { | ||||||
|             return; |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         snprintf(buf, sizeof(buf), "%s", hmstr); |         snprintf(buf, sizeof(buf), "%s", hmstr); | ||||||
|         hmstr = buf; |         hmstr = buf; | ||||||
| @@ -77,8 +78,9 @@ char *get_user_config_dir(void) | |||||||
|     len = strlen(home) + strlen("/Library/Application Support") + 1; |     len = strlen(home) + strlen("/Library/Application Support") + 1; | ||||||
|     user_config_dir = malloc(len); |     user_config_dir = malloc(len); | ||||||
|  |  | ||||||
|     if (user_config_dir == NULL) |     if (user_config_dir == NULL) { | ||||||
|         return NULL; |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     snprintf(user_config_dir, len, "%s/Library/Application Support", home); |     snprintf(user_config_dir, len, "%s/Library/Application Support", home); | ||||||
| # else /* __APPLE__ */ | # else /* __APPLE__ */ | ||||||
| @@ -89,8 +91,9 @@ char *get_user_config_dir(void) | |||||||
|         len = strlen(home) + strlen("/.config") + 1; |         len = strlen(home) + strlen("/.config") + 1; | ||||||
|         user_config_dir = malloc(len); |         user_config_dir = malloc(len); | ||||||
|  |  | ||||||
|         if (user_config_dir == NULL) |         if (user_config_dir == NULL) { | ||||||
|             return NULL; |             return NULL; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         snprintf(user_config_dir, len, "%s/.config", home); |         snprintf(user_config_dir, len, "%s/.config", home); | ||||||
|     } else { |     } else { | ||||||
| @@ -99,30 +102,29 @@ char *get_user_config_dir(void) | |||||||
|  |  | ||||||
| # endif /* __APPLE__ */ | # endif /* __APPLE__ */ | ||||||
|  |  | ||||||
|     if (!file_exists(user_config_dir)) { |  | ||||||
|         free(user_config_dir); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return user_config_dir; |     return user_config_dir; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* Creates the config and chatlog directories. | ||||||
|  * Creates the config and chatlog directories. |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  */ |  */ | ||||||
| int create_user_config_dirs(char *path) | int create_user_config_dirs(char *path) | ||||||
| { | { | ||||||
|     struct stat buf; |     struct stat buf; | ||||||
|     int mkdir_err = mkdir(path, 0700); |     int mkdir_err = mkdir(path, 0700); | ||||||
|  |  | ||||||
|     if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) |     if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1); |     char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1); | ||||||
|     char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1); |     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); |         exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     strcpy(fullpath, path); |     strcpy(fullpath, path); | ||||||
|     strcat(fullpath, CONFIGDIR); |     strcat(fullpath, CONFIGDIR); | ||||||
|   | |||||||
| @@ -34,8 +34,23 @@ | |||||||
| #define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR) | #define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Get the user's config directory. | ||||||
|  |  * | ||||||
|  |  * This is without a trailing slash. Resulting string must be freed. | ||||||
|  |  * | ||||||
|  |  * @return The users config dir or NULL on error. | ||||||
|  |  */ | ||||||
| char *get_user_config_dir(void); | char *get_user_config_dir(void); | ||||||
|  |  | ||||||
|  | /* get the user's home directory. */ | ||||||
| void get_home_dir(char *home, int size); | void get_home_dir(char *home, int size); | ||||||
|  |  | ||||||
|  | /* Creates the config and chatlog directories. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  * Returns -1 on failure. | ||||||
|  |  */ | ||||||
| int create_user_config_dirs(char *path); | int create_user_config_dirs(char *path); | ||||||
|  |  | ||||||
| #endif /* #define CONFIGDIR_H */ | #endif /* 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 <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <assert.h> |  | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "api.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "execute.h" |  | ||||||
| #include "chat_commands.h" | #include "chat_commands.h" | ||||||
|  | #include "execute.h" | ||||||
| #include "global_commands.h" | #include "global_commands.h" | ||||||
| #include "group_commands.h" | #include "conference_commands.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| struct cmd_func { | struct cmd_func { | ||||||
|     const char *name; |     const char *name; | ||||||
| @@ -47,11 +48,13 @@ static struct cmd_func global_commands[] = { | |||||||
|     { "/connect",   cmd_connect       }, |     { "/connect",   cmd_connect       }, | ||||||
|     { "/decline",   cmd_decline       }, |     { "/decline",   cmd_decline       }, | ||||||
|     { "/exit",      cmd_quit          }, |     { "/exit",      cmd_quit          }, | ||||||
|     { "/group",     cmd_groupchat     }, |     { "/conference", cmd_conference    }, | ||||||
|     { "/help",      cmd_prompt_help   }, |     { "/help",      cmd_prompt_help   }, | ||||||
|     { "/log",       cmd_log           }, |     { "/log",       cmd_log           }, | ||||||
|     { "/myid",      cmd_myid          }, |     { "/myid",      cmd_myid          }, | ||||||
|  | #ifdef QRCODE | ||||||
|     { "/myqr",      cmd_myqr          }, |     { "/myqr",      cmd_myqr          }, | ||||||
|  | #endif /* QRCODE */ | ||||||
|     { "/nick",      cmd_nick          }, |     { "/nick",      cmd_nick          }, | ||||||
|     { "/note",      cmd_note          }, |     { "/note",      cmd_note          }, | ||||||
|     { "/nospam",    cmd_nospam        }, |     { "/nospam",    cmd_nospam        }, | ||||||
| @@ -67,13 +70,16 @@ static struct cmd_func global_commands[] = { | |||||||
|     { "/lsvdev",    cmd_list_video_devices  }, |     { "/lsvdev",    cmd_list_video_devices  }, | ||||||
|     { "/svdev",     cmd_change_video_device }, |     { "/svdev",     cmd_change_video_device }, | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|  | #ifdef PYTHON | ||||||
|  |     { "/run",       cmd_run           }, | ||||||
|  | #endif /* PYTHON */ | ||||||
|     { NULL,         NULL              }, |     { NULL,         NULL              }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct cmd_func chat_commands[] = { | static struct cmd_func chat_commands[] = { | ||||||
|     { "/cancel",    cmd_cancelfile        }, |     { "/cancel",    cmd_cancelfile        }, | ||||||
|     { "/invite",    cmd_groupinvite }, |     { "/invite",    cmd_conference_invite }, | ||||||
|     { "/join",      cmd_join_group  }, |     { "/join",      cmd_conference_join   }, | ||||||
|     { "/savefile",  cmd_savefile          }, |     { "/savefile",  cmd_savefile          }, | ||||||
|     { "/sendfile",  cmd_sendfile          }, |     { "/sendfile",  cmd_sendfile          }, | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
| @@ -83,69 +89,127 @@ static struct cmd_func chat_commands[] = { | |||||||
|     { "/hangup",    cmd_hangup            }, |     { "/hangup",    cmd_hangup            }, | ||||||
|     { "/mute",      cmd_mute              }, |     { "/mute",      cmd_mute              }, | ||||||
|     { "/sense",     cmd_sense             }, |     { "/sense",     cmd_sense             }, | ||||||
|  |     { "/bitrate",   cmd_bitrate           }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|  |     { "/vcall",     cmd_vcall             }, | ||||||
|     { "/video",     cmd_video             }, |     { "/video",     cmd_video             }, | ||||||
|  |     { "/res",       cmd_res               }, | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|     { NULL,         NULL                  }, |     { NULL,         NULL                  }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static struct cmd_func group_commands[] = { | static struct cmd_func conference_commands[] = { | ||||||
|     { "/title",     cmd_set_title   }, |     { "/title",     cmd_conference_set_title }, | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|     { "/mute",      cmd_mute        }, |     { "/audio",     cmd_enable_audio }, | ||||||
|     { "/sense",     cmd_sense       }, |     { "/mute",      cmd_conference_mute   }, | ||||||
|  |     { "/ptt",       cmd_conference_push_to_talk }, | ||||||
|  |     { "/sense",     cmd_conference_sense  }, | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|     { NULL,         NULL             }, |     { NULL,         NULL             }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Parses input command and puts args into arg array. |  | ||||||
|    Returns number of arguments on success, -1 on failure. */ | #ifdef PYTHON | ||||||
| static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) | #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); |     char *cmd = strdup(input); | ||||||
|  |  | ||||||
|     if (cmd == NULL) |     if (cmd == NULL) { | ||||||
|         exit_toxic_err("failed in parse_command", FATALERR_MEMORY); |         exit_toxic_err("failed in parse_command", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int num_args = 0; |     int num_args = 0; | ||||||
|     int i = 0;    /* index of last char in an argument */ |  | ||||||
|  |  | ||||||
|     /* characters wrapped in double quotes count as one arg */ |     /* characters wrapped in double quotes count as one arg */ | ||||||
|     while (num_args < MAX_NUM_ARGS) { |     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 == '\"') { |         if (cmd[i] == '\0') {  // no more args | ||||||
|             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 */ |  | ||||||
|             break; |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         char tmp[MAX_STR_SIZE]; |         char tmp[MAX_STR_SIZE]; | ||||||
|         snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]); |         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); |     free(cmd); | ||||||
|     return num_args; |     return num_args; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Matches command to respective function. Returns 0 on match, 1 on no match */ | /* Matches command to respective function. | ||||||
|  |  * | ||||||
|  |  * Returns 0 on match. | ||||||
|  |  * Returns 1 on no match | ||||||
|  |  */ | ||||||
| static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, | static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, | ||||||
|                       char (*args)[MAX_STR_SIZE]) |                       char (*args)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
| @@ -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) | void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | ||||||
| { | { | ||||||
|     if (string_is_empty(input)) |     if (string_is_empty(input)) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char args[MAX_NUM_ARGS][MAX_STR_SIZE]; |     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; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* Try to match input command to command functions. If non-global command mode is specified, |     /* 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. |      * try specified mode's commands first, then upon failure try global commands. | ||||||
|  |      * | ||||||
|        Note: Global commands must come last in case of duplicate command names */ |      * Note: Global commands must come last in case of duplicate command names | ||||||
|  |      */ | ||||||
|     switch (mode) { |     switch (mode) { | ||||||
|         case CHAT_COMMAND_MODE: |         case CHAT_COMMAND_MODE: | ||||||
|             if (do_command(w, self, m, num_args, chat_commands, args) == 0) |             if (do_command(w, self, m, num_args, chat_commands, args) == 0) { | ||||||
|                 return; |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case GROUPCHAT_COMMAND_MODE: |         case CONFERENCE_COMMAND_MODE: | ||||||
|             if (do_command(w, self, m, num_args, group_commands, args) == 0) |             if (do_command(w, self, m, num_args, conference_commands, args) == 0) { | ||||||
|                 return; |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             break; |             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; |         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 { | enum { | ||||||
|     GLOBAL_COMMAND_MODE, |     GLOBAL_COMMAND_MODE, | ||||||
|     CHAT_COMMAND_MODE, |     CHAT_COMMAND_MODE, | ||||||
|     GROUPCHAT_COMMAND_MODE, |     CONFERENCE_COMMAND_MODE, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | ||||||
|  |  | ||||||
| #endif /* #define EXECUTE_H */ | #endif /* EXECUTE_H */ | ||||||
|   | |||||||
| @@ -20,115 +20,94 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "friendlist.h" |  | ||||||
| #include "file_transfers.h" | #include "file_transfers.h" | ||||||
|  | #include "friendlist.h" | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
|  |  | ||||||
| /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | ||||||
| #define NUM_PROG_MARKS 50 | #define NUM_PROG_MARKS 50 | ||||||
|  | #define STR_BUF_SIZE 30 | ||||||
| /* Checks for timed out file transfers and closes them. */ |  | ||||||
| #define CHECK_FILE_TIMEOUT_INTERAVAL 5 |  | ||||||
| void check_file_transfer_timeouts(Tox *m) |  | ||||||
| { |  | ||||||
|     char msg[MAX_STR_SIZE]; |  | ||||||
|     static uint64_t last_check = 0; |  | ||||||
|  |  | ||||||
|     if (!timed_out(last_check, CHECK_FILE_TIMEOUT_INTERAVAL)) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     last_check = get_unix_time(); |  | ||||||
|  |  | ||||||
|     size_t i, j; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < Friends.max_idx; ++i) { |  | ||||||
|         if (!Friends.list[i].active) |  | ||||||
|             continue; |  | ||||||
|  |  | ||||||
|         for (j = 0; j < MAX_FILES; ++j) { |  | ||||||
|             struct FileTransfer *ft_send = &Friends.list[i].file_sender[j]; |  | ||||||
|  |  | ||||||
|             if (ft_send->state > FILE_TRANSFER_PAUSED) { |  | ||||||
|                 if (timed_out(ft_send->last_keep_alive, TIMEOUT_FILESENDER)) { |  | ||||||
|                     snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_send->file_name); |  | ||||||
|                     close_file_transfer(ft_send->window, m, ft_send, TOX_FILE_CONTROL_CANCEL, msg, notif_error); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             struct FileTransfer *ft_recv = &Friends.list[i].file_receiver[j]; |  | ||||||
|  |  | ||||||
|             if (ft_recv->state > FILE_TRANSFER_PAUSED) { |  | ||||||
|                 if (timed_out(ft_recv->last_keep_alive, TIMEOUT_FILESENDER)) { |  | ||||||
|                     snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_recv->file_name); |  | ||||||
|                     close_file_transfer(ft_recv->window, m, ft_recv, TOX_FILE_CONTROL_CANCEL, msg, notif_error); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* creates initial progress line that will be updated during file transfer. | /* creates initial progress line that will be updated during file transfer. | ||||||
|    Assumes progline is of size MAX_STR_SIZE */ |    Assumes progline has room for at least MAX_STR_SIZE bytes */ | ||||||
| void init_progress_bar(char *progline) | void init_progress_bar(char *progline) | ||||||
| { | { | ||||||
|     strcpy(progline, "0.0 B/s ["); |     strcpy(progline, "0% ["); | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; i < NUM_PROG_MARKS; ++i) |     for (i = 0; i < NUM_PROG_MARKS; ++i) { | ||||||
|         strcat(progline, "-"); |         strcat(progline, "-"); | ||||||
|  |  | ||||||
|     strcat(progline, "] 0%"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| /* prints a progress bar for file transfers. |     strcat(progline, "] 0.0 B/s"); | ||||||
|    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) | void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id) | ||||||
| { | { | ||||||
|     if (bps < 0 || pct_done < 0 || pct_done > 100) |     if (bps < 0 || pct_done < 0 || pct_done > 100) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char msg[MAX_STR_SIZE]; |     char pct_str[STR_BUF_SIZE]; | ||||||
|     bytes_convert_str(msg, sizeof(msg), bps); |     snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done); | ||||||
|     strcat(msg, "/s ["); |  | ||||||
|  |     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 n = pct_done / (100 / NUM_PROG_MARKS); | ||||||
|     int i, j; |     int i, j; | ||||||
|  |  | ||||||
|     for (i = 0; i < n; ++i) |     for (i = 0; i < n; ++i) { | ||||||
|         strcat(msg, "#"); |         strcat(prog_line, "="); | ||||||
|  |  | ||||||
|     for (j = i; j < NUM_PROG_MARKS; ++j) |  | ||||||
|         strcat(msg, "-"); |  | ||||||
|  |  | ||||||
|     strcat(msg, "] "); |  | ||||||
|  |  | ||||||
|     char pctstr[16]; |  | ||||||
|     const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%"; |  | ||||||
|     snprintf(pctstr, sizeof(pctstr), frmt, pct_done); |  | ||||||
|     strcat(msg, pctstr); |  | ||||||
|  |  | ||||||
|     line_info_set(self, line_id, msg); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft) |     if (pct_done < 100) { | ||||||
| { |         strcpy(prog_line + n, ">"); | ||||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) |     } | ||||||
|  |  | ||||||
|  |     for (j = i; j < NUM_PROG_MARKS - 1; ++j) { | ||||||
|  |         strcat(prog_line, "-"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t line_buf_size = strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7; | ||||||
|  |     char *full_line = malloc(line_buf_size); | ||||||
|  |  | ||||||
|  |     if (full_line == NULL) { | ||||||
|         return; |         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, struct FileTransfer *ft) | ||||||
|  | { | ||||||
|  |     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* Timeout must be set to 1 second to show correct bytes per second */ |     /* 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; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     double remain = ft->file_size - ft->position; |     double remain = ft->file_size - ft->position; | ||||||
|     double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100; |     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. */ | /* 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 (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         refresh_progress_helper(self, &Friends.list[friendnumber].file_receiver[i]); | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |         refresh_progress_helper(self, &Friends.list[friendnumber].file_sender[i]); | ||||||
|         refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]); |  | ||||||
|         refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns a pointer to friendnum's FileTransfer struct associated with filenum. | static void clear_file_transfer(struct FileTransfer *ft) | ||||||
|  * Returns NULL if filenum is invalid. | { | ||||||
|  |     *ft = (struct FileTransfer) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 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) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i]; | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |         if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) { | ||||||
|         struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i]; |  | ||||||
|  |  | ||||||
|         if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) |  | ||||||
|             return ft_send; |             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 ft_recv; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return NULL; |     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 a pointer to the FileTransfer struct associated with index with the direction specified. | ||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index, | ||||||
|         FILE_TRANSFER_DIRECTION direction) |         FILE_TRANSFER_DIRECTION direction) | ||||||
| { | { | ||||||
|     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) |     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) { | ||||||
|         return NULL; |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     size_t i; |     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |  | ||||||
|         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? |         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? | ||||||
|                                               &Friends.list[friendnum].file_sender[i] : |                                       &Friends.list[friendnumber].file_sender[i] : | ||||||
|                                               &Friends.list[friendnum].file_receiver[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 ft; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return NULL; |     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 a pointer to an unused file sender. | ||||||
|  * Returns NULL if all file senders are in use. |  * 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 (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |  | ||||||
|         struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i]; |  | ||||||
|  |  | ||||||
|         if (ft->state == FILE_TRANSFER_INACTIVE) { |         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|             memset(ft, 0, sizeof(struct FileTransfer)); |             clear_file_transfer(ft); | ||||||
|             ft->window = window; |             ft->window = window; | ||||||
|             ft->index = i; |             ft->index = i; | ||||||
|             ft->friendnum = friendnum; |             ft->friendnumber = friendnumber; | ||||||
|             ft->filenum = filenum; |             ft->filenumber = filenumber; | ||||||
|             ft->file_type = type; |             ft->file_type = type; | ||||||
|             ft->last_keep_alive = get_unix_time(); |  | ||||||
|             ft->state = FILE_TRANSFER_PENDING; |             ft->state = FILE_TRANSFER_PENDING; | ||||||
|             ft->direction = FILE_TRANSFER_SEND; |  | ||||||
|             return ft; |             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 a pointer to an unused file receiver. | ||||||
|  * Returns NULL if all file receivers are in use. |  * 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 (size_t i = 0; i < MAX_FILES; ++i) { | ||||||
|  |         struct FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i]; | ||||||
|     for (i = 0; i < MAX_FILES; ++i) { |  | ||||||
|         struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i]; |  | ||||||
|  |  | ||||||
|         if (ft->state == FILE_TRANSFER_INACTIVE) { |         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|             memset(ft, 0, sizeof(struct FileTransfer)); |             clear_file_transfer(ft); | ||||||
|             ft->window = window; |             ft->window = window; | ||||||
|             ft->index = i; |             ft->index = i; | ||||||
|             ft->friendnum = friendnum; |             ft->friendnumber = friendnumber; | ||||||
|             ft->filenum = filenum; |             ft->filenumber = filenumber; | ||||||
|             ft->file_type = type; |             ft->file_type = type; | ||||||
|             ft->last_keep_alive = get_unix_time(); |  | ||||||
|             ft->state = FILE_TRANSFER_PENDING; |             ft->state = FILE_TRANSFER_PENDING; | ||||||
|             ft->direction = FILE_TRANSFER_RECV; |  | ||||||
|             return ft; |             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. | /* Initializes an unused file transfer and returns its pointer. | ||||||
|  * Returns NULL on failure. |  * 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) |                                        FILE_TRANSFER_DIRECTION direction, uint8_t type) | ||||||
| { | { | ||||||
|     if (direction == FILE_TRANSFER_RECV) |     if (direction == FILE_TRANSFER_RECV) { | ||||||
|         return new_file_receiver(window, friendnum, filenum, type); |         return new_file_receiver(window, friendnumber, filenumber, type); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (direction == FILE_TRANSFER_SEND) |     if (direction == FILE_TRANSFER_SEND) { | ||||||
|         return new_file_sender(window, friendnum, filenum, type); |         return new_file_sender(window, friendnumber, filenumber, type); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return NULL; |     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, | void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, | ||||||
|                          Notification sound_type) |                          Notification sound_type) | ||||||
| { | { | ||||||
|     if (!ft) |     if (!ft) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) |     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (ft->file) |     if (ft->file) { | ||||||
|         fclose(ft->file); |         fclose(ft->file); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (CTRL >= 0) |     if (CTRL >= 0) { | ||||||
|         tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL); |         tox_file_control(m, ft->friendnumber, ft->filenumber, (Tox_File_Control) CTRL, NULL); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (message && self) { |     if (message && self) { | ||||||
|         if (self->active_box != -1 && sound_type != silent) |         if (self->active_box != -1 && sound_type != silent) { | ||||||
|             box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message); |             box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message); | ||||||
|         else |         } else { | ||||||
|             box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message); |             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)); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", message); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| /* Kills all active file transfers for friendnum */ |     clear_file_transfer(ft); | ||||||
| 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) { |         if (ft->file_type == TOX_FILE_KIND_AVATAR) { | ||||||
|         close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); |             close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||||
|         close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], 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) | void kill_all_file_transfers(Tox *m) | ||||||
| { | { | ||||||
|     size_t i; |     for (size_t i = 0; i < Friends.max_idx; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < Friends.max_idx; ++i) |  | ||||||
|         kill_all_file_transfers_friend(m, Friends.list[i].num); |         kill_all_file_transfers_friend(m, Friends.list[i].num); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -25,16 +25,15 @@ | |||||||
|  |  | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
|  |  | ||||||
|  | #include "notify.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "notify.h" |  | ||||||
|  |  | ||||||
| #define KiB 1024 | #define KiB 1024 | ||||||
| #define MiB 1048576       /* 1024^2 */ | #define MiB 1048576       /* 1024^2 */ | ||||||
| #define GiB 1073741824    /* 1024^3 */ | #define GiB 1073741824    /* 1024^3 */ | ||||||
|  |  | ||||||
| #define MAX_FILES 32 | #define MAX_FILES 32 | ||||||
| #define TIMEOUT_FILESENDER 120 |  | ||||||
|  |  | ||||||
| typedef enum FILE_TRANSFER_STATE { | typedef enum FILE_TRANSFER_STATE { | ||||||
|     FILE_TRANSFER_INACTIVE, |     FILE_TRANSFER_INACTIVE, | ||||||
| @@ -52,25 +51,20 @@ struct FileTransfer { | |||||||
|     ToxWindow *window; |     ToxWindow *window; | ||||||
|     FILE *file; |     FILE *file; | ||||||
|     FILE_TRANSFER_STATE state; |     FILE_TRANSFER_STATE state; | ||||||
|     FILE_TRANSFER_DIRECTION direction; |  | ||||||
|     uint8_t file_type; |     uint8_t file_type; | ||||||
|     char file_name[TOX_MAX_FILENAME_LENGTH + 1]; |     char file_name[TOX_MAX_FILENAME_LENGTH + 1]; | ||||||
|     char file_path[PATH_MAX + 1];    /* Not used by senders */ |     char file_path[PATH_MAX + 1];    /* Not used by senders */ | ||||||
|     double   bps; |     double   bps; | ||||||
|     uint32_t filenum; |     uint32_t filenumber; | ||||||
|     uint32_t friendnum; |     uint32_t friendnumber; | ||||||
|     size_t   index; |     size_t   index; | ||||||
|     uint64_t file_size; |     uint64_t file_size; | ||||||
|     uint64_t position; |     uint64_t position; | ||||||
|     uint64_t last_line_progress;   /* The last time we updated the progress bar */ |     time_t   last_line_progress;   /* The last time we updated the progress bar */ | ||||||
|     uint64_t last_keep_alive;  /* The last time we sent or received data */ |  | ||||||
|     uint32_t line_id; |     uint32_t line_id; | ||||||
|     uint8_t  file_id[TOX_FILE_ID_LENGTH]; |     uint8_t  file_id[TOX_FILE_ID_LENGTH]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Checks for timed out file transfers and closes them. */ |  | ||||||
| void check_file_transfer_timeouts(Tox *m); |  | ||||||
|  |  | ||||||
| /* creates initial progress line that will be updated during file transfer. | /* creates initial progress line that will be updated during file transfer. | ||||||
|    progline must be at lesat MAX_STR_SIZE bytes */ |    progline must be at lesat MAX_STR_SIZE bytes */ | ||||||
| void init_progress_bar(char *progline); | void init_progress_bar(char *progline); | ||||||
| @@ -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); | void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id); | ||||||
|  |  | ||||||
| /* refreshes active file transfer status bars. */ | /* 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 a pointer to friendnumber's FileTransfer struct associated with filenumber. | ||||||
|  * Returns NULL if filenum is invalid. |  * 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 a pointer to the FileTransfer struct associated with index with the direction specified. | ||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index, | ||||||
|         FILE_TRANSFER_DIRECTION direction); |         FILE_TRANSFER_DIRECTION direction); | ||||||
|  |  | ||||||
| /* Initializes an unused file transfer and returns its pointer. | /* Initializes an unused file transfer and returns its pointer. | ||||||
|  * Returns NULL on failure. |  * Returns NULL on failure. | ||||||
|  */ |  */ | ||||||
| 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); |                                        FILE_TRANSFER_DIRECTION direction, uint8_t type); | ||||||
|  |  | ||||||
| /* Closes file transfer ft. | /* 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, | void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, | ||||||
|                          Notification sound_type); |                          Notification sound_type); | ||||||
|  |  | ||||||
| /* Kills all active file transfers for friendnum */ | /* Kills active outgoing avatar file transfers for friendnumber */ | ||||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum); | 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); | void kill_all_file_transfers(Tox *m); | ||||||
|  |  | ||||||
| #endif  /* #define FILE_TRANSFERS_H */ | #endif /* FILE_TRANSFERS_H */ | ||||||
|   | |||||||
							
								
								
									
										659
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										659
									
								
								src/friendlist.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -25,9 +25,9 @@ | |||||||
|  |  | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  |  | ||||||
|  | #include "file_transfers.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "file_transfers.h" |  | ||||||
|  |  | ||||||
| struct LastOnline { | struct LastOnline { | ||||||
|     uint64_t last_on; |     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" */ |     char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct GroupChatInvite { | struct ConferenceInvite { | ||||||
|     char *key; |     char *key; | ||||||
|     uint16_t length; |     uint16_t length; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
| @@ -44,20 +44,20 @@ struct GroupChatInvite { | |||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; |     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     int namelength; |     uint16_t namelength; | ||||||
|     char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; |     char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; | ||||||
|     size_t statusmsg_len; |     size_t statusmsg_len; | ||||||
|     char pub_key[TOX_PUBLIC_KEY_SIZE]; |     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||||
|     uint32_t num; |     uint32_t num; | ||||||
|     int chatwin; |     int chatwin; | ||||||
|     bool active; |     bool active; | ||||||
|     TOX_CONNECTION connection_status; |     Tox_Connection connection_status; | ||||||
|     bool is_typing; |     bool is_typing; | ||||||
|     bool logging_on;    /* saves preference for friend irrespective of global settings */ |     bool logging_on;    /* saves preference for friend irrespective of global settings */ | ||||||
|     uint8_t status; |     Tox_User_Status status; | ||||||
|  |  | ||||||
|     struct LastOnline last_online; |     struct LastOnline last_online; | ||||||
|     struct GroupChatInvite group_invite; |     struct ConferenceInvite conference_invite; | ||||||
|  |  | ||||||
|     struct FileTransfer file_receiver[MAX_FILES]; |     struct FileTransfer file_receiver[MAX_FILES]; | ||||||
|     struct FileTransfer file_sender[MAX_FILES]; |     struct FileTransfer file_sender[MAX_FILES]; | ||||||
| @@ -65,7 +65,7 @@ typedef struct { | |||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; |     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     int namelength; |     uint16_t namelength; | ||||||
|     char pub_key[TOX_PUBLIC_KEY_SIZE]; |     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||||
|     uint32_t num; |     uint32_t num; | ||||||
|     bool active; |     bool active; | ||||||
| @@ -81,14 +81,24 @@ typedef struct { | |||||||
|     ToxicFriend *list; |     ToxicFriend *list; | ||||||
| } FriendsList; | } FriendsList; | ||||||
|  |  | ||||||
| ToxWindow new_friendlist(void); | ToxWindow *new_friendlist(void); | ||||||
|  | void friendlist_onInit(ToxWindow *self, Tox *m); | ||||||
| void disable_chatwin(uint32_t f_num); | void disable_chatwin(uint32_t f_num); | ||||||
| int get_friendnum(uint8_t *name); | int get_friendnum(uint8_t *name); | ||||||
| int load_blocklist(char *data); | 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); | 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 */ | /* sorts friendlist_index first by connection status then alphabetically */ | ||||||
| void sort_friendlist_index(void); | 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 */ | #endif /* end of include guard: FRIENDLIST_H */ | ||||||
|   | |||||||
| @@ -23,19 +23,20 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.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 "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 "name_lookup.h" | ||||||
|  | #include "prompt.h" | ||||||
| #include "qr_code.h" | #include "qr_code.h" | ||||||
|  | #include "term_mplex.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "toxic_strings.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern char *DATA_FILE; | extern char *DATA_FILE; | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| @@ -45,54 +46,58 @@ extern FriendRequests FrndRequests; | |||||||
| /* command functions */ | /* command functions */ | ||||||
| void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long int req = strtol(argv[1], NULL, 10); |     long int req = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { |     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!FrndRequests.request[req].active) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     TOX_ERR_FRIEND_ADD err; |     Tox_Err_Friend_Add err; | ||||||
|     uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err); |     uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_FRIEND_ADD_OK) { |     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; |         return; | ||||||
|     } else { |     } else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); | ||||||
|         on_friendadded(m, friendnum, true); |         on_friend_added(m, friendnum, true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); |     FrndRequests.request[req] = (struct friend_request) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = FrndRequests.max_idx; i > 0; --i) { |     for (i = FrndRequests.max_idx; i > 0; --i) { | ||||||
|         if (FrndRequests.request[i - 1].active) |         if (FrndRequests.request[i - 1].active) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     FrndRequests.max_idx = i; |     FrndRequests.max_idx = i; | ||||||
|     --FrndRequests.num_requests; |     --FrndRequests.num_requests; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg) | void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg) | ||||||
| { | { | ||||||
|     const char *errmsg; |     const char *errmsg; | ||||||
|  |  | ||||||
|     TOX_ERR_FRIEND_ADD err; |     Tox_Err_Friend_Add err; | ||||||
|     uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err); |     uint32_t f_num = tox_friend_add(m, (const uint8_t *) id_bin, (const uint8_t *) msg, strlen(msg), &err); | ||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_FRIEND_ADD_TOO_LONG: |         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: |         case TOX_ERR_FRIEND_ADD_OK: | ||||||
|             errmsg = "Friend request sent."; |             errmsg = "Friend request sent."; | ||||||
|             on_friendadded(m, f_num, true); |             on_friend_added(m, f_num, true); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case TOX_ERR_FRIEND_ADD_NULL: |         case TOX_ERR_FRIEND_ADD_NULL: | ||||||
|  |  | ||||||
|         /* fallthrough */ |         /* fallthrough */ | ||||||
|         default: |         default: | ||||||
|             errmsg = "Faile to add friend: Unknown error."; |             errmsg = "Failed to add friend: Unknown error."; | ||||||
|             break; |             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]) | void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -150,7 +158,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|  |  | ||||||
|     if (argc > 1) { |     if (argc > 1) { | ||||||
|         if (argv[2][0] != '\"') { |         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; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -184,13 +192,18 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | |||||||
|             xx[2] = '\0'; |             xx[2] = '\0'; | ||||||
|  |  | ||||||
|             if (sscanf(xx, "%02x", &x) != 1) { |             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; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             id_bin[i] = x; |             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); |         cmd_add_helper(self, m, id_bin, msg); | ||||||
|     } else {    /* assume id is a username@domain address and do http name server lookup */ |     } else {    /* assume id is a username@domain address and do http name server lookup */ | ||||||
|         name_lookup(self, m, id_bin, id, msg); |         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]) | 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); |         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; |         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]; |     char path[MAX_STR_SIZE]; | ||||||
|     snprintf(path, sizeof(path), "%s", &argv[1][1]); |     snprintf(path, sizeof(path), "%s", argv[1]); | ||||||
|     int len = strlen(path) - 1; |     int len = strlen(path); | ||||||
|  |  | ||||||
|     if (len <= 0) { |     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; |         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); |     get_file_name(filename, sizeof(filename), path); | ||||||
|  |  | ||||||
|     if (avatar_set(m, path, len) == -1) { |     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.", |                       "Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.", | ||||||
|                       MAX_AVATAR_FILE_SIZE); |                       MAX_AVATAR_FILE_SIZE); | ||||||
|         return; |         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]) | 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); |     line_info_clear(self->chatwin->hst); | ||||||
|     force_refresh(window); |     force_refresh(window); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc != 3) { |     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; |         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); |     long int port = strtol(port_str, NULL, 10); | ||||||
|  |  | ||||||
|     if (port <= 0 || port > MAX_PORT_RANGE) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; |     char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; | ||||||
|  |  | ||||||
|     if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { |     if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     TOX_ERR_BOOTSTRAP err; |     Tox_Err_Bootstrap err; | ||||||
|     tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err); |     tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err); | ||||||
|     tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err); |     tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err); | ||||||
|  |  | ||||||
|     switch (err) { |     switch (err) { | ||||||
|         case TOX_ERR_BOOTSTRAP_BAD_HOST: |         case TOX_ERR_BOOTSTRAP_BAD_HOST: | ||||||
|             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; |             break; | ||||||
|  |  | ||||||
|         case TOX_ERR_BOOTSTRAP_BAD_PORT: |         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; |             break; | ||||||
|  |  | ||||||
|         case TOX_ERR_BOOTSTRAP_NULL: |         case TOX_ERR_BOOTSTRAP_NULL: | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); |             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         default: |         default: | ||||||
|             break; |             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]) | 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) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     long int req = strtol(argv[1], NULL, 10); |     long int req = strtol(argv[1], NULL, 10); | ||||||
|  |  | ||||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { |     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!FrndRequests.request[req].active) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); |     FrndRequests.request[req] = (struct friend_request) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = FrndRequests.max_idx; i > 0; --i) { |     for (i = FrndRequests.max_idx; i > 0; --i) { | ||||||
|         if (FrndRequests.request[i - 1].active) |         if (FrndRequests.request[i - 1].active) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     FrndRequests.max_idx = i; |     FrndRequests.max_idx = i; | ||||||
|     --FrndRequests.num_requests; |     --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) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (argc < 1) { |     if (argc < 1) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|  |  | ||||||
|     if (!strcasecmp(argv[1], "audio")) |     if (!strcasecmp(argv[1], "audio")) { | ||||||
|         type = TOX_GROUPCHAT_TYPE_AV; |         type = TOX_CONFERENCE_TYPE_AV; | ||||||
|     else if (!strcasecmp(argv[1], "text")) |     } else if (!strcasecmp(argv[1], "text")) { | ||||||
|         type = TOX_GROUPCHAT_TYPE_TEXT; |         type = TOX_CONFERENCE_TYPE_TEXT; | ||||||
|     else { |     } else { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int groupnum = -1; |     uint32_t conferencenum = 0; | ||||||
|  |  | ||||||
|     if (type == TOX_GROUPCHAT_TYPE_TEXT) |     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||||
|         groupnum = tox_add_groupchat(m); |         Tox_Err_Conference_New err; | ||||||
| /*#ifdef AUDIO |  | ||||||
|     else |  | ||||||
|         groupnum = toxav_add_av_groupchat(m, NULL, NULL); |  | ||||||
| #endif*/ |  | ||||||
|  |  | ||||||
|     if (groupnum == -1) { |         conferencenum = tox_conference_new(m, &err); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize."); |  | ||||||
|  |         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; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     if (init_groupchat_win(prompt, m, groupnum, type) == -1) { | #else | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||||
|         tox_del_groupchat(m, groupnum); |         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; |         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]) | void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     const char *msg; |     const char *msg; | ||||||
|     struct chatlog *log = self->chatwin->log; |     struct chatlog *log = self->chatwin->log; | ||||||
|  |  | ||||||
|     if (argc == 0) { |     if (argc == 0) { | ||||||
|         if (log->log_on) |         if (log->log_on) { | ||||||
|             msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)"; |             msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)"; | ||||||
|         else |         } else { | ||||||
|             msg = "Logging for this window is OFF; type \"/log on\" to enable."; |             msg = "Logging for this window is OFF; type \"/log on\" to enable."; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *swch = argv[1]; |     const char *swch = argv[1]; | ||||||
|  |  | ||||||
|     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { |     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { | ||||||
|         char myid[TOX_ADDRESS_SIZE]; |         msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log."; | ||||||
|         tox_self_get_address(m, (uint8_t *) myid); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|  |  | ||||||
|         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); |  | ||||||
|         return; |         return; | ||||||
|     } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { |     } 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; |             Friends.list[self->num].logging_on = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         log_disable(log); |         log_disable(log); | ||||||
|  |  | ||||||
|         msg = "Logging disabled."; |         msg = "Logging disabled."; | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; |     msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; | ||||||
|     line_info_add(self, NULL, NULL, NULL, 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]) | 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 id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||||
|     char bin_id[TOX_ADDRESS_SIZE]; |     char bin_id[TOX_ADDRESS_SIZE]; | ||||||
|     tox_self_get_address(m, (uint8_t *) bin_id); |     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) { |     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; |         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]) | 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 id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||||
|     char bin_id[TOX_ADDRESS_SIZE]; |     char bin_id[TOX_ADDRESS_SIZE]; | ||||||
|     tox_self_get_address(m, (uint8_t *) bin_id); |     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) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -444,51 +488,97 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | |||||||
|     nick[nick_len] = '\0'; |     nick[nick_len] = '\0'; | ||||||
|  |  | ||||||
|     size_t data_file_len = strlen(DATA_FILE); |     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); |     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]; | #ifdef QRPNG | ||||||
|     snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); |  | ||||||
|  |  | ||||||
|     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) { | #endif /* QRPNG */ | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); |         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; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     if (ID_to_QRcode(id_string, output) == -1) { |         snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); | ||||||
|         fclose(output); |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); |         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; |             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]) | void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     char nick[MAX_STR_SIZE]; |     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]); |     snprintf(nick, sizeof(nick), "%s", argv[1]); | ||||||
|         len = strlen(nick); |     size_t len = strlen(nick); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!valid_nick(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; |         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]) | void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |  | ||||||
|     if (argc < 1) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (argv[1][0] != '\"') { |     prompt_update_statusmessage(prompt, m, argv[1]); | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes."); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* 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); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|     uint32_t nospam = rand();   /* should be random enough */ |     long int nospam = rand(); | ||||||
|     tox_self_set_nospam(m, nospam); |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your Tox ID has been changed to:"); |     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); |     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]) | 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); |     help_init_menu(self); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |     UNUSED_VAR(argc); | ||||||
|  |     UNUSED_VAR(argv); | ||||||
|  |     UNUSED_VAR(self); | ||||||
|  |  | ||||||
|     exit_toxic_success(m); |     exit_toxic_success(m); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(window); | ||||||
|  |     UNUSED_VAR(m); | ||||||
|  |     UNUSED_VAR(argc); | ||||||
|  |     UNUSED_VAR(argv); | ||||||
|  |  | ||||||
|     if (FrndRequests.num_requests == 0) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -552,8 +664,9 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|     int count = 0; |     int count = 0; | ||||||
|  |  | ||||||
|     for (i = 0; i < FrndRequests.max_idx; ++i) { |     for (i = 0; i < FrndRequests.max_idx; ++i) { | ||||||
|         if (!FrndRequests.request[i].active) |         if (!FrndRequests.request[i].active) { | ||||||
|             continue; |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0}; |         char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0}; | ||||||
|  |  | ||||||
| @@ -563,61 +676,48 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | |||||||
|             strcat(id, d); |             strcat(id, d); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); |         line_info_add(self, false, 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, "%s", FrndRequests.request[i].msg); | ||||||
|  |  | ||||||
|         if (++count < FrndRequests.num_requests) |         if (++count < FrndRequests.num_requests) { | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); |             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]) | 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; |     const char *errmsg; | ||||||
|  |  | ||||||
|     lock_status(); |     lock_status(); | ||||||
|  |  | ||||||
|     if (argc >= 2) { |     if (argc < 1) { | ||||||
|         have_note = true; |  | ||||||
|     } else if (argc < 1) { |  | ||||||
|         errmsg = "Require a status. Statuses are: online, busy and away."; |         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; |         goto finish; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *status_str = argv[1]; |     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; |         status = TOX_USER_STATUS_NONE; | ||||||
|     else if (!strcasecmp(status_str, "away")) |     } else if (!strcasecmp(status_str, "away")) { | ||||||
|         status = TOX_USER_STATUS_AWAY; |         status = TOX_USER_STATUS_AWAY; | ||||||
|     else if (!strcasecmp(status_str, "busy")) |     } else if (!strcasecmp(status_str, "busy")) { | ||||||
|         status = TOX_USER_STATUS_BUSY; |         status = TOX_USER_STATUS_BUSY; | ||||||
|     else { |     } else { | ||||||
|         errmsg = "Invalid status. Valid statuses are: online, busy and away."; |         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; |         goto finish; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     tox_self_set_status(m, status); |     tox_self_set_status(m, status); | ||||||
|     prompt_update_status(prompt, 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: | finish: | ||||||
|     unlock_status(); |     unlock_status(); | ||||||
|   | |||||||
| @@ -23,8 +23,8 @@ | |||||||
| #ifndef GLOBAL_COMMANDS_H | #ifndef GLOBAL_COMMANDS_H | ||||||
| #define GLOBAL_COMMANDS_H | #define GLOBAL_COMMANDS_H | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| @@ -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_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_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_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
|  | #ifdef QRCODE | ||||||
| void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | 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_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | void cmd_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]); | void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||||
| #endif /* VIDEO */ | #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 */ |  | ||||||
							
								
								
									
										188
									
								
								src/help.c
									
									
									
									
									
								
							
							
						
						
									
										188
									
								
								src/help.c
									
									
									
									
									
								
							| @@ -22,24 +22,34 @@ | |||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "help.h" | #include "help.h" | ||||||
| #include "misc_tools.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 | #define HELP_MENU_HEIGHT 9 | ||||||
|  | #endif /* PYTHON */ | ||||||
| #define HELP_MENU_WIDTH 26 | #define HELP_MENU_WIDTH 26 | ||||||
|  |  | ||||||
| void help_init_menu(ToxWindow *self) | void help_init_menu(ToxWindow *self) | ||||||
| { | { | ||||||
|     if (self->help->win) |     if (self->help->win) { | ||||||
|         delwin(self->help->win); |         delwin(self->help->win); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int y2, x2; |     int y2, x2; | ||||||
|     getmaxyx(self->window, 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; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3); |     self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3); | ||||||
|     self->help->active = true; |     self->help->active = true; | ||||||
| @@ -49,19 +59,24 @@ void help_init_menu(ToxWindow *self) | |||||||
| static void help_exit(ToxWindow *self) | static void help_exit(ToxWindow *self) | ||||||
| { | { | ||||||
|     delwin(self->help->win); |     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) | static void help_init_window(ToxWindow *self, int height, int width) | ||||||
| { | { | ||||||
|     if (self->help->win) |     if (self->help->win) { | ||||||
|         delwin(self->help->win); |         delwin(self->help->win); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int y2, x2; |     int y2, x2; | ||||||
|     getmaxyx(stdscr, y2, x2); |     getmaxyx(stdscr, y2, x2); | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) |     if (y2 <= 0 || x2 <= 0) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     height = MIN(height, y2); |     height = MIN(height, y2); | ||||||
|     width = MIN(width, x2); |     width = MIN(width, x2); | ||||||
| @@ -89,11 +104,18 @@ static void help_draw_menu(ToxWindow *self) | |||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); |     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|     wprintw(win, "hat commands\n"); |     wprintw(win, "hat commands\n"); | ||||||
|  |  | ||||||
|     wprintw(win, " g"); |     wprintw(win, " c"); | ||||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|     wprintw(win, "r"); |     wprintw(win, "o"); | ||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); |     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)); |     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||||
|     wprintw(win, " f"); |     wprintw(win, " f"); | ||||||
| @@ -112,14 +134,15 @@ static void help_draw_menu(ToxWindow *self) | |||||||
|     wprintw(win, "it menu\n"); |     wprintw(win, "it menu\n"); | ||||||
|  |  | ||||||
|     box(win, ACS_VLINE, ACS_HLINE); |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|     wrefresh(win); |     wnoutrefresh(win); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void help_draw_bottom_menu(WINDOW *win) | static void help_draw_bottom_menu(WINDOW *win) | ||||||
| { | { | ||||||
|     int y2, x2; |     int y2, x2; | ||||||
|     getmaxyx(win, y2, x2); |     getmaxyx(win, y2, x2); | ||||||
|     (void) x2; |  | ||||||
|  |     UNUSED_VAR(x2); | ||||||
|  |  | ||||||
|     wmove(win, y2 - 2, 1); |     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, "  /status <type> <msg>       : Set status with optional note\n"); | ||||||
|     wprintw(win, "  /note <msg>                : Set a personal note\n"); |     wprintw(win, "  /note <msg>                : Set a personal note\n"); | ||||||
|     wprintw(win, "  /nick <nick>               : Set your nickname\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, "  /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"); |     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"); |     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, "  /clear                     : Clear window history\n"); | ||||||
|     wprintw(win, "  /close                     : Close the current chat window\n"); |     wprintw(win, "  /close                     : Close the current chat window\n"); | ||||||
|     wprintw(win, "  /quit or /exit             : Exit Toxic\n"); |     wprintw(win, "  /quit or /exit             : Exit Toxic\n"); | ||||||
| @@ -181,10 +210,18 @@ static void help_draw_global(ToxWindow *self) | |||||||
|     wprintw(win, "  /svdev <type> <id>         : Set active video device\n"); |     wprintw(win, "  /svdev <type> <id>         : Set active video device\n"); | ||||||
| #endif /* VIDEO */ | #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); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|     box(win, ACS_VLINE, ACS_HLINE); |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|     wrefresh(win); |     wnoutrefresh(win); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void help_draw_chat(ToxWindow *self) | static void help_draw_chat(ToxWindow *self) | ||||||
| @@ -197,8 +234,8 @@ static void help_draw_chat(ToxWindow *self) | |||||||
|     wprintw(win, "Chat Commands:\n"); |     wprintw(win, "Chat Commands:\n"); | ||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|     wprintw(win, "  /invite <n>                : Invite contact to a group chat\n"); |     wprintw(win, "  /invite <n>                : Invite contact to a conference \n"); | ||||||
|     wprintw(win, "  /join                      : Join a pending group chat\n"); |     wprintw(win, "  /join                      : Join a pending conference\n"); | ||||||
|     wprintw(win, "  /sendfile <path>           : Send a file\n"); |     wprintw(win, "  /sendfile <path>           : Send a file\n"); | ||||||
|     wprintw(win, "  /savefile <id>             : Receive a file\n"); |     wprintw(win, "  /savefile <id>             : Receive a file\n"); | ||||||
|     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); |     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); | ||||||
| @@ -215,19 +252,22 @@ static void help_draw_chat(ToxWindow *self) | |||||||
|     wprintw(win, "  /sdev <type> <id>          : Change active device\n"); |     wprintw(win, "  /sdev <type> <id>          : Change active device\n"); | ||||||
|     wprintw(win, "  /mute <type>               : Mute active device if in call\n"); |     wprintw(win, "  /mute <type>               : Mute active device if in call\n"); | ||||||
|     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n"); |     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n"); | ||||||
|  |     wprintw(win, "  /bitrate <n>               : Set the audio encoding bitrate\n"); | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|     wattron(win, A_BOLD); |     wattron(win, A_BOLD); | ||||||
|     wprintw(win, "\n Video:\n"); |     wprintw(win, "\n Video:\n"); | ||||||
|     wattroff(win, A_BOLD); |     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 */ | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|     help_draw_bottom_menu(win); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|     box(win, ACS_VLINE, ACS_HLINE); |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|     wrefresh(win); |     wnoutrefresh(win); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void help_draw_keys(ToxWindow *self) | 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, "  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+F and Ctrl+V         : Scroll window history half a page\n"); | ||||||
|     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); |     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); | ||||||
|     wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n"); |     wprintw(win, "  Ctrl+up and Ctrl+down     : Scroll peer list in conference\n"); | ||||||
|     wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n\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"); |     wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n"); | ||||||
|  |  | ||||||
|     help_draw_bottom_menu(win); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|     box(win, ACS_VLINE, ACS_HLINE); |     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; |     WINDOW *win = self->help->win; | ||||||
|  |  | ||||||
|     wmove(win, 1, 1); |     wmove(win, 1, 1); | ||||||
|  |  | ||||||
|     wattron(win, A_BOLD | COLOR_PAIR(RED)); |     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|     wprintw(win, "Group commands:\n"); |     wprintw(win, "Conference commands:\n"); | ||||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); |     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||||
|  |  | ||||||
|     wprintw(win, "  /title <msg>               : Set group title (show current title if no msg)\n\n"); |     wprintw(win, "  /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); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|     box(win, ACS_VLINE, ACS_HLINE); |     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) | static void help_draw_contacts(ToxWindow *self) | ||||||
| { | { | ||||||
|     WINDOW *win = self->help->win; |     WINDOW *win = self->help->win; | ||||||
| @@ -291,55 +363,72 @@ static void help_draw_contacts(ToxWindow *self) | |||||||
|     help_draw_bottom_menu(win); |     help_draw_bottom_menu(win); | ||||||
|  |  | ||||||
|     box(win, ACS_VLINE, ACS_HLINE); |     box(win, ACS_VLINE, ACS_HLINE); | ||||||
|     wrefresh(win); |     wnoutrefresh(win); | ||||||
| } | } | ||||||
|  |  | ||||||
| void help_onKey(ToxWindow *self, wint_t key) | void help_onKey(ToxWindow *self, wint_t key) | ||||||
| { | { | ||||||
|  |     int height; | ||||||
|  |  | ||||||
|     switch (key) { |     switch (key) { | ||||||
|         case 'x': |         case L'x': | ||||||
|         case T_KEY_ESC: |         case T_KEY_ESC: | ||||||
|             help_exit(self); |             help_exit(self); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'c': |         case L'c': | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|             help_init_window(self, 22, 80); |             help_init_window(self, 25, 80); | ||||||
| #elif AUDIO | #elif AUDIO | ||||||
|             help_init_window(self, 19, 80); |             help_init_window(self, 20, 80); | ||||||
| #else | #else | ||||||
|             help_init_window(self, 10, 80); |             help_init_window(self, 10, 80); | ||||||
| #endif | #endif | ||||||
|             self->help->type = HELP_CHAT; |             self->help->type = HELP_CHAT; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'g': |         case L'g': | ||||||
|  |             height = 22; | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|             help_init_window(self, 30, 80); |             height += 8; | ||||||
| #elif AUDIO | #elif AUDIO | ||||||
|             help_init_window(self, 26, 80); |             height += 4; | ||||||
| #else |  | ||||||
|             help_init_window(self, 22, 80); |  | ||||||
| #endif | #endif | ||||||
|  | #ifdef PYTHON | ||||||
|  |             height += 2; | ||||||
|  | #endif | ||||||
|  |             help_init_window(self, height, 80); | ||||||
|             self->help->type = HELP_GLOBAL; |             self->help->type = HELP_GLOBAL; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'r': |         case L'o': | ||||||
|             help_init_window(self, 6, 80); |             height = 6; | ||||||
|             self->help->type = HELP_GROUP; | #ifdef AUDIO | ||||||
|  |             height += 7; | ||||||
|  | #endif | ||||||
|  |             help_init_window(self, height, 80); | ||||||
|  |             self->help->type = HELP_CONFERENCE; | ||||||
|             break; |             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); |             help_init_window(self, 10, 80); | ||||||
|             self->help->type = HELP_CONTACTS; |             self->help->type = HELP_CONTACTS; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'k': |         case L'k': | ||||||
|             help_init_window(self, 13, 80); |             help_init_window(self, 15, 80); | ||||||
|             self->help->type = HELP_KEYS; |             self->help->type = HELP_KEYS; | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case 'm': |         case L'm': | ||||||
|             help_init_menu(self); |             help_init_menu(self); | ||||||
|             self->help->type = HELP_MENU; |             self->help->type = HELP_MENU; | ||||||
|             break; |             break; | ||||||
| @@ -348,8 +437,6 @@ void help_onKey(ToxWindow *self, wint_t key) | |||||||
|  |  | ||||||
| void help_onDraw(ToxWindow *self) | void help_onDraw(ToxWindow *self) | ||||||
| { | { | ||||||
|     curs_set(0); |  | ||||||
|  |  | ||||||
|     switch (self->help->type) { |     switch (self->help->type) { | ||||||
|         case HELP_MENU: |         case HELP_MENU: | ||||||
|             help_draw_menu(self); |             help_draw_menu(self); | ||||||
| @@ -371,8 +458,15 @@ void help_onDraw(ToxWindow *self) | |||||||
|             help_draw_contacts(self); |             help_draw_contacts(self); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case HELP_GROUP: |         case HELP_CONFERENCE: | ||||||
|             help_draw_group(self); |             help_draw_conference(self); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|  | #ifdef PYTHON | ||||||
|  |  | ||||||
|  |         case HELP_PLUGIN: | ||||||
|  |             help_draw_plugin(self); | ||||||
|  |             break; | ||||||
|  | #endif /* PYTHON */ | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -30,13 +30,16 @@ typedef enum { | |||||||
|     HELP_MENU, |     HELP_MENU, | ||||||
|     HELP_GLOBAL, |     HELP_GLOBAL, | ||||||
|     HELP_CHAT, |     HELP_CHAT, | ||||||
|     HELP_GROUP, |     HELP_CONFERENCE, | ||||||
|     HELP_KEYS, |     HELP_KEYS, | ||||||
|     HELP_CONTACTS, |     HELP_CONTACTS, | ||||||
|  | #ifdef PYTHON | ||||||
|  |     HELP_PLUGIN, | ||||||
|  | #endif | ||||||
| } HELP_TYPES; | } HELP_TYPES; | ||||||
|  |  | ||||||
| void help_onDraw(ToxWindow *self); | void help_onDraw(ToxWindow *self); | ||||||
| void help_init_menu(ToxWindow *self); | void help_init_menu(ToxWindow *self); | ||||||
| void help_onKey(ToxWindow *self, wint_t key); | 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 <wchar.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "conference.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "toxic_strings.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
|  | #include "misc_tools.h" | ||||||
| #include "notify.h" | #include "notify.h" | ||||||
| #include "groupchat.h" |  | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "toxic_strings.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| /* add a char to input field and buffer */ | /* 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; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|  |     /* this is the only place we need to do this check */ | ||||||
|  |     if (key == '\n') { | ||||||
|  |         key = L'¶'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int cur_len = wcwidth(key); |     int cur_len = wcwidth(key); | ||||||
|  |  | ||||||
|     /* this is the only place we need to do this check */ |  | ||||||
|     if (cur_len == -1) { |     if (cur_len == -1) { | ||||||
|         sound_notify(self, notif_error, 0, NULL); |         sound_notify(self, notif_error, 0, NULL); | ||||||
|         return; |         return; | ||||||
| @@ -74,43 +78,46 @@ 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 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; |     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)); |         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); |         ctx->start = MAX(0, ctx->start - cur_len); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* delete a char via delete key from input field and buffer */ | /* delete a char via delete key from input field and buffer */ | ||||||
| static void input_delete(ToxWindow *self) | 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); |         sound_notify(self, notif_error, 0, NULL); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* delete last typed word */ | /* 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; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     if (del_word_buf(ctx) == -1) { |     if (del_word_buf(ctx) == -1) { | ||||||
|         sound_notify(self, notif_error, 0, NULL); |         sound_notify(self, notif_error, 0, NULL); | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* deletes entire line before cursor from input field and buffer */ | /* deletes entire line before cursor from input field and buffer */ | ||||||
| static void input_discard(ToxWindow *self) | 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); |         sound_notify(self, notif_error, 0, NULL); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* deletes entire line after cursor from input field and buffer */ | /* deletes entire line after cursor from input field and buffer */ | ||||||
| static void input_kill(ChatContext *ctx) | static void input_kill(ChatContext *ctx) | ||||||
| { | { | ||||||
|     if (kill_buf(ctx) == -1) |     if (kill_buf(ctx) == -1) { | ||||||
|         sound_notify(NULL, notif_error, NT_ALWAYS, NULL); |         sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static void input_yank(ToxWindow *self, int x, int mx_x) | 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 */ | /* 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; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
| @@ -146,8 +153,9 @@ static void input_mv_home(ToxWindow *self) | |||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     if (ctx->pos <= 0) |     if (ctx->pos <= 0) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ctx->pos = 0; |     ctx->pos = 0; | ||||||
|     ctx->start = 0; |     ctx->start = 0; | ||||||
| @@ -158,27 +166,54 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x) | |||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     if (ctx->pos <= 0) |     if (ctx->pos <= 0) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0; |     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; |     --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)); |         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); |         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 */ | /* moves cursor/line position right in input field and buffer */ | ||||||
| static void input_mv_right(ToxWindow *self, int x, int mx_x) | static void input_mv_right(ToxWindow *self, int x, int mx_x) | ||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     if (ctx->pos >= ctx->len) |     if (ctx->pos >= ctx->len) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ++ctx->pos; |     ++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 */ | /* puts a line history item in input field and buffer */ | ||||||
| static void input_history(ToxWindow *self, wint_t key, int mx_x) | 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. | /* Handles non-printable input keys that behave the same for all types of chat windows. | ||||||
|    return true if key matches a function, false otherwise */ |    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; |     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; |             break; | ||||||
|  |  | ||||||
|         case T_KEY_C_W: |         case T_KEY_C_W: | ||||||
|             input_del_word(self, x, mx_x); |             input_del_word(self); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case KEY_HOME: |         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 KEY_END: | ||||||
|         case T_KEY_C_E: |         case T_KEY_C_E: | ||||||
|             input_mv_end(self, y, mx_x); |             input_mv_end(self, mx_x); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case KEY_LEFT: |         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); |             force_refresh(self->chatwin->history); | ||||||
|             break; |             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: |         default: | ||||||
|             match = false; |             match = false; | ||||||
|             break; |             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. |     /* TODO: this special case is ugly. | ||||||
|        maybe convert entire function to if/else and make them all customizable keys? */ |        maybe convert entire function to if/else and make them all customizable keys? */ | ||||||
|     if (!match && key == user_settings->key_toggle_peerlist) { |     if (!match) { | ||||||
|         if (self->is_groupchat) { |         if (key == user_settings->key_toggle_peerlist) { | ||||||
|  |             if (self->type == WINDOW_TYPE_CONFERENCE) { | ||||||
|                 self->show_peerlist ^= 1; |                 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; |             match = true; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return match; |     return match; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,10 +24,10 @@ | |||||||
| #define INPUT_H | #define INPUT_H | ||||||
|  |  | ||||||
| /* add a char to input field and buffer for given chatcontext */ | /* 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. | /* Handles non-printable input keys that behave the same for all types of chat windows. | ||||||
|    return true if key matches a function, false otherwise */ |    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 <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdarg.h> |  | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "conference.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "groupchat.h" |  | ||||||
| #include "settings.h" |  | ||||||
| #include "notify.h" |  | ||||||
| #include "message_queue.h" | #include "message_queue.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
|  | #include "notify.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | 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)); |     hst->line_root = calloc(1, sizeof(struct line_info)); | ||||||
|  |  | ||||||
|     if (hst->line_root == NULL) |     if (hst->line_root == NULL) { | ||||||
|         exit_toxic_err("failed in line_info_init", FATALERR_MEMORY); |         exit_toxic_err("failed in line_info_init", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     hst->line_start = hst->line_root; |     hst->line_start = hst->line_root; | ||||||
|     hst->line_end = hst->line_start; |     hst->line_end = hst->line_start; | ||||||
|     hst->queue_sz = 0; |     hst->queue_size = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* resets line_start (moves to end of chat history) */ | /* 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; |     struct line_info *line = hst->line_end; | ||||||
|  |  | ||||||
|     if (line->prev == NULL) |     if (line == NULL || line->prev == NULL) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int y2, x2; |     int y2; | ||||||
|  |     int x2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |     UNUSED_VAR(x2); | ||||||
|  |  | ||||||
|     int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; |     int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||||
|     int top_offst = self->is_chat || self->is_prompt ? 2 : 0; |     int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst; | ||||||
|     int max_y = (y2 - CHATBOX_HEIGHT - top_offst); |  | ||||||
|  |  | ||||||
|     int curlines = 0; |     uint16_t curlines = 0; | ||||||
|     int nxtlines = line->newlines + (line->len / (x2 - side_offst)); |  | ||||||
|  |  | ||||||
|     do { |     do { | ||||||
|         curlines += 1 + nxtlines; |         curlines += line->format_lines; | ||||||
|         line = line->prev; |         line = line->prev; | ||||||
|         nxtlines = line->newlines + (line->len / (x2 - side_offst)); |     } while (line->prev && curlines + line->format_lines <= max_y); | ||||||
|     } while (line->prev && curlines + nxtlines < max_y); |  | ||||||
|  |  | ||||||
|     hst->line_start = line; |     hst->line_start = line; | ||||||
|  |  | ||||||
|  |     self->scroll_pause = false; | ||||||
| } | } | ||||||
|  |  | ||||||
| void line_info_cleanup(struct history *hst) | void line_info_cleanup(struct history *hst) | ||||||
| @@ -85,12 +88,11 @@ void line_info_cleanup(struct history *hst) | |||||||
|         tmp1 = tmp2; |         tmp1 = tmp2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int i; |     for (size_t i = 0; i < hst->queue_size; ++i) { | ||||||
|  |         if (hst->queue[i]) { | ||||||
|     for (i = 0; i < hst->queue_sz; ++i) { |  | ||||||
|         if (hst->queue[i]) |  | ||||||
|             free(hst->queue[i]); |             free(hst->queue[i]); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     free(hst); |     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. */ | /* 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) | static struct line_info *line_info_ret_queue(struct history *hst) | ||||||
| { | { | ||||||
|     if (hst->queue_sz <= 0) |     if (hst->queue_size == 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     struct line_info *line = hst->queue[0]; |     struct line_info *line = hst->queue[0]; | ||||||
|  |  | ||||||
|     int i; |     for (size_t i = 0; i < hst->queue_size; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < hst->queue_sz; ++i) |  | ||||||
|         hst->queue[i] = hst->queue[i + 1]; |         hst->queue[i] = hst->queue[i + 1]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     --hst->queue_sz; |     --hst->queue_size; | ||||||
|  |  | ||||||
|     return line; |     return line; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* creates new line_info line and puts it in the queue. */ | /* Prints a maximum of `n` chars from `s` to `win`. | ||||||
| void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, |  * | ||||||
|  |  * 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, ...) |                   uint8_t bold, uint8_t colour, const char *msg, ...) | ||||||
| { | { | ||||||
|     if (!self) |     if (!self) { | ||||||
|         return; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     struct history *hst = self->chatwin->hst; |     struct history *hst = self->chatwin->hst; | ||||||
|  |  | ||||||
|     if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) |     if (hst->queue_size >= MAX_LINE_INFO_QUEUE) { | ||||||
|         return; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     struct line_info *new_line = calloc(1, sizeof(struct line_info)); |     struct line_info *new_line = calloc(1, sizeof(struct line_info)); | ||||||
|  |  | ||||||
|     if (new_line == NULL) |     if (new_line == NULL) { | ||||||
|         exit_toxic_err("failed in line_info_add", FATALERR_MEMORY); |         exit_toxic_err("failed in line_info_add", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     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_list args; | ||||||
|     va_start(args, msg); |     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 */ |     /* for type-specific formatting in print function */ | ||||||
|     switch (type) { |     switch (type) { | ||||||
|         case IN_ACTION: |         case IN_ACTION: | ||||||
|  |  | ||||||
|         /* fallthrough */ |         /* fallthrough */ | ||||||
|         case OUT_ACTION: |         case OUT_ACTION: | ||||||
|             len += strlen(user_settings->line_normal) + 2; |             len += strlen(user_settings->line_normal) + 2; // two spaces | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case IN_MSG: |         case IN_MSG: | ||||||
|  |  | ||||||
|         /* fallthrough */ |         /* fallthrough */ | ||||||
|         case OUT_MSG: |         case OUT_MSG: | ||||||
|             len += strlen(user_settings->line_normal) + 3; |             len += strlen(user_settings->line_normal) + 3; // two spaces and a ':' char | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case CONNECTION: |         case CONNECTION: | ||||||
|             len += strlen(user_settings->line_join) + 2; |             len += strlen(user_settings->line_join) + 2;  // two spaces | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case DISCONNECTION: |         case DISCONNECTION: | ||||||
|             len += strlen(user_settings->line_quit) + 2; |             len += strlen(user_settings->line_quit) + 2;  // two spaces | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case SYS_MSG: |         case SYS_MSG: | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case NAME_CHANGE: |         case NAME_CHANGE: | ||||||
|             len += strlen(user_settings->line_alert) + 1; |             len += strlen(user_settings->line_alert) + 2;  // two spaces | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         case PROMPT: |         case PROMPT: | ||||||
|             ++len; |             len += 2;  // '$' char and a space | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|         default: |         default: | ||||||
| @@ -193,20 +408,16 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons | |||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     uint16_t msg_len = 0; | ||||||
|  |  | ||||||
|     if (frmt_msg[0]) { |     if (frmt_msg[0]) { | ||||||
|         snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg); |         snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg); | ||||||
|         len += strlen(new_line->msg); |         msg_len = strlen(new_line->msg); | ||||||
|  |         len += msg_len; | ||||||
|         int i; |  | ||||||
|  |  | ||||||
|         for (i = 0; frmt_msg[i]; ++i) { |  | ||||||
|             if (frmt_msg[i] == '\n') |  | ||||||
|                 ++new_line->newlines; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (timestr) { |     if (show_timestamp) { | ||||||
|         snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr); |         get_time_str(new_line->timestr, sizeof(new_line->timestr)); | ||||||
|         len += strlen(new_line->timestr) + 1; |         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); |         len += strlen(new_line->name2); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     new_line->id = (hst->line_end->id + 1 + hst->queue_size) % INT_MAX; | ||||||
|     new_line->len = len; |     new_line->len = len; | ||||||
|  |     new_line->msg_len = msg_len; | ||||||
|     new_line->type = type; |     new_line->type = type; | ||||||
|     new_line->bold = bold; |     new_line->bold = bold; | ||||||
|     new_line->colour = colour; |     new_line->colour = colour; | ||||||
|     new_line->noread_flag = false; |     new_line->noread_flag = false; | ||||||
|     new_line->timestamp = get_unix_time(); |     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() */ | /* 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 history *hst = self->chatwin->hst; | ||||||
|     struct line_info *line = line_info_ret_queue(hst); |     struct line_info *line = line_info_ret_queue(hst); | ||||||
|  |  | ||||||
|     if (line == NULL) |     if (line == NULL) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (hst->start_id > user_settings->history_size) |     if (hst->start_id > user_settings->history_size) { | ||||||
|         line_info_root_fwd(hst); |         line_info_root_fwd(hst); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     line->id = hst->line_end->id + 1; |  | ||||||
|     line->prev = hst->line_end; |     line->prev = hst->line_end; | ||||||
|     hst->line_end->next = line; |     hst->line_end->next = line; | ||||||
|     hst->line_end = line; |     hst->line_end = line; | ||||||
|  |     hst->line_end->id = line->id; | ||||||
|  |  | ||||||
|     int y, y2, x, x2; |     if (!self->scroll_pause) { | ||||||
|     getmaxyx(self->window, y2, x2); |         line_info_reset_start(self, hst); | ||||||
|     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; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -275,8 +477,9 @@ void line_info_print(ToxWindow *self) | |||||||
| { | { | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     if (ctx == NULL) |     if (ctx == NULL) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     struct history *hst = ctx->hst; |     struct history *hst = ctx->hst; | ||||||
|  |  | ||||||
| @@ -284,28 +487,55 @@ void line_info_print(ToxWindow *self) | |||||||
|     line_info_check_queue(self); |     line_info_check_queue(self); | ||||||
|  |  | ||||||
|     WINDOW *win = ctx->history; |     WINDOW *win = ctx->history; | ||||||
|  |  | ||||||
|     wclear(win); |     wclear(win); | ||||||
|     int y2, x2; |  | ||||||
|  |     int y2; | ||||||
|  |  | ||||||
|  |     int x2; | ||||||
|  |  | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (x2 <= SIDEBAR_WIDTH) |     if (x2 - 1 <= SIDEBAR_WIDTH) {  // leave room on x axis for sidebar padding | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (self->is_groupchat) |     if (self->type == WINDOW_TYPE_CONFERENCE) { | ||||||
|         wmove(win, 0, 0); |         wmove(win, 0, 0); | ||||||
|     else |     } else { | ||||||
|         wmove(win, 2, 0); |         wmove(win, TOP_BAR_HEIGHT, 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     struct line_info *line = hst->line_start->next; |     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; |         uint8_t type = line->type; | ||||||
|  |  | ||||||
|         switch (type) { |         switch (type) { | ||||||
|             case OUT_MSG: |             case OUT_MSG: | ||||||
|  |  | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case OUT_MSG_READ: |             case OUT_MSG_READ: | ||||||
|  |  | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case IN_MSG: |             case IN_MSG: | ||||||
|                 wattron(win, COLOR_PAIR(BLUE)); |                 wattron(win, COLOR_PAIR(BLUE)); | ||||||
| @@ -314,44 +544,49 @@ void line_info_print(ToxWindow *self) | |||||||
|  |  | ||||||
|                 int nameclr = GREEN; |                 int nameclr = GREEN; | ||||||
|  |  | ||||||
|                 if (line->colour) |                 if (line->colour) { | ||||||
|                     nameclr = line->colour; |                     nameclr = line->colour; | ||||||
|                 else if (type == IN_MSG) |                 } else if (type == IN_MSG) { | ||||||
|                     nameclr = CYAN; |                     nameclr = CYAN; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 wattron(win, COLOR_PAIR(nameclr)); |                 wattron(win, COLOR_PAIR(nameclr)); | ||||||
|                 wprintw(win, "%s %s: ", user_settings->line_normal, line->name1); |                 wprintw(win, "%s %s: ", user_settings->line_normal, line->name1); | ||||||
|                 wattroff(win, COLOR_PAIR(nameclr)); |                 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)); |                     wattron(win, COLOR_PAIR(GREEN)); | ||||||
|                 else if (line->msg[0] == '<') |                 } else if (line->msg[0] == '<') { | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |                     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)); |                     wattroff(win, COLOR_PAIR(GREEN)); | ||||||
|                 else if (line->msg[0] == '<') |                 } else if (line->msg[0] == '<') { | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |                     wattroff(win, COLOR_PAIR(RED)); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { |                 if (type == OUT_MSG && !line->read_flag) { | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |                     if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||||
|                     wprintw(win, " x", line->msg); |  | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |  | ||||||
|  |  | ||||||
|                     if (line->noread_flag == false) { |  | ||||||
|                         line->noread_flag = true; |                         line->noread_flag = true; | ||||||
|                         line->len += 2; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "\n", line->msg); |                 waddch(win, '\n'); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case OUT_ACTION_READ: |             case OUT_ACTION_READ: | ||||||
|  |  | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case OUT_ACTION: |             case OUT_ACTION: | ||||||
|  |  | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
|             case IN_ACTION: |             case IN_ACTION: | ||||||
|                 wattron(win, COLOR_PAIR(BLUE)); |                 wattron(win, COLOR_PAIR(BLUE)); | ||||||
| @@ -359,21 +594,17 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wattroff(win, COLOR_PAIR(BLUE)); |                 wattroff(win, COLOR_PAIR(BLUE)); | ||||||
|  |  | ||||||
|                 wattron(win, COLOR_PAIR(YELLOW)); |                 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)); |                 wattroff(win, COLOR_PAIR(YELLOW)); | ||||||
|  |  | ||||||
|                 if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { |                 if (type == OUT_ACTION && !line->read_flag) { | ||||||
|                     wattron(win, COLOR_PAIR(RED)); |                     if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||||
|                     wprintw(win, " x", line->msg); |  | ||||||
|                     wattroff(win, COLOR_PAIR(RED)); |  | ||||||
|  |  | ||||||
|                     if (line->noread_flag == false) { |  | ||||||
|                         line->noread_flag = true; |                         line->noread_flag = true; | ||||||
|                         line->len += 2; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "\n", line->msg); |                 waddch(win, '\n'); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case SYS_MSG: |             case SYS_MSG: | ||||||
| @@ -383,19 +614,24 @@ void line_info_print(ToxWindow *self) | |||||||
|                     wattroff(win, COLOR_PAIR(BLUE)); |                     wattroff(win, COLOR_PAIR(BLUE)); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (line->bold) |                 if (line->bold) { | ||||||
|                     wattron(win, A_BOLD); |                     wattron(win, A_BOLD); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 if (line->colour) |                 if (line->colour) { | ||||||
|                     wattron(win, COLOR_PAIR(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); |                     wattroff(win, A_BOLD); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 if (line->colour) |                 if (line->colour) { | ||||||
|                     wattroff(win, COLOR_PAIR(line->colour)); |                     wattroff(win, COLOR_PAIR(line->colour)); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
| @@ -404,10 +640,11 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wprintw(win, "$ "); |                 wprintw(win, "$ "); | ||||||
|                 wattroff(win, COLOR_PAIR(GREEN)); |                 wattroff(win, COLOR_PAIR(GREEN)); | ||||||
|  |  | ||||||
|                 if (line->msg[0]) |                 if (line->msg[0]) { | ||||||
|                     wprintw(win, "%s", line->msg); |                     print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 wprintw(win, "\n"); |                 waddch(win, '\n'); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case CONNECTION: |             case CONNECTION: | ||||||
| @@ -422,7 +659,9 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wprintw(win, "%s ", line->name1); |                 wprintw(win, "%s ", line->name1); | ||||||
|                 wattroff(win, A_BOLD); |                 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)); |                 wattroff(win, COLOR_PAIR(line->colour)); | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
| @@ -439,7 +678,9 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wprintw(win, "%s ", line->name1); |                 wprintw(win, "%s ", line->name1); | ||||||
|                 wattroff(win, A_BOLD); |                 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)); |                 wattroff(win, COLOR_PAIR(line->colour)); | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
| @@ -455,7 +696,7 @@ void line_info_print(ToxWindow *self) | |||||||
|                 wprintw(win, "%s", line->name1); |                 wprintw(win, "%s", line->name1); | ||||||
|                 wattroff(win, A_BOLD); |                 wattroff(win, A_BOLD); | ||||||
|  |  | ||||||
|                 wprintw(win, "%s", line->msg); |                 print_ret = print_wrap(win, line, max_x, max_y); | ||||||
|  |  | ||||||
|                 wattron(win, A_BOLD); |                 wattron(win, A_BOLD); | ||||||
|                 wprintw(win, "%s\n", line->name2); |                 wprintw(win, "%s\n", line->name2); | ||||||
| @@ -469,9 +710,42 @@ void line_info_print(ToxWindow *self) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* keep calling until queue is empty */ |     /* keep calling until queue is empty */ | ||||||
|     if (hst->queue_sz > 0) |     if (hst->queue_size > 0) { | ||||||
|         line_info_print(self); |         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 */ | /* puts msg in specified line_info msg buffer */ | ||||||
| void line_info_set(ToxWindow *self, uint32_t id, char *msg) | void line_info_set(ToxWindow *self, uint32_t id, char *msg) | ||||||
| @@ -480,6 +754,9 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg) | |||||||
|  |  | ||||||
|     while (line) { |     while (line) { | ||||||
|         if (line->id == id) { |         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); |             snprintf(line->msg, sizeof(line->msg), "%s", msg); | ||||||
|             return; |             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; |     if (hst->line_start->prev) { | ||||||
| } */ |  | ||||||
|  |  | ||||||
| static void line_info_scroll_up(struct history *hst) |  | ||||||
| { |  | ||||||
|     if (hst->line_start->prev) |  | ||||||
|         hst->line_start = 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) |     struct line_info *next = hst->line_start->next; | ||||||
|         hst->line_start = hst->line_start->next; |  | ||||||
|     else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); |     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) | static void line_info_page_up(ToxWindow *self, struct history *hst) | ||||||
| { | { | ||||||
|     int x2, y2; |     int x2; | ||||||
|  |     int y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     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; |         hst->line_start = hst->line_start->prev; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     self->scroll_pause = true; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void line_info_page_down(ToxWindow *self, struct history *hst) | static void line_info_page_down(ToxWindow *self, struct history *hst) | ||||||
| { | { | ||||||
|     int x2, y2; |     if (!self->scroll_pause) { | ||||||
|     getmaxyx(self->window, y2, x2); |         return; | ||||||
|     (void) x2; |     } | ||||||
|     int jump_dist = y2 / 2; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < jump_dist && hst->line_start->next; ++i) |     int x2; | ||||||
|         hst->line_start = hst->line_start->next; |     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) | 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) { |     if (key == user_settings->key_half_page_up) { | ||||||
|         line_info_page_up(self, hst); |         line_info_page_up(self, hst); | ||||||
|     } |     } else if (key == user_settings->key_half_page_down) { | ||||||
|     else if (key == user_settings->key_half_page_down) { |  | ||||||
|         line_info_page_down(self, hst); |         line_info_page_down(self, hst); | ||||||
|     } |     } else if (key == user_settings->key_scroll_line_up) { | ||||||
|     else if (key == user_settings->key_scroll_line_up) { |         line_info_scroll_up(self, hst); | ||||||
|         line_info_scroll_up(hst); |     } else if (key == user_settings->key_scroll_line_down) { | ||||||
|     } |         line_info_scroll_down(self, hst); | ||||||
|     else if (key == user_settings->key_scroll_line_down) { |     } else if (key == user_settings->key_page_bottom) { | ||||||
|         line_info_scroll_down(hst); |  | ||||||
|     } |  | ||||||
|     else if (key == user_settings->key_page_bottom) { |  | ||||||
|         line_info_reset_start(self, hst); |         line_info_reset_start(self, hst); | ||||||
|     } |     } else { | ||||||
|     else { |  | ||||||
|         match = false; |         match = false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,15 +23,15 @@ | |||||||
| #ifndef LINE_INFO_H | #ifndef LINE_INFO_H | ||||||
| #define LINE_INFO_H | #define LINE_INFO_H | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| #define MAX_HISTORY 100000 | #define MAX_HISTORY 100000 | ||||||
| #define MIN_HISTORY 40 | #define MIN_HISTORY 40 | ||||||
| #define MAX_LINE_INFO_QUEUE 1024 | #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, |     SYS_MSG, | ||||||
|     IN_MSG, |     IN_MSG, | ||||||
|     OUT_MSG, |     OUT_MSG, | ||||||
| @@ -50,14 +50,16 @@ struct line_info { | |||||||
|     char name1[TOXIC_MAX_NAME_LENGTH + 1]; |     char name1[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     char name2[TOXIC_MAX_NAME_LENGTH + 1]; |     char name2[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|     char msg[MAX_LINE_INFO_MSG_SIZE]; |     char msg[MAX_LINE_INFO_MSG_SIZE]; | ||||||
|     uint64_t timestamp; |     time_t timestamp; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|     uint8_t bold; |     uint8_t bold; | ||||||
|     uint8_t colour; |     uint8_t colour; | ||||||
|     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; |     uint32_t id; | ||||||
|     uint16_t len;   /* combined len of entire line */ |     uint16_t len;        /* combined length of entire line */ | ||||||
|     uint8_t newlines; |     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 *prev; | ||||||
|     struct line_info *next; |     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 */ |     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]; |     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. */ | /* 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, |  * | ||||||
|  |  * 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, ...); |                   uint8_t bold, uint8_t colour, const char *msg, ...); | ||||||
|  |  | ||||||
| /* Prints a section of history starting at line_start */ | /* Prints a section of history starting at line_start */ | ||||||
| @@ -96,4 +102,4 @@ void line_info_reset_start(ToxWindow *self, struct history *hst); | |||||||
| void line_info_init(struct history *hst); | void line_info_init(struct history *hst); | ||||||
| bool line_info_onKey(ToxWindow *self, wint_t key);    /* returns true if key is a match */ | bool line_info_onKey(ToxWindow *self, wint_t key);    /* returns true if key is a match */ | ||||||
|  |  | ||||||
| #endif /* #define LINE_INFO_H */ | #endif /* LINE_INFO_H */ | ||||||
|   | |||||||
							
								
								
									
										307
									
								
								src/log.c
									
									
									
									
									
								
							
							
						
						
									
										307
									
								
								src/log.c
									
									
									
									
									
								
							| @@ -22,41 +22,46 @@ | |||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <time.h> |  | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  | #include <time.h> | ||||||
|  |  | ||||||
| #include "configdir.h" | #include "configdir.h" | ||||||
|  | #include "line_info.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "misc_tools.h" | ||||||
|  | #include "settings.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "log.h" |  | ||||||
| #include "settings.h" |  | ||||||
| #include "line_info.h" |  | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | 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) | /* Creates a log path and puts it in `dest. | ||||||
|    A prompt log is in the format: LOGDIR/selfkey-home.log |  * | ||||||
|    A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log |  * There are two types of logs: chat logs and prompt logs (see LOG_TYPE in log.h) | ||||||
|    A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log |  * A prompt log is in the format: LOGDIR/selfkey-home.log | ||||||
|  |  * A chat log is in the format: LOGDIR/selfkey-name-otherkey.log | ||||||
|    Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used. |  * | ||||||
|  |  * For friend chats `otherkey` is the first 6 bytes of the friend's Tox ID. | ||||||
|    Returns 0 on success, -1 if the path is too long */ |  * For Conferences/groups `otherkey` is the first 6 bytes of the group's unique ID. | ||||||
| static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype) |  * | ||||||
|  |  * 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; |         name = UNKNOWN_NAME; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const char *namedash = logtype == LOG_PROMPT ? "" : "-"; |     const char *namedash = otherkey ? "-" : ""; | ||||||
|     const char *set_path = user_settings->chatlogs_path; |     const char *set_path = user_settings->chatlogs_path; | ||||||
|  |  | ||||||
|     char *user_config_dir = get_user_config_dir(); |     char *user_config_dir = get_user_config_dir(); | ||||||
|     int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash); |     int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash); | ||||||
|     path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR); |     path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR); | ||||||
|  |  | ||||||
|     /* first 6 digits of selfkey */ |     /* first 6 bytes of selfkey */ | ||||||
|     char self_id[32]; |     char self_id[32] = {0}; | ||||||
|     path_len += KEY_IDENT_DIGITS * 2; |     path_len += KEY_IDENT_DIGITS * 2; | ||||||
|     sprintf(&self_id[0], "%02X", selfkey[0] & 0xff); |     sprintf(&self_id[0], "%02X", selfkey[0] & 0xff); | ||||||
|     sprintf(&self_id[2], "%02X", selfkey[1] & 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}; |     char other_id[32] = {0}; | ||||||
|  |  | ||||||
|     switch (logtype) { |     if (otherkey) { | ||||||
|         case LOG_CHAT: |         /* first 6 bytes of otherkey */ | ||||||
|         path_len += KEY_IDENT_DIGITS * 2; |         path_len += KEY_IDENT_DIGITS * 2; | ||||||
|         sprintf(&other_id[0], "%02X", otherkey[0] & 0xff); |         sprintf(&other_id[0], "%02X", otherkey[0] & 0xff); | ||||||
|         sprintf(&other_id[2], "%02X", otherkey[1] & 0xff); |         sprintf(&other_id[2], "%02X", otherkey[1] & 0xff); | ||||||
|         sprintf(&other_id[4], "%02X", otherkey[2] & 0xff); |         sprintf(&other_id[4], "%02X", otherkey[2] & 0xff); | ||||||
|         other_id[KEY_IDENT_DIGITS * 2] = '\0'; |         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) { |     if (path_len >= destsize) { | ||||||
| @@ -85,32 +84,43 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke | |||||||
|         return -1; |         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); |         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); |         snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     free(user_config_dir); |     free(user_config_dir); | ||||||
|  |  | ||||||
|     return 0; |     return path_len; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Opens log file or creates a new one */ | /* Initializes log path for `log`. | ||||||
| static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) |  * | ||||||
|  |  * 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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (selfkey == NULL || (type == LOG_TYPE_CHAT && otherkey == NULL)) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char log_path[MAX_STR_SIZE]; |     char log_path[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|     if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) |     int path_len = get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey); | ||||||
|         return -1; |  | ||||||
|  |  | ||||||
|     log->file = fopen(log_path, "a+"); |     if (path_len == -1 || path_len >= sizeof(log->path)) { | ||||||
|     snprintf(log->path, sizeof(log->path), "%s", log_path); |  | ||||||
|  |  | ||||||
|     if (log->file == NULL) |  | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(log->path, log_path, path_len); | ||||||
|  |     log->path[path_len] = 0; | ||||||
|  |  | ||||||
|     return 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) | void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event) | ||||||
| { | { | ||||||
|     if (!log->log_on) |     if (log == NULL) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!log->log_on) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (log->file == NULL) { |     if (log->file == NULL) { | ||||||
|         log->log_on = false; |         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]; |     char name_frmt[TOXIC_MAX_NAME_LENGTH + 3]; | ||||||
|  |  | ||||||
|     if (event) |     if (event) { | ||||||
|         snprintf(name_frmt, sizeof(name_frmt), "* %s", name); |         snprintf(name_frmt, sizeof(name_frmt), "* %s", name); | ||||||
|     else |     } else { | ||||||
|         snprintf(name_frmt, sizeof(name_frmt), "%s:", name); |         snprintf(name_frmt, sizeof(name_frmt), "%s:", name); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const char *t = user_settings->log_timestamp_format; |     const char *t = user_settings->log_timestamp_format; | ||||||
|     char s[MAX_STR_SIZE]; |     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) | void log_disable(struct chatlog *log) | ||||||
| { | { | ||||||
|     if (log->file != NULL) |     if (log == NULL) { | ||||||
|         fclose(log->file); |         return; | ||||||
|  |  | ||||||
|     memset(log, 0, sizeof(struct chatlog)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) |     if (log->file != NULL) { | ||||||
|  |         fclose(log->file); | ||||||
|  |         log->file = NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     log->lastwrite = 0; | ||||||
|  |     log->log_on = false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int log_enable(struct chatlog *log) | ||||||
| { | { | ||||||
|     log->log_on = true; |     if (log == NULL) { | ||||||
|  |  | ||||||
|     if (log->file != NULL) |  | ||||||
|         return 0; |  | ||||||
|  |  | ||||||
|     if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) { |  | ||||||
|         log_disable(log); |  | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (log->log_on) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| /* Loads previous history from chat log */ |     if (*log->path == 0) { | ||||||
| void load_chat_history(ToxWindow *self, struct chatlog *log) |         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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 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) |     if (log == NULL) { | ||||||
|         return; |         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); |     off_t sz = file_size(log->path); | ||||||
|  |  | ||||||
|     if (sz <= 0) |     if (sz <= 0) { | ||||||
|         return; |         return 0; | ||||||
|  |  | ||||||
|     char *hstbuf = malloc(sz + 1); |  | ||||||
|  |  | ||||||
|     if (hstbuf == NULL) |  | ||||||
|         exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY); |  | ||||||
|  |  | ||||||
|     if (fseek(log->file, 0L, SEEK_SET) == -1) { |  | ||||||
|         free(hstbuf); |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file"); |  | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (fread(hstbuf, sz, 1, log->file) != 1) { |     FILE *fp = fopen(log->path, "r"); | ||||||
|         free(hstbuf); |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file"); |     if (fp == NULL) { | ||||||
|         return; |         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 */ |     /* 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 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 */ |     /* start at end and backtrace L lines or to the beginning of buffer */ | ||||||
|     for (start = sz - 1; start >= 0 && count < L; --start) { |     for (start = sz - 1; start >= 0 && count < L; --start) { | ||||||
|         if (hstbuf[start] == '\n') |         if (buf[start] == '\n') { | ||||||
|             ++count; |             ++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) { |     if (line == NULL) { | ||||||
|         free(hstbuf); |         free(buf); | ||||||
|         return; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (line != NULL && count--) { |     while (line != NULL && count--) { | ||||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", line); | ||||||
|         line = strtok(NULL, "\n"); |         line = strtok_r(NULL, "\n", &tmp); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, YELLOW, "---"); | ||||||
|     free(hstbuf); |  | ||||||
|  |     free(buf); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* renames chatlog file replacing src with dest. | /* Renames chatlog file `src` to `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) |  * 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); |     ToxWindow *toxwin = get_window_ptr(winnum); | ||||||
|     struct chatlog *log = NULL; |     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 */ |     /* disable log if necessary and save its state */ | ||||||
|     if (toxwin != NULL) { |     if (toxwin != NULL) { | ||||||
|         log = toxwin->chatwin->log; |         log = toxwin->chatwin->log; | ||||||
|  |  | ||||||
|  |         if (log == NULL) { | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         log_on = log->log_on; |         log_on = log->log_on; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (log_on) |     if (log_on) { | ||||||
|         log_disable(log); |         log_disable(log); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char newpath[MAX_STR_SIZE]; |     char newpath[MAX_STR_SIZE]; | ||||||
|     char oldpath[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; |         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; |         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; |         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; |         goto on_error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (log_on) |     if (log != NULL) { | ||||||
|         log_enable(dest, selfkey, otherkey, log, LOG_CHAT); |         memcpy(log->path, newpath, new_path_len); | ||||||
|  |         log->path[new_path_len] = 0; | ||||||
|  |  | ||||||
|  |         if (log_on) { | ||||||
|  |             log_enable(log); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| on_error: | on_error: | ||||||
|     if (log_on) |  | ||||||
|         log_enable(src, selfkey, otherkey, log, LOG_CHAT); |     if (log_on) { | ||||||
|  |         log_enable(log); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								src/log.h
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								src/log.h
									
									
									
									
									
								
							| @@ -25,35 +25,48 @@ | |||||||
|  |  | ||||||
| struct chatlog { | struct chatlog { | ||||||
|     FILE *file; |     FILE *file; | ||||||
|     uint64_t lastwrite; |     time_t lastwrite; | ||||||
|     char path[MAX_STR_SIZE]; |     char path[MAX_STR_SIZE]; | ||||||
|     bool log_on;    /* specific to current chat window */ |     bool log_on;    /* specific to current chat window */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum { | typedef enum LOG_TYPE { | ||||||
|     LOG_GROUP, |     LOG_TYPE_PROMPT, | ||||||
|     LOG_PROMPT, |     LOG_TYPE_CHAT, | ||||||
|     LOG_CHAT, |  | ||||||
| } LOG_TYPE; | } 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 */ | /* formats/writes line to log file */ | ||||||
| void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event); | void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event); | ||||||
|  |  | ||||||
| /* enables logging for specified log and creates/fetches file if necessary. | /* enables logging for specified log. | ||||||
|  * |  * | ||||||
|  * Returns 0 on success. |  * Returns 0 on success. | ||||||
|  * Returns -1 on failure. |  * 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 */ | /* disables logging for specified log and closes file */ | ||||||
| void log_disable(struct chatlog *log); | void log_disable(struct chatlog *log); | ||||||
|  |  | ||||||
| /* Loads previous history from chat log */ | /* Loads chat log history and prints it to `self` window. | ||||||
| void load_chat_history(ToxWindow *self, struct chatlog *log); |  * | ||||||
|  |  * 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. | /* Renames chatlog file `src` to `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); |  * 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 <stdlib.h> | ||||||
|  |  | ||||||
| #include "toxic.h" |  | ||||||
| #include "windows.h" |  | ||||||
| #include "message_queue.h" |  | ||||||
| #include "misc_tools.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "log.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) | void cqueue_cleanup(struct chat_queue *q) | ||||||
| { | { | ||||||
| @@ -42,19 +42,24 @@ void cqueue_cleanup(struct chat_queue *q) | |||||||
|     free(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)); |     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); |         exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     snprintf(new_m->message, sizeof(new_m->message), "%s", msg); |     snprintf(new_m->message, sizeof(new_m->message), "%s", msg); | ||||||
|     new_m->len = len; |     new_m->len = len; | ||||||
|     new_m->type = type; |     new_m->type = type; | ||||||
|     new_m->line_id = line_id; |     new_m->line_id = line_id; | ||||||
|     new_m->last_send_try = 0; |     new_m->last_send_try = 0; | ||||||
|     new_m->receipt = 0; |     new_m->receipt = -1; | ||||||
|     new_m->next = NULL; |     new_m->next = NULL; | ||||||
|  |  | ||||||
|     if (q->root == 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; |         line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ; | ||||||
|  |  | ||||||
|         if (line->noread_flag == true) { |         if (line->noread_flag) { | ||||||
|             line->len -= 2; |  | ||||||
|             line->noread_flag = false; |             line->noread_flag = false; | ||||||
|  |             line->read_flag = true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return; |         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. */ | /* 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) | void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | ||||||
| { | { | ||||||
|  |     struct chatlog *log = self->chatwin->log; | ||||||
|     struct chat_queue *q = self->chatwin->cqueue; |     struct chat_queue *q = self->chatwin->cqueue; | ||||||
|     struct cqueue_msg *msg = q->root; |     struct cqueue_msg *msg = q->root; | ||||||
|  |  | ||||||
| @@ -102,53 +108,84 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (log->log_on) { | ||||||
|             char selfname[TOX_MAX_NAME_LENGTH]; |             char selfname[TOX_MAX_NAME_LENGTH]; | ||||||
|             tox_self_get_name(m, (uint8_t *) selfname); |             tox_self_get_name(m, (uint8_t *) selfname); | ||||||
|  |  | ||||||
|             size_t len = tox_self_get_name_size(m); |             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); |         cqueue_mark_read(self, msg); | ||||||
|  |  | ||||||
|         struct cqueue_msg *next = msg->next; |         struct cqueue_msg *next = msg->next; | ||||||
|  |  | ||||||
|         if (msg->prev == NULL) {    /* root */ |         if (msg->prev == NULL) {    /* root */ | ||||||
|             if (next) |             if (next) { | ||||||
|                 next->prev = NULL; |                 next->prev = NULL; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             free(msg); |  | ||||||
|             q->root = next; |             q->root = next; | ||||||
|         } else { |         } else { | ||||||
|             struct cqueue_msg *prev = msg->prev; |             struct cqueue_msg *prev = msg->prev; | ||||||
|             free(msg); |  | ||||||
|             prev->next = next; |             prev->next = next; | ||||||
|  |             next->prev = prev; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         free(msg); | ||||||
|  |  | ||||||
|         return; |         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) | void cqueue_try_send(ToxWindow *self, Tox *m) | ||||||
| { | { | ||||||
|     struct chat_queue *q = self->chatwin->cqueue; |     struct chat_queue *q = self->chatwin->cqueue; | ||||||
|     struct cqueue_msg *msg = q->root; |     struct cqueue_msg *msg = q->root; | ||||||
|  |  | ||||||
|     if (!msg) |     while (msg) { | ||||||
|         return; |         if (msg->receipt != -1) { | ||||||
|  |             // we can no longer try to send unsent messages until we get receipts for our previous sent | ||||||
|     if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) |             // messages, but we continue to iterate the list, checking timestamps for any further | ||||||
|         return; |             // successfully sent messages that have not yet gotten a receipt. | ||||||
|  |             cqueue_check_timeouts(msg); | ||||||
|     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; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         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; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         msg->receipt = receipt; | ||||||
|  |         msg->last_send_try = get_unix_time(); | ||||||
|  |         msg = msg->next; | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -27,9 +27,9 @@ struct cqueue_msg { | |||||||
|     char message[MAX_STR_SIZE]; |     char message[MAX_STR_SIZE]; | ||||||
|     size_t len; |     size_t len; | ||||||
|     int line_id; |     int line_id; | ||||||
|  |     time_t last_send_try; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|     uint32_t receipt; |     int64_t receipt; | ||||||
|     uint64_t last_send_try; |  | ||||||
|     struct cqueue_msg *next; |     struct cqueue_msg *next; | ||||||
|     struct cqueue_msg *prev; |     struct cqueue_msg *prev; | ||||||
| }; | }; | ||||||
| @@ -40,12 +40,15 @@ struct chat_queue { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| void cqueue_cleanup(struct chat_queue *q); | 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); | 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. */ | /* 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); | void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt); | ||||||
|  |  | ||||||
| #endif  /* #define MESSAGE_QUEUE_H */ | #endif /* MESSAGE_QUEUE_H */ | ||||||
|   | |||||||
							
								
								
									
										437
									
								
								src/misc_tools.c
									
									
									
									
									
								
							
							
						
						
									
										437
									
								
								src/misc_tools.c
									
									
									
									
									
								
							| @@ -20,95 +20,121 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <arpa/inet.h> | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| #include <string.h> |  | ||||||
| #include <time.h> |  | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
| #include <dirent.h> | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  | #include <time.h> | ||||||
|  |  | ||||||
| #include "toxic.h" | #include "file_transfers.h" | ||||||
| #include "windows.h" |  | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
| #include "file_transfers.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| extern ToxWindow *prompt; | extern ToxWindow *prompt; | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| static uint64_t current_unix_time; | void clear_screen(void) | ||||||
|  | { | ||||||
|  |     printf("\033[2J\033[1;1H"); | ||||||
|  | } | ||||||
|  |  | ||||||
| void hst_to_net(uint8_t *num, uint16_t numbytes) | void hst_to_net(uint8_t *num, uint16_t numbytes) | ||||||
| { | { | ||||||
| #ifndef WORDS_BIGENDIAN | #ifndef WORDS_BIGENDIAN | ||||||
|     uint32_t i; |     uint8_t *buff = malloc(numbytes); | ||||||
|     uint8_t buff[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]; |         buff[i] = num[numbytes - i - 1]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memcpy(num, buff, numbytes); |     memcpy(num, buff, numbytes); | ||||||
|  |     free(buff); | ||||||
| #endif | #endif | ||||||
|     return; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Note: The time functions are not thread safe */ | time_t get_unix_time(void) | ||||||
| void update_unix_time(void) |  | ||||||
| { | { | ||||||
|     current_unix_time = (uint64_t) time(NULL); |     return time(NULL); | ||||||
| } |  | ||||||
|  |  | ||||||
| uint64_t get_unix_time(void) |  | ||||||
| { |  | ||||||
|     return current_unix_time; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | /* Returns 1 if connection has timed out, 0 otherwise */ | ||||||
| int timed_out(uint64_t timestamp, uint64_t timeout) | int timed_out(time_t timestamp, time_t timeout) | ||||||
| { | { | ||||||
|     return timestamp + timeout <= get_unix_time(); |     return timestamp + timeout <= get_unix_time(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* 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 */ | /* Get the current local time */ | ||||||
| struct tm *get_time(void) | struct tm *get_time(void) | ||||||
| { | { | ||||||
|     struct tm *timeinfo; |     struct tm *timeinfo; | ||||||
|     uint64_t t = get_unix_time(); |     time_t t = get_unix_time(); | ||||||
|     timeinfo = localtime((const time_t *) &t); |     timeinfo = localtime((const time_t *) &t); | ||||||
|     return timeinfo; |     return timeinfo; | ||||||
| } | } | ||||||
|  |  | ||||||
| /*Puts the current time in buf in the format of [HH:mm:ss] */ | /* Puts the current time in buf in the format of specified by the config */ | ||||||
| void get_time_str(char *buf, int bufsize) | void get_time_str(char *buf, size_t bufsize) | ||||||
| { | { | ||||||
|  |     if (buf == NULL || bufsize == 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     *buf = 0; | ||||||
|  |  | ||||||
|     if (user_settings->timestamps == TIMESTAMPS_OFF) { |     if (user_settings->timestamps == TIMESTAMPS_OFF) { | ||||||
|         buf[0] = '\0'; |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const char *t = user_settings->timestamp_format; |     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 */ | /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||||
| void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs) | void get_elapsed_time_str(char *buf, int bufsize, time_t secs) | ||||||
| { | { | ||||||
|     if (!secs) |     if (!secs) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     long int seconds = secs % 60; |     long int seconds = secs % 60; | ||||||
|     long int minutes = (secs % 3600) / 60; |     long int minutes = (secs % 3600) / 60; | ||||||
|     long int hours = secs / 3600; |     long int hours = secs / 3600; | ||||||
|  |  | ||||||
|     if (!minutes && !hours) |     if (!minutes && !hours) { | ||||||
|         snprintf(buf, bufsize, "%.2ld", seconds); |         snprintf(buf, bufsize, "%.2ld", seconds); | ||||||
|     else if (!hours) |     } else if (!hours) { | ||||||
|         snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds); |         snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds); | ||||||
|     else |     } else { | ||||||
|         snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); |         snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. |  * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. | ||||||
| @@ -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) | 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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < output_size; ++i) { |     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; |         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) | int hex_string_to_bytes(char *buf, int size, const char *keystr) | ||||||
| { | { | ||||||
|     if (size % 2 != 0) |     if (size % 2 != 0) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int i, res; |  | ||||||
|     const char *pos = keystr; |     const char *pos = keystr; | ||||||
|  |  | ||||||
|     for (i = 0; i < size; ++i) { |     for (size_t i = 0; i < size; ++i) { | ||||||
|         res = sscanf(pos, "%2hhx", &buf[i]); |         int res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]); | ||||||
|         pos += 2; |         pos += 2; | ||||||
|  |  | ||||||
|         if (res == EOF || res < 1) |         if (res == EOF || res < 1) { | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     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) | 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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     size_t i; |     for (size_t i = 0; i < TOX_ADDRESS_SIZE; ++i) { | ||||||
|  |  | ||||||
|     for (i = 0; i < TOX_ADDRESS_SIZE; ++i) |  | ||||||
|         snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); |         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; |     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 */ | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
| int string_is_empty(const char *string) | int string_is_empty(const char *string) | ||||||
| { | { | ||||||
|     if (!string) |     if (!string) { | ||||||
|         return true; |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return string[0] == '\0'; |     return string[0] == '\0'; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
|  | int wstring_is_empty(const wchar_t *string) | ||||||
|  | { | ||||||
|  |     if (!string) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return string[0] == L'\0'; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* convert a multibyte string to a wide character string and puts in buf. */ | /* convert a multibyte string to a wide character string and puts in buf. */ | ||||||
| int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | ||||||
| { | { | ||||||
|     size_t len = mbstowcs(NULL, string, 0) + 1; |     size_t len = mbstowcs(NULL, string, 0) + 1; | ||||||
|  |  | ||||||
|     if (n < len) |     if (n < len) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ((len = mbstowcs(buf, string, n)) == (size_t) -1) |     if ((len = mbstowcs(buf, string, n)) == (size_t) - 1) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return len; |     return len; | ||||||
| } | } | ||||||
| @@ -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; |     size_t len = wcstombs(NULL, string, 0) + 1; | ||||||
|  |  | ||||||
|     if (n < len) |     if (n < len) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ((len = wcstombs(buf, string, n)) == (size_t) -1) |     if ((len = wcstombs(buf, string, n)) == (size_t) - 1) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return len; |     return len; | ||||||
| } | } | ||||||
| @@ -210,43 +271,69 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2) | |||||||
|     return strcasecmp((const char *) str1, (const char *) str2); |     return strcasecmp((const char *) str1, (const char *) str2); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | /* case-insensitive string compare function for use with qsort */ | ||||||
|       - cannot be empty | int qsort_ptr_char_array_helper(const void *str1, const void *str2) | ||||||
|       - 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) |  | ||||||
| { | { | ||||||
|     if (!nick[0] || nick[0] == ' ') |     return strcasecmp(*(char **)str1, *(char **)str2); | ||||||
|         return 0; |  | ||||||
|  |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     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') |  | ||||||
|  |  | ||||||
|             return 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|     return 1; | static const char invalid_chars[] = {'/', '\n', '\t', '\v', '\r', '\0'}; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * 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; | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; (tmp = invalid_chars[i]); ++i) { | ||||||
|  |         if (tmp == ch) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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) */ | /* 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) | 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 (!is_valid_char(ch) || str[i] == '\0') { | ||||||
|         if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') |  | ||||||
|             str[i] = ' '; |             str[i] = ' '; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* gets base file name from path or original file name if no path is supplied. | /* gets base file name from path or original file name if no path is supplied. | ||||||
|  * Returns the file name length |  * Returns the file name length | ||||||
| @@ -256,23 +343,27 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname) | |||||||
|     int len = strlen(pathname) - 1; |     int len = strlen(pathname) - 1; | ||||||
|     char *path = strdup(pathname); |     char *path = strdup(pathname); | ||||||
|  |  | ||||||
|     if (path == NULL) |     if (path == NULL) { | ||||||
|         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); |         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     while (len >= 0 && pathname[len] == '/') |     while (len >= 0 && pathname[len] == '/') { | ||||||
|         path[len--] = '\0'; |         path[len--] = '\0'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char *finalname = strdup(path); |     char *finalname = strdup(path); | ||||||
|  |  | ||||||
|     if (finalname == NULL) |     if (finalname == NULL) { | ||||||
|         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); |         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const char *basenm = strrchr(path, '/'); |     const char *basenm = strrchr(path, '/'); | ||||||
|  |  | ||||||
|     if (basenm != NULL) { |     if (basenm != NULL) { | ||||||
|         if (basenm[1]) |         if (basenm[1]) { | ||||||
|             strcpy(finalname, &basenm[1]); |             strcpy(finalname, &basenm[1]); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     snprintf(namebuf, bufsize, "%s", finalname); |     snprintf(namebuf, bufsize, "%s", finalname); | ||||||
|     free(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) | 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; |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     size_t dir_len = char_rfind(path, '/', path_len); |     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 */ |         ++dir_len;    /* Leave trailing slash */ | ||||||
|  |     } | ||||||
|  |  | ||||||
|     memcpy(dir, path, dir_len); |     memcpy(dir, path, dir_len); | ||||||
|     dir[dir_len] = '\0'; |     dir[dir_len] = '\0'; | ||||||
| @@ -307,80 +400,117 @@ void str_to_lower(char *str) | |||||||
| { | { | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; str[i]; ++i) |     for (i = 0; str[i]; ++i) { | ||||||
|         str[i] = tolower(str[i]); |         str[i] = tolower(str[i]); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary. | /* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary. | ||||||
|    if toxcore API call fails, put UNKNOWN_NAME in buf |    if toxcore API call fails, put UNKNOWN_NAME in buf | ||||||
|    Returns nick len */ |    Returns nick len */ | ||||||
| size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum) | 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) { |     if (err != TOX_ERR_FRIEND_QUERY_OK) { | ||||||
|         strcpy(buf, UNKNOWN_NAME); |         goto on_error; | ||||||
|         len = strlen(UNKNOWN_NAME); |  | ||||||
|     } else { |     } 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); |     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||||
|     buf[len] = '\0'; |     buf[len] = '\0'; | ||||||
|     filter_str(buf, len); |     filter_str(buf, len); | ||||||
|     return len; |     return len; | ||||||
| } |  | ||||||
|  |  | ||||||
| /* same as get_nick_truncate but for groupchats */ | on_error: | ||||||
| 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) { |  | ||||||
|     strcpy(buf, UNKNOWN_NAME); |     strcpy(buf, UNKNOWN_NAME); | ||||||
|     len = strlen(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); |     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||||
|     buf[len] = '\0'; |     buf[len] = '\0'; | ||||||
|     filter_str(buf, len); |     filter_str(buf, len); | ||||||
|     return 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 */ |    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 copy_tox_str(char *msg, size_t size, const char *data, size_t length) | ||||||
| { | { | ||||||
|     size_t len = MIN(length, size - 1); |     size_t i; | ||||||
|     memcpy(msg, data, len); |     size_t j = 0; | ||||||
|     msg[len] = '\0'; |  | ||||||
|     return len; |     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 index of the first instance of ch in s starting at idx. | ||||||
|    returns length of s if char not found */ |    returns length of s if char not found or 0 if s is NULL. */ | ||||||
| int char_find(int idx, const char *s, char ch) | int char_find(int idx, const char *s, char ch) | ||||||
| { | { | ||||||
|  |     if (!s) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int i = idx; |     int i = idx; | ||||||
|  |  | ||||||
|     for (i = idx; s[i]; ++i) { |     for (i = idx; s[i]; ++i) { | ||||||
|         if (s[i] == ch) |         if (s[i] == ch) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return i; |     return i; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* returns index of the last instance of ch in s starting at len. | /* returns index of the last instance of ch in s starting at len. | ||||||
|    returns 0 if char not found (skips 0th index). */ |    returns 0 if char not found or s is NULL (skips 0th index). */ | ||||||
| int char_rfind(const char *s, char ch, int len) | int char_rfind(const char *s, char ch, int len) | ||||||
| { | { | ||||||
|  |     if (!s) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |  | ||||||
|     for (i = len; i > 0; --i) { |     for (i = len; i > 0; --i) { | ||||||
|         if (s[i] == ch) |         if (s[i] == ch) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return i; |     return i; | ||||||
| } | } | ||||||
| @@ -414,45 +544,59 @@ bool file_exists(const char *path) | |||||||
|     return stat(path, &s) == 0; |     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. */ | /* returns file size. If file doesn't exist returns 0. */ | ||||||
| off_t file_size(const char *path) | off_t file_size(const char *path) | ||||||
| { | { | ||||||
|     struct stat st; |     struct stat st; | ||||||
|  |  | ||||||
|     if (stat(path, &st) == -1) |     if (stat(path, &st) == -1) { | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
|     return st.st_size; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| /* compares the first size bytes of fp to signature. |     return st.st_size; | ||||||
|    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. */ | /* sets window title in tab bar. */ | ||||||
| void set_window_title(ToxWindow *self, const char *title, int len) | void set_window_title(ToxWindow *self, const char *title, int len) | ||||||
| { | { | ||||||
|  |     if (len <= 0 || !title) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char cpy[TOXIC_MAX_NAME_LENGTH + 1]; |     char cpy[TOXIC_MAX_NAME_LENGTH + 1]; | ||||||
|  |  | ||||||
|     if (self->is_groupchat)   /* keep groupnumber in title */ |     if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title for invites */ | ||||||
|         snprintf(cpy, sizeof(cpy), "%d %s", self->num, title); |         snprintf(cpy, sizeof(cpy), "%u %s", self->num, title); | ||||||
|     else |     } else { | ||||||
|         snprintf(cpy, sizeof(cpy), "%s", title); |         snprintf(cpy, sizeof(cpy), "%s", title); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (len > MAX_WINDOW_NAME_LENGTH) { |     if (len > MAX_WINDOW_NAME_LENGTH) { | ||||||
|         strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "..."); |         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); |     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 <sys/stat.h> | ||||||
|  |  | ||||||
| #include "windows.h" |  | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
|  | #include "windows.h" | ||||||
|  |  | ||||||
| #ifndef MIN | #ifndef MIN | ||||||
| #define MIN(x, y) (((x) < (y)) ? (x) : (y)) | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) | ||||||
| @@ -39,6 +39,17 @@ | |||||||
| #define net_to_host(x, y) hst_to_net(x, y) | #define net_to_host(x, y) hst_to_net(x, y) | ||||||
| #endif | #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); | 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); | 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) */ | /* Converts a binary representation of a Tox public key into a string. | ||||||
| uint64_t get_unix_time(void); |  * | ||||||
|  |  * 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) */ | /* get the current unix time (not thread safe) */ | ||||||
| void get_time_str(char *buf, int bufsize); | 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 */ | /* 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) */ | /* get the current local time (not thread safe) */ | ||||||
| struct tm *get_time(void); | struct tm *get_time(void); | ||||||
|  |  | ||||||
| /* updates current unix time (should be run once per do_toxic loop) */ |  | ||||||
| void update_unix_time(void); |  | ||||||
|  |  | ||||||
| /* Returns 1 if the string is empty, 0 otherwise */ | /* Returns 1 if the string is empty, 0 otherwise */ | ||||||
| int string_is_empty(const char *string); | int string_is_empty(const char *string); | ||||||
|  |  | ||||||
| /* 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); | 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. */ | /* 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); | 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); | int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||||
|  |  | ||||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | /* Returns 1 if connection has timed out, 0 otherwise */ | ||||||
| int timed_out(uint64_t timestamp, uint64_t timeout); | int timed_out(time_t timestamp, time_t timeout); | ||||||
|  |  | ||||||
|  | /* 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 */ | /* Colours the window tab according to type. Beeps if is_beep is true */ | ||||||
| void alert_window(ToxWindow *self, int type, bool is_beep); | void alert_window(ToxWindow *self, int type, bool is_beep); | ||||||
| @@ -96,13 +117,19 @@ void alert_window(ToxWindow *self, int type, bool is_beep); | |||||||
| /* case-insensitive string compare function for use with qsort */ | /* case-insensitive string compare function for use with qsort */ | ||||||
| int qsort_strcasecmp_hlpr(const void *str1, const void *str2); | int qsort_strcasecmp_hlpr(const void *str1, const void *str2); | ||||||
|  |  | ||||||
| /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | /* case-insensitive string compare function for use with qsort */ | ||||||
|       - cannot be empty | int qsort_ptr_char_array_helper(const void *str1, const void *str2); | ||||||
|       - cannot start with a space |  | ||||||
|       - must not contain a forward slash (for logfile naming purposes) | /* Returns true if nick is valid. | ||||||
|       - must not contain contiguous spaces |  * | ||||||
|       - must not contain a newline or tab seqeunce */ |  * A valid toxic nick: | ||||||
| int valid_nick(const char *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) */ | /* 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); | 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 */ |    Returns nick len on success, -1 on failure */ | ||||||
| size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | ||||||
|  |  | ||||||
| /* same as get_nick_truncate but for groupchats */ | /* same as get_nick_truncate but for conferences */ | ||||||
| int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum); | int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum); | ||||||
|  |  | ||||||
| /* copies data to msg buffer. | /* copies data to msg buffer. | ||||||
|    returns length of msg, which will be no larger than size-1 */ |    returns length of msg, 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 copy_tox_str(char *msg, size_t size, const char *data, size_t length); | ||||||
|  |  | ||||||
| /* returns index of the first instance of ch in s starting at idx. | /* returns index of the first instance of ch in s starting at idx. | ||||||
|    returns length of s if char not found */ |    returns length of s if char not found or 0 if s is NULL. */ | ||||||
| int char_find(int idx, const char *s, char ch); | int char_find(int idx, const char *s, char ch); | ||||||
|  |  | ||||||
| /* returns index of the last instance of ch in s | /* returns index of the last instance of ch in s starting at len. | ||||||
|    returns 0 if char not found */ |    returns 0 if char not found or s is NULL (skips 0th index). */ | ||||||
| int char_rfind(const char *s, char ch, int len); | int char_rfind(const char *s, char ch, int len); | ||||||
|  |  | ||||||
| /* Converts bytes to appropriate unit and puts in buf as a string */ | /* Converts bytes to appropriate unit and puts in buf as a string */ | ||||||
| @@ -146,16 +173,32 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes); | |||||||
| /* checks if a file exists. Returns true or false */ | /* checks if a file exists. Returns true or false */ | ||||||
| bool file_exists(const char *path); | 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. */ | /* returns file size. If file doesn't exist returns 0. */ | ||||||
| off_t file_size(const char *path); | 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. */ | /* sets window title in tab bar. */ | ||||||
| void set_window_title(ToxWindow *self, const char *title, int len); | void set_window_title(ToxWindow *self, const char *title, int len); | ||||||
|  |  | ||||||
| #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 <stdlib.h> | ||||||
| #include <string.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 "toxic.h" | ||||||
| #include "windows.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 arg_opts arg_opts; | ||||||
| extern struct Winthread Winthread;; | extern struct Winthread Winthread; | ||||||
|  |  | ||||||
| #define NAMESERVER_API_PATH "api" | #define NAMESERVER_API_PATH "api" | ||||||
| #define SERVER_KEY_SIZE 32 | #define SERVER_KEY_SIZE 32 | ||||||
| @@ -41,10 +42,7 @@ extern struct Winthread Winthread;; | |||||||
| #define MAX_DOMAIN_SIZE 32 | #define MAX_DOMAIN_SIZE 32 | ||||||
| #define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3 | #define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3 | ||||||
|  |  | ||||||
| /* List based on Mozilla's recommended configurations for modern browsers */ | static struct Nameservers { | ||||||
| #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 { |  | ||||||
|     int     lines; |     int     lines; | ||||||
|     char    names[MAX_SERVERS][MAX_DOMAIN_SIZE]; |     char    names[MAX_SERVERS][MAX_DOMAIN_SIZE]; | ||||||
|     char    keys[MAX_SERVERS][SERVER_KEY_SIZE]; |     char    keys[MAX_SERVERS][SERVER_KEY_SIZE]; | ||||||
| @@ -56,8 +54,8 @@ static struct thread_data { | |||||||
|     char    id_bin[TOX_ADDRESS_SIZE]; |     char    id_bin[TOX_ADDRESS_SIZE]; | ||||||
|     char    addr[MAX_STR_SIZE]; |     char    addr[MAX_STR_SIZE]; | ||||||
|     char    msg[MAX_STR_SIZE]; |     char    msg[MAX_STR_SIZE]; | ||||||
|     bool    busy; |  | ||||||
|     bool    disabled; |     bool    disabled; | ||||||
|  |     volatile bool busy; | ||||||
| } t_data; | } t_data; | ||||||
|  |  | ||||||
| static struct lookup_thread { | static struct lookup_thread { | ||||||
| @@ -65,6 +63,13 @@ static struct lookup_thread { | |||||||
|     pthread_attr_t attr; |     pthread_attr_t attr; | ||||||
| } lookup_thread; | } lookup_thread; | ||||||
|  |  | ||||||
|  | static void clear_thread_data(void) | ||||||
|  | { | ||||||
|  |     t_data = (struct thread_data) { | ||||||
|  |         0 | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int lookup_error(ToxWindow *self, const char *errmsg, ...) | static int lookup_error(ToxWindow *self, const char *errmsg, ...) | ||||||
| { | { | ||||||
|     char frmt_msg[MAX_STR_SIZE]; |     char frmt_msg[MAX_STR_SIZE]; | ||||||
| @@ -75,7 +80,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...) | |||||||
|     va_end(args); |     va_end(args); | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     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); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     return -1; |     return -1; | ||||||
| @@ -83,7 +88,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...) | |||||||
|  |  | ||||||
| static void kill_lookup_thread(void) | static void kill_lookup_thread(void) | ||||||
| { | { | ||||||
|     memset(&t_data, 0, sizeof(struct thread_data)); |     clear_thread_data(); | ||||||
|     pthread_attr_destroy(&lookup_thread.attr); |     pthread_attr_destroy(&lookup_thread.attr); | ||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| } | } | ||||||
| @@ -99,42 +104,49 @@ static int load_nameserver_list(const char *path) | |||||||
| { | { | ||||||
|     FILE *fp = fopen(path, "r"); |     FILE *fp = fopen(path, "r"); | ||||||
|  |  | ||||||
|     if (fp == NULL) |     if (fp == NULL) { | ||||||
|         return -2; |         return -2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char line[MAX_SERVER_LINE]; |     char line[MAX_SERVER_LINE]; | ||||||
|  |  | ||||||
|     while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) { |     while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) { | ||||||
|         int linelen = strlen(line); |         int linelen = strlen(line); | ||||||
|  |  | ||||||
|         if (linelen < SERVER_KEY_SIZE * 2 + 5) |         if (linelen < SERVER_KEY_SIZE * 2 + 5) { | ||||||
|             continue; |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (line[linelen - 1] == '\n') |         if (line[linelen - 1] == '\n') { | ||||||
|             line[--linelen] = '\0'; |             line[--linelen] = '\0'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         const char *name = strtok(line, " "); |         const char *name = strtok(line, " "); | ||||||
|         const char *keystr = strtok(NULL, " "); |         const char *keystr = strtok(NULL, " "); | ||||||
|  |  | ||||||
|         if (name == NULL || keystr == NULL) |         if (name == NULL || keystr == NULL) { | ||||||
|             continue; |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (strlen(keystr) != SERVER_KEY_SIZE * 2) |         if (strlen(keystr) != SERVER_KEY_SIZE * 2) { | ||||||
|             continue; |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name); |         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); |         int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr); | ||||||
|  |  | ||||||
|         if (res == -1) |         if (res == -1) { | ||||||
|             continue; |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         ++Nameservers.lines; |         ++Nameservers.lines; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fclose(fp); |     fclose(fp); | ||||||
|  |  | ||||||
|     if (Nameservers.lines < 1) |     if (Nameservers.lines < 1) { | ||||||
|         return -3; |         return -3; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     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) | 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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char tmpaddr[MAX_STR_SIZE]; |     char tmpaddr[MAX_STR_SIZE]; | ||||||
|     char *tmpname = NULL; |     char *tmpname = NULL; | ||||||
| @@ -158,8 +171,9 @@ static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char * | |||||||
|     tmpname = strtok(tmpaddr, "@"); |     tmpname = strtok(tmpaddr, "@"); | ||||||
|     tmpdom = strtok(NULL, ""); |     tmpdom = strtok(NULL, ""); | ||||||
|  |  | ||||||
|     if (tmpname == NULL || tmpdom == NULL) |     if (tmpname == NULL || tmpdom == NULL) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     str_to_lower(tmpdom); |     str_to_lower(tmpdom); | ||||||
|     snprintf(namebuf, namebuf_sz, "%s", tmpname); |     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; |     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. | /* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer. | ||||||
|  * |  * | ||||||
|  * Returns 0 on success. |  * Returns 0 on success. | ||||||
|  * Returns -1 on failure. |  * Returns -1 on failure. | ||||||
|  */ |  */ | ||||||
| #define ID_PREFIX "\"tox_id\": \"" | #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); |     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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const char *IDstart = strstr(recv_data->data, ID_PREFIX); |     const char *IDstart = strstr(recv_data->data, ID_PREFIX); | ||||||
|  |  | ||||||
|     if (IDstart == NULL) |     if (IDstart == NULL) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) |     if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char ID_string[TOX_ADDRESS_SIZE * 2 + 1]; |     char ID_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||||
|     memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2); |     memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2); | ||||||
|     ID_string[TOX_ADDRESS_SIZE * 2] = 0; |     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) |     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); |  | ||||||
|         return -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) | void *lookup_thread_func(void *data) | ||||||
| { | { | ||||||
|  |     UNUSED_VAR(data); | ||||||
|  |  | ||||||
|     ToxWindow *self = t_data.self; |     ToxWindow *self = t_data.self; | ||||||
|  |  | ||||||
|     char input_domain[MAX_STR_SIZE]; |     char input_domain[MAX_STR_SIZE]; | ||||||
| @@ -300,10 +256,11 @@ void *lookup_thread_func(void *data) | |||||||
|     char real_domain[MAX_DOMAIN_SIZE]; |     char real_domain[MAX_DOMAIN_SIZE]; | ||||||
|  |  | ||||||
|     if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) { |     if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) { | ||||||
|         if (!strcasecmp(input_domain, "utox.org")) |         if (!strcasecmp(input_domain, "utox.org")) { | ||||||
|             lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic"); |             lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic."); | ||||||
|         else |         } else { | ||||||
|             lookup_error(self, "Name server domain not found."); |             lookup_error(self, "Name server domain not found."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         kill_lookup_thread(); |         kill_lookup_thread(); | ||||||
|     } |     } | ||||||
| @@ -315,26 +272,41 @@ void *lookup_thread_func(void *data) | |||||||
|         kill_lookup_thread(); |         kill_lookup_thread(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct Recv_Data recv_data; |     struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data)); | ||||||
|     memset(&recv_data, 0, sizeof(struct Recv_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); |     snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name); | ||||||
|  |  | ||||||
|     struct curl_slist *headers = NULL; |     struct curl_slist *headers = NULL; | ||||||
|  |  | ||||||
|     headers = curl_slist_append(headers, "Content-Type: application/json"); |     headers = curl_slist_append(headers, "Content-Type: application/json"); | ||||||
|  |  | ||||||
|     headers = curl_slist_append(headers, "charsets: utf-8"); |     headers = curl_slist_append(headers, "charsets: utf-8"); | ||||||
|  |  | ||||||
|     curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers); |     curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers); | ||||||
|  |  | ||||||
|     curl_easy_setopt(c_handle, CURLOPT_URL, real_domain); |     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_USERAGENT, "libcurl-agent/1.0"); | ||||||
|  |  | ||||||
|     curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data); |     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; |         goto on_exit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL); |     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."); |         lookup_error(self, "Bad response."); | ||||||
|         goto on_exit; |         goto on_exit; | ||||||
|     } |     } | ||||||
| @@ -382,6 +354,7 @@ void *lookup_thread_func(void *data) | |||||||
|     pthread_mutex_unlock(&Winthread.lock); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
| on_exit: | on_exit: | ||||||
|  |     free(recv_data); | ||||||
|     curl_slist_free_all(headers); |     curl_slist_free_all(headers); | ||||||
|     curl_easy_cleanup(c_handle); |     curl_easy_cleanup(c_handle); | ||||||
|     kill_lookup_thread(); |     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) | void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message) | ||||||
| { | { | ||||||
|     if (t_data.disabled) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (t_data.busy) { |     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; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -409,22 +382,22 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, | |||||||
|     t_data.busy = true; |     t_data.busy = true; | ||||||
|  |  | ||||||
|     if (pthread_attr_init(&lookup_thread.attr) != 0) { |     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"); |         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init"); | ||||||
|         memset(&t_data, 0, sizeof(struct thread_data)); |         clear_thread_data(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) { |     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); |         pthread_attr_destroy(&lookup_thread.attr); | ||||||
|         memset(&t_data, 0, sizeof(struct thread_data)); |         clear_thread_data(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) { |     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); |         pthread_attr_destroy(&lookup_thread.attr); | ||||||
|         memset(&t_data, 0, sizeof(struct thread_data)); |         clear_thread_data(); | ||||||
|         return; |         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 -2 if the nameserver list cannot be found. | ||||||
|  * Returns -3 if the nameserver list does not contain any valid entries. |  * 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; |         t_data.disabled = true; | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -454,8 +427,3 @@ int name_lookup_init(void) | |||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void name_lookup_cleanup(void) |  | ||||||
| { |  | ||||||
|     curl_global_cleanup(); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -29,8 +29,7 @@ | |||||||
|  * Returns 0 on success. |  * Returns 0 on success. | ||||||
|  * Returns -1 on failure. |  * Returns -1 on failure. | ||||||
|  */ |  */ | ||||||
| int name_lookup_init(void); | int name_lookup_init(int curl_init_status); | ||||||
| void name_lookup_cleanup(void); |  | ||||||
|  |  | ||||||
| int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message); | int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										378
									
								
								src/notify.c
									
									
									
									
									
								
							
							
						
						
									
										378
									
								
								src/notify.c
									
									
									
									
									
								
							| @@ -20,22 +20,22 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include <assert.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.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 <sys/stat.h> | ||||||
|  | #include <time.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "notify.h" |  | ||||||
| #include "audio_device.h" | #include "audio_device.h" | ||||||
| #include "settings.h" |  | ||||||
| #include "line_info.h" | #include "line_info.h" | ||||||
| #include "misc_tools.h" | #include "misc_tools.h" | ||||||
| #include "xtra.h" | #include "notify.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "x11focus.h" | ||||||
|  |  | ||||||
| #if defined(AUDIO) || defined(SOUND_NOTIFY) | #if defined(AUDIO) || defined(SOUND_NOTIFY) | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
| @@ -47,12 +47,12 @@ | |||||||
| /* compatibility with older versions of OpenAL */ | /* compatibility with older versions of OpenAL */ | ||||||
| #ifndef ALC_ALL_DEVICES_SPECIFIER | #ifndef ALC_ALL_DEVICES_SPECIFIER | ||||||
| #include <AL/alext.h> | #include <AL/alext.h> | ||||||
|         #endif | #endif /* ALC_ALL_DEVICES_SPECIFIER */ | ||||||
|     #endif | #endif /* __APPLE__ */ | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
| #include <AL/alut.h> /* freealut packet */ | #include <AL/alut.h> /* freealut packet */ | ||||||
|     #endif | #endif /* SOUND_NOTIFY */ | ||||||
| #endif /* AUDIO */ | #endif /* defined(AUDIO) || defined(SOUND_NOTIFY) */ | ||||||
|  |  | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
| #include <libnotify/notify.h> | #include <libnotify/notify.h> | ||||||
| @@ -60,11 +60,11 @@ | |||||||
|  |  | ||||||
| #define MAX_BOX_MSG_LEN 127 | #define MAX_BOX_MSG_LEN 127 | ||||||
| #define SOUNDS_SIZE 10 | #define SOUNDS_SIZE 10 | ||||||
| #define ACTIVE_NOTIFS_MAX 50 | #define ACTIVE_NOTIFS_MAX 10 | ||||||
|  |  | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
|  |  | ||||||
| struct Control { | static struct Control { | ||||||
|     time_t cooldown; |     time_t cooldown; | ||||||
|     time_t notif_timeout; |     time_t notif_timeout; | ||||||
|  |  | ||||||
| @@ -79,12 +79,12 @@ struct Control { | |||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } Control = {0}; | } Control = {0}; | ||||||
|  |  | ||||||
| struct _ActiveNotifications { | static struct _ActiveNotifications { | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     uint32_t source; |     uint32_t source; | ||||||
|     uint32_t buffer; |     uint32_t buffer; | ||||||
|     bool looping; |     bool looping; | ||||||
| #endif | #endif /* SOUND_NOTIFY */ | ||||||
|     bool active; |     bool active; | ||||||
|     int *id_indicator; |     int *id_indicator; | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
| @@ -93,7 +93,7 @@ struct _ActiveNotifications { | |||||||
|     char title[64]; |     char title[64]; | ||||||
|     size_t size; |     size_t size; | ||||||
|     time_t n_timeout; |     time_t n_timeout; | ||||||
| #endif | #endif /* BOX_NOTIFY */ | ||||||
| } actives[ACTIVE_NOTIFS_MAX]; | } 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 */ | /* coloured tab notifications: primary notification type */ | ||||||
| static void tab_notify(ToxWindow *self, uint64_t flags) | static void tab_notify(ToxWindow *self, uint64_t flags) | ||||||
| { | { | ||||||
|     if (self == NULL) |     if (self == NULL) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (flags & NT_WNDALERT_0) |     if (flags & NT_WNDALERT_0) { | ||||||
|         self->alert = WINDOW_ALERT_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; |         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->alert = WINDOW_ALERT_2; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ++self->pending_messages; | ||||||
|  | } | ||||||
|  |  | ||||||
| static bool notifications_are_disabled(uint64_t flags) | static bool notifications_are_disabled(uint64_t flags) | ||||||
| { | { | ||||||
|     if (user_settings->alerts != ALERTS_ENABLED) |     if (user_settings->alerts != ALERTS_ENABLED) { | ||||||
|         return true; |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time()); |     bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time()); | ||||||
| #ifdef X11 | #ifdef X11 | ||||||
| @@ -128,14 +144,14 @@ static bool notifications_are_disabled(uint64_t flags) | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| static void control_lock() | static void control_lock(void) | ||||||
| { | { | ||||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||||
|     pthread_mutex_lock(Control.poll_mutex); |     pthread_mutex_lock(Control.poll_mutex); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| static void control_unlock() | static void control_unlock(void) | ||||||
| { | { | ||||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||||
|     pthread_mutex_unlock(Control.poll_mutex); |     pthread_mutex_unlock(Control.poll_mutex); | ||||||
| @@ -157,21 +173,25 @@ static bool device_opened = false; | |||||||
| time_t last_opened_update = 0; | time_t last_opened_update = 0; | ||||||
|  |  | ||||||
| /* Opens primary device. Returns true on succe*/ | /* Opens primary device. Returns true on succe*/ | ||||||
| void m_open_device() | void m_open_device(void) | ||||||
| { | { | ||||||
|     last_opened_update = get_unix_time(); |     last_opened_update = get_unix_time(); | ||||||
|  |  | ||||||
|     if (device_opened) return; |     if (device_opened) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* Blah error check */ |     /* 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; |     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); |     close_device(output, Control.device_idx); | ||||||
|  |  | ||||||
| @@ -179,32 +199,37 @@ void m_close_device() | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Terminate all sounds but wait for them to finish first */ | /* Terminate all sounds but wait for them to finish first */ | ||||||
| void graceful_clear() | void graceful_clear(void) | ||||||
| { | { | ||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
|     while (1) { |     while (1) { | ||||||
|         int i; |         int i; | ||||||
|  |  | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |         for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|             if (actives[i].active) { |             if (actives[i].active) { | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|  |  | ||||||
|                 if (actives[i].box) { |                 if (actives[i].box) { | ||||||
|                     GError *ignore; |                     GError *ignore; | ||||||
|                     notify_notification_close(actives[i].box, &ignore); |                     notify_notification_close(actives[i].box, &ignore); | ||||||
|                     actives[i].box = NULL; |                     actives[i].box = NULL; | ||||||
|                 } |                 } | ||||||
|             #endif |  | ||||||
|  |  | ||||||
|                 if(actives[i].id_indicator) | #endif /* BOX_NOTIFY */ | ||||||
|  |  | ||||||
|  |                 if (actives[i].id_indicator) { | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 if (actives[i].looping) { |                 if (actives[i].looping) { | ||||||
|                     stop_sound(i); |                     stop_sound(i); | ||||||
|                 } else { |                 } else { | ||||||
|                     if (!is_playing(actives[i].source)) |                     if (!is_playing(actives[i].source)) { | ||||||
|                         memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |                         clear_actives_index(i); | ||||||
|                     else break; |                     } else { | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -215,7 +240,7 @@ void graceful_clear() | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         usleep(1000); |         sleep_thread(1000L); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     control_unlock(); |     control_unlock(); | ||||||
| @@ -223,7 +248,7 @@ void graceful_clear() | |||||||
|  |  | ||||||
| void *do_playing(void *_p) | void *do_playing(void *_p) | ||||||
| { | { | ||||||
|     (void)_p; |     UNUSED_VAR(_p); | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         control_lock(); |         control_lock(); | ||||||
| @@ -234,46 +259,54 @@ void* do_playing(void* _p) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         bool has_looping = false; |         bool has_looping = false; | ||||||
|  |         bool test_active_notify = false; | ||||||
|         int i; |         int i; | ||||||
|  |  | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |         for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|  |  | ||||||
|             if (actives[i].looping) has_looping = true; |             if (actives[i].looping) { | ||||||
|  |                 has_looping = true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             if (actives[i].active && !actives[i].looping |             test_active_notify = actives[i].active && !actives[i].looping; | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|                     && !actives[i].box |             test_active_notify = test_active_notify && !actives[i].box; | ||||||
| #endif | #endif | ||||||
|             ) { |  | ||||||
|                 if(actives[i].id_indicator) |             if (test_active_notify) { | ||||||
|  |                 if (actives[i].id_indicator) { | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 if (!is_playing(actives[i].source)) { |                 if (!is_playing(actives[i].source)) { | ||||||
|                     /* Close */ |                     /* Close */ | ||||||
|                     alSourceStop(actives[i].source); |                     alSourceStop(actives[i].source); | ||||||
|                     alDeleteSources(1, &actives[i].source); |                     alDeleteSources(1, &actives[i].source); | ||||||
|                     alDeleteBuffers(1, &actives[i].buffer); |                     alDeleteBuffers(1, &actives[i].buffer); | ||||||
|                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |                     clear_actives_index(i); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|             else if (actives[i].box && time(NULL) >= actives[i].n_timeout) |             else if (actives[i].box && time(NULL) >= actives[i].n_timeout) { | ||||||
|             { |  | ||||||
|                 GError *ignore; |                 GError *ignore; | ||||||
|                 notify_notification_close(actives[i].box, &ignore); |                 notify_notification_close(actives[i].box, &ignore); | ||||||
|                 actives[i].box = NULL; |                 actives[i].box = NULL; | ||||||
|                 if(actives[i].id_indicator) |  | ||||||
|  |                 if (actives[i].id_indicator) { | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 if (!actives[i].looping && !is_playing(actives[i].source)) { |                 if (!actives[i].looping && !is_playing(actives[i].source)) { | ||||||
|                     /* stop source if not looping or playing, just terminate box */ |                     /* stop source if not looping or playing, just terminate box */ | ||||||
|                     alSourceStop(actives[i].source); |                     alSourceStop(actives[i].source); | ||||||
|                     alDeleteSources(1, &actives[i].source); |                     alDeleteSources(1, &actives[i].source); | ||||||
|                     alDeleteBuffers(1, &actives[i].buffer); |                     alDeleteBuffers(1, &actives[i].buffer); | ||||||
|                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |                     clear_actives_index(i); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         #endif |  | ||||||
|  | #endif /* BOX_NOTIFY */ | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/ |         /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/ | ||||||
| @@ -281,18 +314,22 @@ void* do_playing(void* _p) | |||||||
|                 (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) { |                 (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) { | ||||||
|             m_close_device(); |             m_close_device(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         has_looping = false; |         has_looping = false; | ||||||
|  |  | ||||||
|         control_unlock(); |         control_unlock(); | ||||||
|         usleep(10000); |         sleep_thread(10000L); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| int play_source(uint32_t source, uint32_t buffer, bool looping) | int play_source(uint32_t source, uint32_t buffer, bool looping) | ||||||
| { | { | ||||||
|     int i = 0; |     int i = 0; | ||||||
|     for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++); |  | ||||||
|  |     for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; ++i); | ||||||
|  |  | ||||||
|     if (i == ACTIVE_NOTIFS_MAX) { |     if (i == ACTIVE_NOTIFS_MAX) { | ||||||
|         return -1; /* Full */ |         return -1; /* Full */ | ||||||
|     } |     } | ||||||
| @@ -310,7 +347,7 @@ int play_source(uint32_t source, uint32_t buffer, bool looping) | |||||||
| #elif BOX_NOTIFY | #elif BOX_NOTIFY | ||||||
| void *do_playing(void *_p) | void *do_playing(void *_p) | ||||||
| { | { | ||||||
|     (void)_p; |     UNUSED_VAR(_p); | ||||||
|  |  | ||||||
|     while (true) { |     while (true) { | ||||||
|         control_lock(); |         control_lock(); | ||||||
| @@ -320,47 +357,64 @@ void* do_playing(void* _p) | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         int i; |         for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|  |             if (actives[i].box && time(NULL) >= actives[i].n_timeout) { | ||||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |  | ||||||
|             if (actives[i].box && time(NULL) >= actives[i].n_timeout) |  | ||||||
|             { |  | ||||||
|                 GError *ignore; |                 GError *ignore; | ||||||
|                 notify_notification_close(actives[i].box, &ignore); |                 notify_notification_close(actives[i].box, &ignore); | ||||||
|                 actives[i].box = NULL; |                 clear_actives_index(i); | ||||||
|                 if(actives[i].id_indicator) |             } | ||||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ |         } | ||||||
|  |  | ||||||
|                 memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         control_unlock(); |         control_unlock(); | ||||||
|         usleep(10000); |         sleep_thread(10000L); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_exit(NULL); |     pthread_exit(NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| void graceful_clear() | void graceful_clear(void) | ||||||
| { | { | ||||||
|     int i; |  | ||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
|     for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { |     for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||||
|         if (actives[i].box) { |         if (actives[i].box) { | ||||||
|             GError *ignore; |             GError *ignore; | ||||||
|             notify_notification_close(actives[i].box, &ignore); |             notify_notification_close(actives[i].box, &ignore); | ||||||
|             actives[i].box = NULL; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (actives[i].id_indicator) |         clear_actives_index(i); | ||||||
|             *actives[i].id_indicator = -1;    /* reset indicator value */ |     } | ||||||
|  |  | ||||||
|         memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); |     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(); |     control_unlock(); | ||||||
| } | } | ||||||
| #endif |  | ||||||
|  |  | ||||||
| /**********************************************************************************/ | /**********************************************************************************/ | ||||||
| /**********************************************************************************/ | /**********************************************************************************/ | ||||||
| @@ -378,8 +432,10 @@ int init_notify(int login_cooldown, int notification_timeout) | |||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||||
|     if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) |  | ||||||
|  |     if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     Control.poll_active = 1; |     Control.poll_active = 1; | ||||||
|     pthread_t thread; |     pthread_t thread; | ||||||
| @@ -390,7 +446,7 @@ int init_notify(int login_cooldown, int notification_timeout) | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #endif | #endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */ | ||||||
|     Control.cooldown = time(NULL) + login_cooldown; |     Control.cooldown = time(NULL) + login_cooldown; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -401,7 +457,7 @@ int init_notify(int login_cooldown, int notification_timeout) | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| void terminate_notify() | void terminate_notify(void) | ||||||
| { | { | ||||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||||
|     control_lock(); |     control_lock(); | ||||||
| @@ -415,11 +471,15 @@ void terminate_notify() | |||||||
|     control_unlock(); |     control_unlock(); | ||||||
|  |  | ||||||
|     graceful_clear(); |     graceful_clear(); | ||||||
| #endif | #endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */ | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     int i = 0; |     int i = 0; | ||||||
|     for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]); |  | ||||||
|  |     for (; i < SOUNDS_SIZE; ++i) { | ||||||
|  |         free(Control.sounds[i]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     alutExit(); |     alutExit(); | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
| @@ -431,7 +491,9 @@ void terminate_notify() | |||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
| int set_sound(Notification sound, const char *value) | int set_sound(Notification sound, const char *value) | ||||||
| { | { | ||||||
|     if (sound == silent) return 0; |     if (sound == silent) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     free(Control.sounds[sound]); |     free(Control.sounds[sound]); | ||||||
|  |  | ||||||
| @@ -457,6 +519,7 @@ int play_sound_internal(Notification what, bool loop) | |||||||
|     alSourcei(source, AL_LOOPING, loop); |     alSourcei(source, AL_LOOPING, loop); | ||||||
|  |  | ||||||
|     int rc = play_source(source, buffer, loop); |     int rc = play_source(source, buffer, loop); | ||||||
|  |  | ||||||
|     if (rc < 0) { |     if (rc < 0) { | ||||||
|         alSourceStop(source); |         alSourceStop(source); | ||||||
|         alDeleteSources(1, &source); |         alDeleteSources(1, &source); | ||||||
| @@ -471,10 +534,14 @@ int play_notify_sound(Notification notif, uint64_t flags) | |||||||
| { | { | ||||||
|     int rc = -1; |     int rc = -1; | ||||||
|  |  | ||||||
|     if (flags & NT_BEEP) beep(); |     if (flags & NT_BEEP) { | ||||||
|     else if (notif != silent) { |         beep(); | ||||||
|         if ( !Control.poll_active || !Control.sounds[notif] ) |     } | ||||||
|  |  | ||||||
|  |     if (notif != silent) { | ||||||
|  |         if (!Control.poll_active || !Control.sounds[notif]) { | ||||||
|             return -1; |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0); |         rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0); | ||||||
|     } |     } | ||||||
| @@ -482,71 +549,70 @@ int play_notify_sound(Notification notif, uint64_t flags) | |||||||
|     return rc; |     return rc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void stop_sound(int id) | void stop_sound(int id) | ||||||
| { | { | ||||||
|     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active) { |     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active) { | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|  |  | ||||||
|         if (actives[id].box) { |         if (actives[id].box) { | ||||||
|             GError *ignore; |             GError *ignore; | ||||||
|             notify_notification_close(actives[id].box, &ignore); |             notify_notification_close(actives[id].box, &ignore); | ||||||
|         } |         } | ||||||
| #endif |  | ||||||
|         if (actives[id].id_indicator) | #endif /* BOX_NOTIFY */ | ||||||
|             *actives[id].id_indicator = -1; |  | ||||||
|         // alSourcei(actives[id].source, AL_LOOPING, false); |         // alSourcei(actives[id].source, AL_LOOPING, false); | ||||||
|         alSourceStop(actives[id].source); |         alSourceStop(actives[id].source); | ||||||
|         alDeleteSources(1, &actives[id].source); |         alDeleteSources(1, &actives[id].source); | ||||||
|         alDeleteBuffers(1, &actives[id].buffer); |         alDeleteBuffers(1, &actives[id].buffer); | ||||||
|         memset(&actives[id], 0, sizeof(struct _ActiveNotifications)); |         clear_actives_index(id); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| #endif | #endif /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
| static int m_play_sound(Notification notif, uint64_t flags) | static int m_play_sound(Notification notif, uint64_t flags) | ||||||
| { | { | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     return play_notify_sound(notif, flags); |     return play_notify_sound(notif, flags); | ||||||
| #else | #else | ||||||
|     if (notif != silent) |  | ||||||
|  |     if (notif != silent) { | ||||||
|         beep(); |         beep(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return -1; |     return -1; | ||||||
| #endif /* SOUND_NOTIFY */ | #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); |     tab_notify(self, flags); | ||||||
|  |  | ||||||
|     if (notifications_are_disabled(flags)) |     if (notifications_are_disabled(flags)) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int id = -1; |     int id = -1; | ||||||
|     control_lock(); |     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); |         id = m_play_sound(notif, flags); | ||||||
|     else if (flags & NT_ALWAYS) |     } else if (flags & NT_ALWAYS) { | ||||||
|         id = m_play_sound(notif, flags); |         id = m_play_sound(notif, flags); | ||||||
|  |     } | ||||||
|  |  | ||||||
| #if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) | #if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) | ||||||
|  |  | ||||||
|     if (id == -1) { |     if (id == -1) { | ||||||
|         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++); |         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++); | ||||||
|  |  | ||||||
|         if (id == ACTIVE_NOTIFS_MAX) { |         if (id == ACTIVE_NOTIFS_MAX) { | ||||||
|             control_unlock(); |             control_unlock(); | ||||||
|             return -1; /* Full */ |             return -1; /* Full */ | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #endif | #endif /* defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) */ | ||||||
|  |  | ||||||
|     if (id_indicator && id != -1) { |     if (id_indicator && id != -1) { | ||||||
|         actives[id].id_indicator = id_indicator; |         actives[id].id_indicator = id_indicator; | ||||||
| @@ -562,10 +628,14 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id) | |||||||
| { | { | ||||||
|     tab_notify(self, flags); |     tab_notify(self, flags); | ||||||
|  |  | ||||||
|     if (notifications_are_disabled(flags)) |     if (notifications_are_disabled(flags)) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (id < 0 || id >= ACTIVE_NOTIFS_MAX) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1; |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
| @@ -580,7 +650,6 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id) | |||||||
|     alDeleteSources(1, &actives[id].source); |     alDeleteSources(1, &actives[id].source); | ||||||
|     alDeleteBuffers(1, &actives[id].buffer); |     alDeleteBuffers(1, &actives[id].buffer); | ||||||
|  |  | ||||||
|  |  | ||||||
|     alGenSources(1, &actives[id].source); |     alGenSources(1, &actives[id].source); | ||||||
|     alGenBuffers(1, &actives[id].buffer); |     alGenBuffers(1, &actives[id].buffer); | ||||||
|     actives[id].buffer = alutCreateBufferFromFile(Control.sounds[notif]); |     actives[id].buffer = alutCreateBufferFromFile(Control.sounds[notif]); | ||||||
| @@ -593,14 +662,17 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id) | |||||||
|  |  | ||||||
|     return id; |     return id; | ||||||
| #else | #else | ||||||
|     if (notif != silent) |  | ||||||
|  |     if (notif != silent) { | ||||||
|         beep(); |         beep(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
| } | } | ||||||
|  |  | ||||||
| int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...) | int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title, | ||||||
|  |                const char *format, ...) | ||||||
| { | { | ||||||
|     if (notifications_are_disabled(flags)) { |     if (notifications_are_disabled(flags)) { | ||||||
|         tab_notify(self, flags); |         tab_notify(self, flags); | ||||||
| @@ -614,9 +686,11 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi | |||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
| #ifdef SOUND_NOTIFY | #ifdef SOUND_NOTIFY | ||||||
|  |  | ||||||
|     if (id == -1) { /* Could not play */ |     if (id == -1) { /* Could not play */ | ||||||
|  |  | ||||||
|         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); |         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); | ||||||
|  |  | ||||||
|         if (id == ACTIVE_NOTIFS_MAX) { |         if (id == ACTIVE_NOTIFS_MAX) { | ||||||
|             control_unlock(); |             control_unlock(); | ||||||
|             return -1; /* Full */ |             return -1; /* Full */ | ||||||
| @@ -624,22 +698,34 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi | |||||||
|  |  | ||||||
|         actives[id].active = 1; |         actives[id].active = 1; | ||||||
|         actives[id].id_indicator = id_indicator; |         actives[id].id_indicator = id_indicator; | ||||||
|         if (id_indicator) *id_indicator = id; |  | ||||||
|  |         if (id_indicator) { | ||||||
|  |             *id_indicator = id; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| #else | #else | ||||||
|     if (id == -1) |  | ||||||
|  |     if (id == -1) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
| #endif /* SOUND_NOTIFY */ | #endif /* SOUND_NOTIFY */ | ||||||
|  |  | ||||||
|     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); |     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); | ||||||
|     if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); |  | ||||||
|  |  | ||||||
|     va_list __ARGS__; va_start (__ARGS__, format); |     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__); |     vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||||
|     va_end(__ARGS__); |     va_end(__ARGS__); | ||||||
|  |  | ||||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) |     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) { | ||||||
|         strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); |         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].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); | ||||||
|     actives[id].size++; |     actives[id].size++; | ||||||
| @@ -666,8 +752,9 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con | |||||||
|  |  | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|  |  | ||||||
|     if (sound_notify2(self, notif, flags, id) == -1) |     if (sound_notify2(self, notif, flags, id) == -1) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
| @@ -676,50 +763,54 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     va_list __ARGS__; va_start (__ARGS__, format); |     va_list __ARGS__; | ||||||
|  |     va_start(__ARGS__, format); | ||||||
|     vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); |     vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||||
|     va_end(__ARGS__); |     va_end(__ARGS__); | ||||||
|  |  | ||||||
|     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, "..."); |         strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     actives[id].size++; |     actives[id].size++; | ||||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; |     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 (size_t i = 0; i < actives[id].size; ++i) { | ||||||
|     for (; i <actives[id].size; i ++) { |         strcat(formatted, actives[id].messages[i]); | ||||||
|         strcat(formated, actives[id].messages[i]); |         strcat(formatted, "\n"); | ||||||
|         strcat(formated, "\n"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     formated[strlen(formated) - 1] = '\0'; |     notify_notification_update(actives[id].box, actives[id].title, formatted, NULL); | ||||||
|  |  | ||||||
|     notify_notification_update(actives[id].box, actives[id].title, formated, NULL); |  | ||||||
|     notify_notification_show(actives[id].box, NULL); |     notify_notification_show(actives[id].box, NULL); | ||||||
|  |  | ||||||
|  |     free(formatted); | ||||||
|  |  | ||||||
|     control_unlock(); |     control_unlock(); | ||||||
|  |  | ||||||
|     return id; |     return id; | ||||||
| #else | #else | ||||||
|     return sound_notify2(self, notif, flags, id); |     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); |     tab_notify(self, flags); | ||||||
|  |  | ||||||
|     if (notifications_are_disabled(flags)) |     if (notifications_are_disabled(flags)) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|  |  | ||||||
|     control_lock(); |     control_lock(); | ||||||
|  |  | ||||||
|     int id; |     int id; | ||||||
|  |  | ||||||
|     for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); |     for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); | ||||||
|  |  | ||||||
|     if (id == ACTIVE_NOTIFS_MAX) { |     if (id == ACTIVE_NOTIFS_MAX) { | ||||||
|         control_unlock(); |         control_unlock(); | ||||||
|         return -1; /* Full */ |         return -1; /* Full */ | ||||||
| @@ -731,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); |     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); |     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__); |     vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||||
|     va_end(__ARGS__); |     va_end(__ARGS__); | ||||||
|  |  | ||||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) |     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) { | ||||||
|         strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); |         strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     actives[id].active = 1; |     actives[id].active = 1; | ||||||
|     actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); |     actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); | ||||||
| @@ -754,15 +850,16 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const | |||||||
|     return id; |     return id; | ||||||
| #else | #else | ||||||
|     return -1; |     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); |     tab_notify(self, flags); | ||||||
|  |  | ||||||
|     if (notifications_are_disabled(flags)) |     if (notifications_are_disabled(flags)) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
| #ifdef BOX_NOTIFY | #ifdef BOX_NOTIFY | ||||||
|     control_lock(); |     control_lock(); | ||||||
| @@ -773,33 +870,34 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form | |||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     va_list __ARGS__; va_start (__ARGS__, format); |     va_list __ARGS__; | ||||||
|  |     va_start(__ARGS__, format); | ||||||
|     vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); |     vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||||
|     va_end(__ARGS__); |     va_end(__ARGS__); | ||||||
|  |  | ||||||
|     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, "..."); |         strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     actives[id].size ++; |     actives[id].size ++; | ||||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; |     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 (size_t i = 0; i < actives[id].size; ++i) { | ||||||
|     for (; i <actives[id].size; i ++) { |         strcat(formatted, actives[id].messages[i]); | ||||||
|         strcat(formated, actives[id].messages[i]); |         strcat(formatted, "\n"); | ||||||
|         strcat(formated, "\n"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     formated[strlen(formated) - 1] = '\0'; |     notify_notification_update(actives[id].box, actives[id].title, formatted, NULL); | ||||||
|  |  | ||||||
|     notify_notification_update(actives[id].box, actives[id].title, formated, NULL); |  | ||||||
|     notify_notification_show(actives[id].box, NULL); |     notify_notification_show(actives[id].box, NULL); | ||||||
|  |  | ||||||
|  |     free(formatted); | ||||||
|  |  | ||||||
|     control_unlock(); |     control_unlock(); | ||||||
|  |  | ||||||
|     return id; |     return id; | ||||||
| #else | #else | ||||||
|     return -1; |     return -1; | ||||||
| #endif | #endif /* BOX_NOTIFY */ | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								src/notify.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/notify.h
									
									
									
									
									
								
							| @@ -23,11 +23,10 @@ | |||||||
| #ifndef NOTIFY_H | #ifndef NOTIFY_H | ||||||
| #define NOTIFY_H | #define NOTIFY_H | ||||||
|  |  | ||||||
| #include <inttypes.h> | #include <stdint.h> | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| typedef enum _Notification | typedef enum _Notification { | ||||||
| { |  | ||||||
|     silent = -1, |     silent = -1, | ||||||
|     notif_error, |     notif_error, | ||||||
|     self_log_in, |     self_log_in, | ||||||
| @@ -61,14 +60,18 @@ typedef enum _Flags { | |||||||
| } Flags; | } Flags; | ||||||
|  |  | ||||||
| int init_notify(int login_cooldown, int notification_timeout); | int init_notify(int login_cooldown, int notification_timeout); | ||||||
| void terminate_notify(); | void terminate_notify(void); | ||||||
|  |  | ||||||
|  | /* 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_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator); | ||||||
| int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id); | int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id); | ||||||
|  |  | ||||||
| void stop_sound(int id); | void stop_sound(int id); | ||||||
|  |  | ||||||
| int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...); | int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title, | ||||||
|  |                const char *format, ...); | ||||||
| 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, ...); | ||||||
| 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, ...); | ||||||
| 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, ...); | ||||||
|   | |||||||
| @@ -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); | 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__ | #ifdef __OBJC__ | ||||||
| @interface OSXVideo : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> | @interface OSXVideo : | ||||||
| - (instancetype)initWithDeviceNames:(char **)device_names AmtDevices:(int *)size; |     NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> | ||||||
|  | - (instancetype)initWithDeviceNames: | ||||||
|  |     (char **)device_names AmtDevices: | ||||||
|  |     (int *)size; | ||||||
| @end | @end | ||||||
| #endif /* __OBJC__ */ | #endif /* __OBJC__ */ | ||||||
|  |  | ||||||
| int osx_video_init(char **device_names, int *size); | int osx_video_init(char **device_names, int *size); | ||||||
| void osx_video_release(); | void osx_video_release(void); | ||||||
| /* Start device */ | /* Start device */ | ||||||
| int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height); | int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height); | ||||||
| /* Stop device */ | /* Stop device */ | ||||||
|   | |||||||
| @@ -67,6 +67,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|  |  | ||||||
|     for (y = 0; y != height; y += 2) { |     for (y = 0; y != height; y += 2) { | ||||||
|         p = rgb; |         p = rgb; | ||||||
|  |  | ||||||
|         for (x = 0; x != width; x++) { |         for (x = 0; x != width; x++) { | ||||||
|             b = *rgb++; |             b = *rgb++; | ||||||
|             g = *rgb++; |             g = *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); |             *plane_y++ = rgb_to_y(r, g, b); | ||||||
|  |  | ||||||
|             b = ((int)b + (int)*(rgb - 8) + (int)*p + (int)*(p + 4) + 2) / 4; p++; |             b = ((int)b + (int) * (rgb - 8) + (int) * p + (int) * (p + 4) + 2) / 4; | ||||||
|             g = ((int)g + (int)*(rgb - 7) + (int)*p + (int)*(p + 4) + 2) / 4; p++; |             p++; | ||||||
|             r = ((int)r + (int)*(rgb - 6) + (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++; |             p++; | ||||||
|  |  | ||||||
|             *plane_u++ = rgb_to_u(r, g, b); |             *plane_u++ = rgb_to_u(r, g, b); | ||||||
| @@ -122,11 +126,15 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|     BOOL _shouldMangleDimensions; |     BOOL _shouldMangleDimensions; | ||||||
| } | } | ||||||
|  |  | ||||||
| - (instancetype)initWithDeviceNames: (char **)device_names AmtDevices: (int *)size { | - (instancetype)initWithDeviceNames: | ||||||
|  |     (char **)device_names AmtDevices: | ||||||
|  |     (int *)size | ||||||
|  | { | ||||||
|     _session = [[AVCaptureSession alloc] init]; |     _session = [[AVCaptureSession alloc] init]; | ||||||
|  |  | ||||||
|     NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; |     NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; i < [devices count]; ++i) { |     for (i = 0; i < [devices count]; ++i) { | ||||||
|         AVCaptureDevice *device = [devices objectAtIndex: i]; |         AVCaptureDevice *device = [devices objectAtIndex: i]; | ||||||
|         char *video_input_name; |         char *video_input_name; | ||||||
| @@ -136,14 +144,17 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|         device_names[i] = video_input_name; |         device_names[i] = video_input_name; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ( i <= 0 ) |     if (i <= 0) { | ||||||
|         return nil; |         return nil; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     *size = i; |     *size = i; | ||||||
|  |  | ||||||
|     return self; |     return self; | ||||||
| } | } | ||||||
|  |  | ||||||
| - (void)dealloc { | - (void)dealloc | ||||||
|  | { | ||||||
|     pthread_mutex_destroy(&_frameLock); |     pthread_mutex_destroy(&_frameLock); | ||||||
|     [_session release]; |     [_session release]; | ||||||
|     [_linkerVideo release]; |     [_linkerVideo release]; | ||||||
| @@ -151,7 +162,11 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|     [super dealloc]; |     [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_init(&_frameLock, NULL); | ||||||
|     pthread_mutex_lock(&_frameLock); |     pthread_mutex_lock(&_frameLock); | ||||||
|     _processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL); |     _processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL); | ||||||
| @@ -178,6 +193,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|     /* Obtain device resolution */ |     /* Obtain device resolution */ | ||||||
|     AVCaptureInputPort *port = [input.ports objectAtIndex: 0]; |     AVCaptureInputPort *port = [input.ports objectAtIndex: 0]; | ||||||
|     CMFormatDescriptionRef format_description = port.formatDescription; |     CMFormatDescriptionRef format_description = port.formatDescription; | ||||||
|  |  | ||||||
|     if (format_description) { |     if (format_description) { | ||||||
|         CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description); |         CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description); | ||||||
|         *width = dimensions.width; |         *width = dimensions.width; | ||||||
| @@ -189,14 +205,18 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|  |  | ||||||
|     _linkerVideo = [[AVCaptureVideoDataOutput alloc] init]; |     _linkerVideo = [[AVCaptureVideoDataOutput alloc] init]; | ||||||
|     [_linkerVideo setSampleBufferDelegate: self queue: _processingQueue]; |     [_linkerVideo setSampleBufferDelegate: self queue: _processingQueue]; | ||||||
|  |  | ||||||
|     // TODO possibly get a better pixel format |     // TODO possibly get a better pixel format | ||||||
|     if (_shouldMangleDimensions) { |     if (_shouldMangleDimensions) { | ||||||
|         [_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), |         [_linkerVideo setVideoSettings: @ { | ||||||
|  |              (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), | ||||||
|              (id)kCVPixelBufferWidthKey: @640, |              (id)kCVPixelBufferWidthKey: @640, | ||||||
|                                          (id)kCVPixelBufferHeightKey: @480}]; |              (id)kCVPixelBufferHeightKey: @480 | ||||||
|  |                      }]; | ||||||
|     } else { |     } else { | ||||||
|         [_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}]; |         [_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [_session addOutput: _linkerVideo]; |     [_session addOutput: _linkerVideo]; | ||||||
|     [_session startRunning]; |     [_session startRunning]; | ||||||
|  |  | ||||||
| @@ -204,7 +224,9 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| - (void)closeVideoDeviceIndex: (uint32_t)device_idx { | - (void)closeVideoDeviceIndex: | ||||||
|  |     (uint32_t)device_idx | ||||||
|  | { | ||||||
|     NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; |     NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; | ||||||
|     AVCaptureDevice *device = [devices objectAtIndex: device_idx]; |     AVCaptureDevice *device = [devices objectAtIndex: device_idx]; | ||||||
|     NSError *error = NULL; |     NSError *error = NULL; | ||||||
| @@ -215,9 +237,14 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|     [_linkerVideo release]; |     [_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); |     pthread_mutex_lock(&_frameLock); | ||||||
|     CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer); |     CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer); | ||||||
|  |  | ||||||
|     if (!img) { |     if (!img) { | ||||||
|         NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!"); |         NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!"); | ||||||
|     } else { |     } 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 |         // we're not going to do anything to it, so it's safe to lock it always | ||||||
|         CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly); |         CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pthread_mutex_unlock(&_frameLock); |     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) { |     if (!_currentFrame) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -240,6 +274,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | |||||||
|     CFRetain(_currentFrame); |     CFRetain(_currentFrame); | ||||||
|  |  | ||||||
|     CFTypeID imageType = CFGetTypeID(_currentFrame); |     CFTypeID imageType = CFGetTypeID(_currentFrame); | ||||||
|  |  | ||||||
|     if (imageType == CVPixelBufferGetTypeID()) { |     if (imageType == CVPixelBufferGetTypeID()) { | ||||||
|         // TODO maybe handle other formats |         // TODO maybe handle other formats | ||||||
|         bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height); |         bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height); | ||||||
| @@ -269,8 +304,9 @@ int osx_video_init(char **device_names, int *size) | |||||||
| { | { | ||||||
|     _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size]; |     _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size]; | ||||||
|  |  | ||||||
|     if ( _OSXVideo == nil ) |     if (_OSXVideo == nil) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -283,8 +319,9 @@ void osx_video_release() | |||||||
|  |  | ||||||
| int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height) | int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height) | ||||||
| { | { | ||||||
|     if ( _OSXVideo == nil ) |     if (_OSXVideo == nil) { | ||||||
|         return -1; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height]; |     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) | 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 -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height]; |     return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height]; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										458
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										458
									
								
								src/prompt.c
									
									
									
									
									
								
							| @@ -28,20 +28,20 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <wchar.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 "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 ToxWindow *prompt; | ||||||
| extern struct user_settings *user_settings; | extern struct user_settings *user_settings; | ||||||
| @@ -49,49 +49,50 @@ extern struct Winthread Winthread; | |||||||
|  |  | ||||||
| extern FriendsList Friends; | extern FriendsList Friends; | ||||||
| FriendRequests FrndRequests; | 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. */ | /* Array of global command names used for tab completion. */ | ||||||
| static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { | static const char *glob_cmd_list[] = { | ||||||
|     { "/accept"     }, |     "/accept", | ||||||
|     { "/add"        }, |     "/add", | ||||||
|     { "/avatar"     }, |     "/avatar", | ||||||
|     { "/clear"      }, |     "/clear", | ||||||
|     { "/connect"    }, |     "/connect", | ||||||
|     { "/decline"    }, |     "/decline", | ||||||
|     { "/exit"       }, |     "/exit", | ||||||
|     { "/group"      }, |     "/conference", | ||||||
|     { "/help"       }, |     "/help", | ||||||
|     { "/log"        }, |     "/log", | ||||||
|     { "/myid"       }, |     "/myid", | ||||||
|     { "/myqr"       }, | #ifdef QRCODE | ||||||
|     { "/nick"       }, |     "/myqr", | ||||||
|     { "/note"       }, | #endif /* QRCODE */ | ||||||
|     { "/nospam"     }, |     "/nick", | ||||||
|     { "/quit"       }, |     "/note", | ||||||
|     { "/requests"   }, |     "/nospam", | ||||||
|     { "/status"     }, |     "/quit", | ||||||
|  |     "/requests", | ||||||
|  |     "/status", | ||||||
|  |  | ||||||
| #ifdef AUDIO | #ifdef AUDIO | ||||||
|  |  | ||||||
|     { "/lsdev"       }, |     "/lsdev", | ||||||
|     { "/sdev"        }, |     "/sdev", | ||||||
|  |  | ||||||
| #endif /* AUDIO */ | #endif /* AUDIO */ | ||||||
|  |  | ||||||
| #ifdef VIDEO | #ifdef VIDEO | ||||||
|  |  | ||||||
|     { "/lsvdev"      }, |     "/lsvdev", | ||||||
|     { "/svdev"       }, |     "/svdev", | ||||||
|  |  | ||||||
| #endif /* VIDEO */ | #endif /* VIDEO */ | ||||||
|  |  | ||||||
|  | #ifdef PYTHON | ||||||
|  |  | ||||||
|  |     "/run", | ||||||
|  |  | ||||||
|  | #endif /* PYTHON */ | ||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void kill_prompt_window(ToxWindow *self) | void kill_prompt_window(ToxWindow *self) | ||||||
| @@ -115,8 +116,10 @@ void kill_prompt_window(ToxWindow *self) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* callback: Updates own connection status in prompt statusbar */ | /* 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 *statusbar = prompt->stb; | ||||||
|     statusbar->connection = connection_status; |     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); |     size_t len = strlen(statusbar->statusmsg); | ||||||
|     statusbar->statusmsg_len = len; |     statusbar->statusmsg_len = len; | ||||||
|  |  | ||||||
|     TOX_ERR_SET_INFO err; |     Tox_Err_Set_Info err; | ||||||
|     tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err); |     tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err); | ||||||
|  |  | ||||||
|     if (err != TOX_ERR_SET_INFO_OK) |     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); |         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 */ | /* 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 *statusbar = prompt->stb; | ||||||
|     statusbar->status = status; |     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. | /* Adds friend request to pending friend requests. | ||||||
|    Returns request number on success, -1 if queue is full. */ |    Returns request number on success, -1 if queue is full. */ | ||||||
| static int add_friend_request(const char *public_key, const char *data) | 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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     int i; |     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); |             memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE); | ||||||
|             snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data); |             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.max_idx; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             ++FrndRequests.num_requests; |             ++FrndRequests.num_requests; | ||||||
|  |  | ||||||
| @@ -178,7 +191,10 @@ static int add_friend_request(const char *public_key, const char *data) | |||||||
|     return -1; |     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; |     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); |     getyx(self->window, y, x); | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (x2 <= 0 || y2 <= 0) |     UNUSED_VAR(y); | ||||||
|         return; |  | ||||||
|  |     if (x2 <= 0 || y2 <= 0) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ctx->pastemode && key == '\r') { | ||||||
|  |         key = '\n'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* ignore non-menu related input if active */ |     /* ignore non-menu related input if active */ | ||||||
|     if (self->help->active) { |     if (self->help->active) { | ||||||
|         help_onKey(self, key); |         help_onKey(self, key); | ||||||
|         return; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ltr) {    /* char is printable */ |     if (ltr || key == '\n') {    /* char is printable */ | ||||||
|         input_new_char(self, key, x, y, x2, y2); |         input_new_char(self, key, x, x2); | ||||||
|         return; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (line_info_onKey(self, key)) |     if (line_info_onKey(self, key)) { | ||||||
|         return; |         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 */ |     if (key == '\t') {    /* TAB key: auto-completes command */ | ||||||
|  |         input_ret = true; | ||||||
|  |  | ||||||
|         if (ctx->len > 1 && ctx->line[0] == '/') { |         if (ctx->len > 1 && ctx->line[0] == '/') { | ||||||
|             int diff = -1; |             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"); |                 diff = dir_match(self, m, ctx->line, L"/avatar"); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  | #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) { |             else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { | ||||||
|                 const char status_cmd_list[3][8] = { |                 const char *status_cmd_list[] = { | ||||||
|                   {"online"}, |                     "online", | ||||||
|                   {"away"}, |                     "away", | ||||||
|                   {"busy"}, |                     "busy", | ||||||
|                 }; |                 }; | ||||||
|                 diff = complete_line(self, status_cmd_list, 3, 8); |                 diff = complete_line(self, status_cmd_list, sizeof(status_cmd_list) / sizeof(char *)); | ||||||
|             } else |             } else { | ||||||
|                 diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); |                 diff = complete_line(self, glob_cmd_list, sizeof(glob_cmd_list) / sizeof(char *)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             if (diff != -1) { |             if (diff != -1) { | ||||||
|                 if (x + diff > x2 - 1) { |                 if (x + diff > x2 - 1) { | ||||||
| @@ -232,33 +272,42 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | |||||||
|         } else { |         } else { | ||||||
|             sound_notify(self, notif_error, 0, NULL); |             sound_notify(self, notif_error, 0, NULL); | ||||||
|         } |         } | ||||||
|     } else if (key == '\n') { |     } else if (key == '\r') { | ||||||
|  |         input_ret = true; | ||||||
|  |  | ||||||
|         rm_trailing_spaces_buf(ctx); |         rm_trailing_spaces_buf(ctx); | ||||||
|  |  | ||||||
|         char line[MAX_STR_SIZE] = {0}; |         if (!wstring_is_empty(ctx->line)) { | ||||||
|  |  | ||||||
|         if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) |  | ||||||
|             memset(&line, 0, sizeof(line)); |  | ||||||
|  |  | ||||||
|         if (!string_is_empty(line)) |  | ||||||
|             add_line_to_hist(ctx); |             add_line_to_hist(ctx); | ||||||
|  |             wstrsubst(ctx->line, L'¶', L'\n'); | ||||||
|  |  | ||||||
|         line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); |             char line[MAX_STR_SIZE]; | ||||||
|  |  | ||||||
|  |             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); |                 execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         wclear(ctx->linewin); |         wclear(ctx->linewin); | ||||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); |         wmove(self->window, y2, 0); | ||||||
|         reset_buf(ctx); |         reset_buf(ctx); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     return input_ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void prompt_onDraw(ToxWindow *self, Tox *m) | static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||||
| { | { | ||||||
|     int x2, y2; |     int x2; | ||||||
|  |     int y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     getmaxyx(self->window, y2, x2); | ||||||
|  |  | ||||||
|     if (y2 <= 0 || x2 <= 0) |     if (y2 <= 0 || x2 <= 0) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
| @@ -268,61 +317,88 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | |||||||
|  |  | ||||||
|     wclear(ctx->linewin); |     wclear(ctx->linewin); | ||||||
|  |  | ||||||
|     curs_set(1); |     if (ctx->len > 0) { | ||||||
|  |         mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (ctx->len > 0) |     mvwhline(ctx->linewin, 0, ctx->len, ' ', x2 - ctx->len); | ||||||
|         mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); |  | ||||||
|  |     curs_set(1); | ||||||
|  |  | ||||||
|     StatusBar *statusbar = self->stb; |     StatusBar *statusbar = self->stb; | ||||||
|  |  | ||||||
|     mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2); |  | ||||||
|     wmove(statusbar->topline, 0, 0); |     wmove(statusbar->topline, 0, 0); | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&Winthread.lock); |     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); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     if (connection != TOX_CONNECTION_NONE) { |     if (connection != TOX_CONNECTION_NONE) { | ||||||
|         int colour = MAGENTA; |         int colour = MAGENTA; | ||||||
|         const char *status_text = "ERROR"; |         const char *status_text = "ERROR"; | ||||||
|  |  | ||||||
|         pthread_mutex_lock(&Winthread.lock); |  | ||||||
|         TOX_USER_STATUS status = statusbar->status; |  | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |  | ||||||
|  |  | ||||||
|         switch (status) { |         switch (status) { | ||||||
|             case TOX_USER_STATUS_NONE: |             case TOX_USER_STATUS_NONE: | ||||||
|                 status_text = "Online"; |                 status_text = "Online"; | ||||||
|                 colour = GREEN; |                 colour = STATUS_ONLINE; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_AWAY: |             case TOX_USER_STATUS_AWAY: | ||||||
|                 status_text = "Away"; |                 status_text = "Away"; | ||||||
|                 colour = YELLOW; |                 colour = STATUS_AWAY; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case TOX_USER_STATUS_BUSY: |             case TOX_USER_STATUS_BUSY: | ||||||
|                 status_text = "Busy"; |                 status_text = "Busy"; | ||||||
|                 colour = RED; |                 colour = STATUS_BUSY; | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|         wprintw(statusbar->topline, " [%s]", status_text); |         wprintw(statusbar->topline, " ["); | ||||||
|         wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); |         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); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|         wprintw(statusbar->topline, " %s", statusbar->nick); |         wprintw(statusbar->topline, " %s", statusbar->nick); | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         pthread_mutex_unlock(&Winthread.lock); | ||||||
|         wattroff(statusbar->topline, A_BOLD); |  | ||||||
|     } else { |     } else { | ||||||
|         wprintw(statusbar->topline, " [Offline]"); |         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||||
|         wattron(statusbar->topline, A_BOLD); |         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); |         pthread_mutex_lock(&Winthread.lock); | ||||||
|         wprintw(statusbar->topline, " %s", statusbar->nick); |         wprintw(statusbar->topline, " %s", statusbar->nick); | ||||||
|         pthread_mutex_unlock(&Winthread.lock); |         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 */ |     /* Reset statusbar->statusmsg on window resize */ | ||||||
|     if (x2 != self->x) { |     if (x2 != self->x) { | ||||||
|         char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; |         char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; | ||||||
| @@ -349,97 +425,112 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | |||||||
|         statusbar->statusmsg_len = maxlen; |         statusbar->statusmsg_len = maxlen; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (statusbar->statusmsg[0]) |     if (statusbar->statusmsg[0]) { | ||||||
|         wprintw(statusbar->topline, " : %s", statusbar->statusmsg); |         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); |     pthread_mutex_unlock(&Winthread.lock); | ||||||
|  |  | ||||||
|     mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); |     int y; | ||||||
|  |     int x; | ||||||
|     int y, x; |  | ||||||
|     getyx(self->window, y, 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)); |     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); |         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; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     char nick[TOX_MAX_NAME_LENGTH] = {0};    /* stop removing this initiation */ |     char nick[TOX_MAX_NAME_LENGTH] = {0};    /* stop removing this initiation */ | ||||||
|     get_nick_truncate(m, nick, friendnum); |     get_nick_truncate(m, nick, friendnum); | ||||||
|  |  | ||||||
|     if (!nick[0]) |     if (!nick[0]) { | ||||||
|         snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); |         snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |  | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|     const char *msg; |     const char *msg; | ||||||
|  |  | ||||||
|  |     if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) { |     if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) { | ||||||
|         msg = "has come online"; |         msg = "has come online"; | ||||||
|         line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); |         line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg); | ||||||
|         write_to_log(msg, nick, ctx->log, true); |         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, |             box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box, | ||||||
|                         "%s has come online", nick); |                         "%s has come online", nick); | ||||||
|         else |         } else { | ||||||
|             box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, |             box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, | ||||||
|                        "Toxic", "%s has come online", nick); |                        "Toxic", "%s has come online", nick); | ||||||
|         } |         } | ||||||
|     else if (connection_status == TOX_CONNECTION_NONE) { |     } else if (connection_status == TOX_CONNECTION_NONE) { | ||||||
|         msg = "has gone offline"; |         msg = "has gone offline"; | ||||||
|         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); |         line_info_add(self, true, nick, NULL, DISCONNECTION, 0, RED, msg); | ||||||
|         write_to_log(msg, nick, ctx->log, true); |         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, |             box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box, | ||||||
|                         "%s has gone offline", nick); |                         "%s has gone offline", nick); | ||||||
|         else |         } else { | ||||||
|             box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, |             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) | 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; |     ChatContext *ctx = self->chatwin; | ||||||
|  |  | ||||||
|     char timefrmt[TIME_STR_SIZE]; |     line_info_add(self, true, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data); | ||||||
|     get_time_str(timefrmt, sizeof(timefrmt)); |  | ||||||
|  |  | ||||||
|     line_info_add(self, timefrmt, 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); |     write_to_log("Friend request with the message '%s'", "", ctx->log, true); | ||||||
|  |  | ||||||
|     int n = add_friend_request(key, data); |     int n = add_friend_request(key, data); | ||||||
|  |  | ||||||
|     if (n == -1) { |     if (n == -1) { | ||||||
|         const char *errmsg = "Friend request queue is full. Discarding request."; |         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; |         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); |     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; |     int x2, y2; | ||||||
|     getmaxyx(self->window, y2, x2); |     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); |         exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     (void) y2; |     UNUSED_VAR(y2); | ||||||
|  |  | ||||||
|     /* Init statusbar info */ |     /* Init statusbar info */ | ||||||
|     StatusBar *statusbar = self->stb; |     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); |     size_t s_len = 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); | ||||||
|  |  | ||||||
|     TOX_USER_STATUS status = tox_self_get_status(m); |     Tox_User_Status status = tox_self_get_status(m); | ||||||
|  |  | ||||||
|     nick[n_len] = '\0'; |     nick[n_len] = '\0'; | ||||||
|     statusmsg[s_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"); |         snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic"); | ||||||
|         s_len = strlen(statusmsg); |         s_len = strlen(statusmsg); | ||||||
|         statusmsg[s_len] = '\0'; |         statusmsg[s_len] = '\0'; | ||||||
| @@ -471,90 +562,113 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m) | |||||||
|     prompt_update_nick(prompt, nick); |     prompt_update_nick(prompt, nick); | ||||||
|  |  | ||||||
|     /* Init statusbar subwindow */ |     /* 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) | static void print_welcome_msg(ToxWindow *self) | ||||||
| { | { | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "    _____ _____  _____ ____ "); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "    _____ _____  _____ ____ "); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|"); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|"); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    "); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    "); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ "); |     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ "); | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____| v." TOXICVER); |     line_info_add(self, false, 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, 0, 0, ""); | ||||||
|  |  | ||||||
|     const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client."; |     const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messaging client."; | ||||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg); |     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."; |     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, false, 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, 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) | static void prompt_onInit(ToxWindow *self, Tox *m) | ||||||
| { | { | ||||||
|     curs_set(1); |     curs_set(1); | ||||||
|     int y2, x2; |  | ||||||
|  |     int y2; | ||||||
|  |     int x2; | ||||||
|     getmaxyx(self->window, y2, 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); |         exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ChatContext *ctx = self->chatwin; |     ChatContext *ctx = self->chatwin; | ||||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); |  | ||||||
|     ctx->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->log = calloc(1, sizeof(struct chatlog)); | ||||||
|     ctx->hst = calloc(1, sizeof(struct history)); |     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); |         exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     line_info_init(ctx->hst); |     line_info_init(ctx->hst); | ||||||
|  |  | ||||||
|     if (user_settings->autolog == AUTOLOG_ON) { |     prompt_init_log(self, m, self->name); | ||||||
|         char myid[TOX_ADDRESS_SIZE]; |  | ||||||
|         tox_self_get_address(m, (uint8_t *) myid); |  | ||||||
|  |  | ||||||
|         if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) |  | ||||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     scrollok(ctx->history, 0); |     scrollok(ctx->history, 0); | ||||||
|     wmove(self->window, y2 - CURS_Y_OFFSET, 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); |         print_welcome_msg(self); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| ToxWindow new_prompt(void) | ToxWindow *new_prompt(void) | ||||||
| { | { | ||||||
|     ToxWindow ret; |     ToxWindow *ret = calloc(1, sizeof(ToxWindow)); | ||||||
|     memset(&ret, 0, sizeof(ret)); |  | ||||||
|  |  | ||||||
|     ret.num = -1; |     if (ret == NULL) { | ||||||
|     ret.active = true; |         exit_toxic_err("failed in new_prompt", FATALERR_MEMORY); | ||||||
|     ret.is_prompt = true; |     } | ||||||
|  |  | ||||||
|     ret.onKey = &prompt_onKey; |     ret->num = -1; | ||||||
|     ret.onDraw = &prompt_onDraw; |     ret->type = WINDOW_TYPE_PROMPT; | ||||||
|     ret.onInit = &prompt_onInit; |  | ||||||
|     ret.onConnectionChange = &prompt_onConnectionChange; |  | ||||||
|     ret.onFriendRequest = &prompt_onFriendRequest; |  | ||||||
|  |  | ||||||
|     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)); |     ChatContext *chatwin = calloc(1, sizeof(ChatContext)); | ||||||
|     StatusBar *stb = calloc(1, sizeof(StatusBar)); |     StatusBar *stb = calloc(1, sizeof(StatusBar)); | ||||||
|     Help *help = calloc(1, sizeof(Help)); |     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); |         exit_toxic_err("failed in new_prompt", FATALERR_MEMORY); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ret.chatwin = chatwin; |     ret->chatwin = chatwin; | ||||||
|     ret.stb = stb; |     ret->stb = stb; | ||||||
|     ret.help = help; |     ret->help = help; | ||||||
|  |  | ||||||
|     ret.active_box = -1; |     ret->active_box = -1; | ||||||
|  |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								src/prompt.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/prompt.h
									
									
									
									
									
								
							| @@ -26,31 +26,34 @@ | |||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.h" | #include "windows.h" | ||||||
|  |  | ||||||
| #define MAX_FRIEND_REQUESTS 32 | #define MAX_FRIEND_REQUESTS 20 | ||||||
|  |  | ||||||
| struct friend_request { | struct friend_request { | ||||||
|     bool active; |     bool active; | ||||||
|     char msg[MAX_STR_SIZE]; |     char msg[TOX_MAX_FRIEND_REQUEST_LENGTH + 1]; | ||||||
|     uint8_t key[TOX_PUBLIC_KEY_SIZE]; |     uint8_t key[TOX_PUBLIC_KEY_SIZE]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct FriendRequests { | ||||||
|     int max_idx; |     int max_idx; | ||||||
|     int num_requests; |     int num_requests; | ||||||
|     struct friend_request request[MAX_FRIEND_REQUESTS]; |     struct friend_request request[MAX_FRIEND_REQUESTS]; | ||||||
| } FriendRequests; | } FriendRequests; | ||||||
|  |  | ||||||
| ToxWindow new_prompt(void); | ToxWindow *new_prompt(void); | ||||||
|  |  | ||||||
| void prep_prompt_win(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_nick(ToxWindow *prompt, const char *nick); | ||||||
| void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg); | 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 prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected); | ||||||
| void kill_prompt_window(ToxWindow *self); | void kill_prompt_window(ToxWindow *self); | ||||||
|  |  | ||||||
| /* callback: Updates own connection status in prompt statusbar */ | /* 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 */ | #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. |  *  This file is part of Toxic. | ||||||
|  * |  * | ||||||
| @@ -20,22 +20,20 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #ifndef XTRA_H | #ifndef PYTHON_API_H | ||||||
| #define XTRA_H | #define PYTHON_API_H | ||||||
| 
 | 
 | ||||||
| /* NOTE: If no xlib present don't compile */ | #ifdef PYTHON | ||||||
|  | #include <Python.h> | ||||||
|  | #endif /* PYTHON */ | ||||||
| 
 | 
 | ||||||
| typedef enum { | PyMODINIT_FUNC PyInit_toxic_api(void); | ||||||
|     DT_plain, | void terminate_python(void); | ||||||
|     DT_file_list | void init_python(Tox *m); | ||||||
| }  | void run_python(FILE *fp, char *path); | ||||||
| DropType; | 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); | #endif /* PYTHON_API_H */ | ||||||
| 
 |  | ||||||
| int               init_xtra(drop_callback d); |  | ||||||
| void              terminate_xtra(); |  | ||||||
| long unsigned int focused_window_id(); |  | ||||||
| int               is_focused(); /* returns bool */ |  | ||||||
| 
 |  | ||||||
| #endif /* XTRA_H */ |  | ||||||
							
								
								
									
										167
									
								
								src/qr_code.c
									
									
									
									
									
								
							
							
						
						
									
										167
									
								
								src/qr_code.c
									
									
									
									
									
								
							| @@ -20,45 +20,62 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <stdlib.h> | #ifdef QRCODE | ||||||
|  |  | ||||||
| #include <qrencode.h> | #include <qrencode.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #include "qr_code.h" | ||||||
| #include "toxic.h" | #include "toxic.h" | ||||||
| #include "windows.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 BORDER_LEN 1 | ||||||
| #define CHAR_1 "\342\226\210" | #define CHAR_1 "\342\226\210" | ||||||
| #define CHAR_2 "\342\226\204" | #define CHAR_2 "\342\226\204" | ||||||
| #define CHAR_3 "\342\226\200" | #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 0 on success. | ||||||
|  * Returns -1 on failure. |  * 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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0); |     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; |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     size_t width = qr_obj->width; |     size_t width = qr_obj->width; | ||||||
|     size_t i, j; |     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, "%s", CHAR_1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     fprintf(fp, "\n"); |     fprintf(fp, "\n"); | ||||||
|  |  | ||||||
|     for (i = 0; i < width; i += 2) { |     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); |             fprintf(fp, "%s", CHAR_1); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         const unsigned char *row_1 = qr_obj->data + width * i; |         const unsigned char *row_1 = qr_obj->data + width * i; | ||||||
|         const unsigned char *row_2 = row_1 + width; |         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 x = row_1[j] & 1; | ||||||
|             bool y = (i + 1) < width ? (row_2[j] & 1) : false; |             bool y = (i + 1) < width ? (row_2[j] & 1) : false; | ||||||
|  |  | ||||||
|             if (x && y) |             if (x && y) { | ||||||
|                 fprintf(fp, " "); |                 fprintf(fp, " "); | ||||||
|             else if (x) |             } else if (x) { | ||||||
|                 fprintf(fp, "%s", CHAR_2); |                 fprintf(fp, "%s", CHAR_2); | ||||||
|             else if (y) |             } else if (y) { | ||||||
|                 fprintf(fp, "%s", CHAR_3); |                 fprintf(fp, "%s", CHAR_3); | ||||||
|             else |             } else { | ||||||
|                 fprintf(fp, "%s", CHAR_1); |                 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, "%s", CHAR_1); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         fprintf(fp, "\n"); |         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); |     QRcode_free(qr_obj); | ||||||
|  |  | ||||||
|     return 0; |     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