mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-10-31 01:06:52 +01:00 
			
		
		
		
	Compare commits
	
		
			237 Commits
		
	
	
		
			new_groupc
			...
			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 | 
							
								
								
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| /.github/	@TokTok/admins | ||||
							
								
								
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| --- | ||||
| github: [JFreegman] | ||||
							
								
								
									
										17
									
								
								.github/settings.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.github/settings.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| --- | ||||
| _extends: .github | ||||
|  | ||||
| repository: | ||||
|   name: toxic | ||||
|   description: An ncurses-based Tox client | ||||
|   topics: tox, console, chat | ||||
|  | ||||
| branches: | ||||
|   - name: "master" | ||||
|     protection: | ||||
|       required_status_checks: | ||||
|         contexts: | ||||
|           - Codacy/PR Quality Review | ||||
|           - CodeFactor | ||||
|           - Travis CI - Pull Request | ||||
|           - code-review/reviewable | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -16,3 +16,4 @@ stamp-h1 | ||||
| build/toxic | ||||
| build/*.o | ||||
| build/*.d | ||||
| apidoc/python/build | ||||
|   | ||||
							
								
								
									
										4
									
								
								.restyled.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.restyled.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| --- | ||||
| restylers: | ||||
|   - astyle: | ||||
|       arguments: ["--options=astylerc"] | ||||
							
								
								
									
										60
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| --- | ||||
| language: python | ||||
| python: nightly | ||||
| dist: xenial | ||||
| os: linux | ||||
|  | ||||
| jobs: | ||||
|   include: | ||||
|     - env: JOB=linux | ||||
|  | ||||
|       addons: | ||||
|         apt: | ||||
|           packages: | ||||
|             - libalut-dev | ||||
|             - libconfig-dev | ||||
|             - libnotify-dev | ||||
|             - libopenal-dev | ||||
|             - libopus-dev | ||||
|             - libqrencode-dev | ||||
|             - libvpx-dev | ||||
|  | ||||
|       cache: | ||||
|         directories: | ||||
|           - $HOME/cache | ||||
|  | ||||
|       install: | ||||
|         # Where to find libraries. | ||||
|         - export LD_LIBRARY_PATH=$HOME/cache/usr/lib | ||||
|         - export PKG_CONFIG_PATH=$HOME/cache/usr/lib/pkgconfig | ||||
|         # c-sodium | ||||
|         - git clone --depth=1 --branch=stable https://github.com/jedisct1/libsodium ../libsodium | ||||
|         - test -f $HOME/cache/usr/lib/libsodium.so || (cd ../libsodium && ./configure --prefix=$HOME/cache/usr && make install -j$(nproc)) | ||||
|         # c-toxcore | ||||
|         - git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore | ||||
|         - test -f $HOME/cache/usr/lib/libtoxcore.so || (cd ../c-toxcore && cmake -B_build -H. -DCMAKE_INSTALL_PREFIX:PATH=$HOME/cache/usr && make -C_build install -j$(nproc)) | ||||
|  | ||||
|       script: | ||||
|         - make ENABLE_PYTHON=1 -j$(nproc) | ||||
|  | ||||
|     - env: JOB=macos | ||||
|       os: macos | ||||
|       language: c | ||||
|  | ||||
|       cache: | ||||
|         directories: | ||||
|           - $HOME/cache | ||||
|  | ||||
|       install: | ||||
|         - brew install | ||||
|           freealut | ||||
|           libconfig | ||||
|           libqrencode | ||||
|           libsodium | ||||
|           openal-soft | ||||
|         - export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.21.0/lib" | ||||
|         - git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore | ||||
|         - test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc)) | ||||
|  | ||||
|       script: | ||||
|         - make ENABLE_PYTHON=1 DISABLE_DESKTOP_NOTIFY=1 DISABLE_X11=1 DISABLE_AV=1 DISABLE_SOUND_NOTIFY=1 -j$(nproc) | ||||
							
								
								
									
										41
									
								
								BUILD.bazel
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								BUILD.bazel
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| load("@rules_cc//cc:defs.bzl", "cc_binary") | ||||
| load("//tools/project:build_defs.bzl", "project") | ||||
|  | ||||
| project() | ||||
|  | ||||
| cc_binary( | ||||
|     name = "toxic", | ||||
|     srcs = glob( | ||||
|         [ | ||||
|             "src/*.c", | ||||
|             "src/*.h", | ||||
|         ], | ||||
|         exclude = ["src/video*"], | ||||
|     ) + select({ | ||||
|         "//tools/config:linux": glob(["src/video*"]), | ||||
|         "//tools/config:osx": [], | ||||
|     }), | ||||
|     copts = [ | ||||
|         "-std=gnu99", | ||||
|         "-DAUDIO", | ||||
|         "-DPACKAGE_DATADIR='\"data\"'", | ||||
|         "-DPYTHON", | ||||
|         "-DQRCODE", | ||||
|     ] + select({ | ||||
|         "//tools/config:linux": ["-DVIDEO"], | ||||
|         "//tools/config:osx": [], | ||||
|     }), | ||||
|     deps = [ | ||||
|         "//c-toxcore", | ||||
|         "@curl", | ||||
|         "@libconfig", | ||||
|         "@libqrencode", | ||||
|         "@libvpx", | ||||
|         "@ncurses", | ||||
|         "@openal", | ||||
|         "@python3//:python", | ||||
|     ] + select({ | ||||
|         "//tools/config:linux": ["@x11"], | ||||
|         "//tools/config:osx": [], | ||||
|     }), | ||||
| ) | ||||
							
								
								
									
										70
									
								
								INSTALL.md
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								INSTALL.md
									
									
									
									
									
								
							| @@ -1,70 +1,70 @@ | ||||
| # Installation | ||||
| * [Dependencies](#deps) | ||||
|   * [OS X Notes](#deps_osx) | ||||
| * [Dependencies](#dependencies) | ||||
|   * [OS X Notes](#os-x-notes) | ||||
| * [Compiling](#compiling) | ||||
|   * [Documentation](#docs) | ||||
|   * [Documentation](#documentation) | ||||
| * [Notes](#notes) | ||||
|   * [Compilation variables](#comp_vars) | ||||
|   * [Packaging](#packaging) | ||||
|   * [Compilation variables](#compilation-variables) | ||||
|   * [Environment variables](#environment-variables) | ||||
|  | ||||
| <a name="deps" /> | ||||
| ## Dependencies | ||||
| | Name                                                 | Needed by                  | Debian package      | | ||||
| |------------------------------------------------------|----------------------------|---------------------| | ||||
| | [Tox Core](https://github.com/irungentoo/toxcore)    | BASE                       | *None*              | | ||||
| | [Tox Core](https://github.com/toktok/c-toxcore)      | BASE                       | *None*              | | ||||
| | [NCurses](https://www.gnu.org/software/ncurses)      | BASE                       | libncursesw5-dev    | | ||||
| | [LibConfig](http://www.hyperrealm.com/libconfig)     | BASE                       | libconfig-dev       | | ||||
| | [GNUmake](https://www.gnu.org/software/make)         | BASE                       | make                | | ||||
| | [libcurl](http://curl.haxx.se/)                      | BASE                       | libcurl4-openssl-dev| | ||||
| | [libqrencode](https://fukuchi.org/works/qrencode/)   | BASE                       | libqrencode-dev     | | ||||
| | [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO                      | *None*              | | ||||
| | [libqrencode](https://fukuchi.org/works/qrencode/)   | QRCODE                     | libqrencode-dev     | | ||||
| | [OpenAL](http://openal.org)                          | AUDIO, SOUND NOTIFICATIONS | libopenal-dev       | | ||||
| | [OpenALUT](http://openal.org)                        | SOUND NOTIFICATIONS        | libalut-dev         | | ||||
| | [LibNotify](https://developer.gnome.org/libnotify)   | DESKTOP NOTIFICATIONS      | libnotify-dev       | | ||||
| | [Python 3](http://www.python.org/)                   | PYTHON                     | python3-dev         | | ||||
| | [AsciiDoc](http://asciidoc.org/index.html)           | DOCUMENTATION<sup>1</sup>  | asciidoc            | | ||||
| <sup>1</sup>: see [Documentation](#docs) | ||||
|  | ||||
| <a name="deps_osx" /> | ||||
| <sup>1</sup>: see [Documentation](#documentation) | ||||
|  | ||||
| #### OS X Notes | ||||
| Using [Homebrew](http://brew.sh): | ||||
| ``` | ||||
| brew install openal-soft freealut libconfig | ||||
| brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb | ||||
| brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb | ||||
| brew install curl qrencode openal-soft freealut libconfig libpng | ||||
| brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb | ||||
| brew install libnotify | ||||
| export PKG_CONFIG_PATH=/usr/local/opt/openal-soft/lib/pkgconfig | ||||
| make | ||||
| ``` | ||||
|  | ||||
| You can omit `libnotify` if you intend to build without desktop notifications enabled. | ||||
|  | ||||
| <a name="Compiling"> | ||||
| ## Compiling | ||||
| ``` | ||||
| make PREFIX="/where/to/install" | ||||
| sudo make install PREFIX="/where/to/install" | ||||
| make | ||||
| sudo make install | ||||
| ``` | ||||
|  | ||||
| <a name="docs" /> | ||||
| #### Documentation | ||||
| Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br /> | ||||
| **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br /> | ||||
| **NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them. | ||||
| **Note for developers**: asciidoc files and generated manpages will need to be committed together.<br /> | ||||
| **Note for everyone**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them. | ||||
|  | ||||
| <a name="notes" /> | ||||
| ## Notes | ||||
|  | ||||
| <a name="comp_vars" /> | ||||
| #### Compilation variables | ||||
| * You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""` | ||||
| * You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones) | ||||
| * Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables: | ||||
|   * `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking) | ||||
|   * `DISABLE_AV=1` → build toxic without audio call support | ||||
|   * `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support | ||||
|   * `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support | ||||
| * You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables | ||||
| * Default compile options can be overridden by using special variables: | ||||
|   * `DISABLE_X11=1` → Disable X11 support (needed for focus tracking) | ||||
|   * `DISABLE_AV=1` → Disable audio call support | ||||
|   * `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support | ||||
|   * `DISABLE_QRCODE` → Disable QR exporting support | ||||
|   * `DISABLE_QRPNG` → Disable support for exporting QR as PNG | ||||
|   * `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support | ||||
|   * `ENABLE_PYTHON=1` → Build toxic with Python scripting support | ||||
|   * `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations | ||||
|   * `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled | ||||
|  | ||||
| <a name="packaging" /> | ||||
| #### Packaging | ||||
| * For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files | ||||
| * `DESTDIR=""` can be used in addition to `PREFIX=""`: | ||||
|   * `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg") | ||||
|   * `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local") | ||||
| * `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg") | ||||
|  | ||||
| #### Environment variables | ||||
| * You can use the `CFLAGS` and `LDFLAGS` environment variables to add specific flags to the Makefile | ||||
| * The `PREFIX` environment variable specifies a base install directory for binaries and data files. This is interchangeable with the `DESTDIR` variable, and is generally used by systems that have the `PREFIX` environment variable set by default.<br /> | ||||
| **Note**: `sudo` does not preserve user environment variables by default on some systems. See the `sudoers` manual for more information. | ||||
|   | ||||
							
								
								
									
										47
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								Makefile
									
									
									
									
									
								
							| @@ -3,39 +3,52 @@ CFG_DIR = $(BASE_DIR)/cfg | ||||
|  | ||||
| -include $(CFG_DIR)/global_vars.mk | ||||
|  | ||||
| LIBS = libtoxcore ncursesw libconfig libqrencode | ||||
| LIBS = toxcore ncursesw libconfig libcurl | ||||
|  | ||||
| CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all | ||||
| CFLAGS ?= -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wno-missing-braces | ||||
| CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | ||||
| CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | ||||
| CFLAGS += $(USER_CFLAGS) | ||||
| LDFLAGS = $(USER_LDFLAGS) | ||||
| CFLAGS += ${USER_CFLAGS} | ||||
| LDFLAGS ?= | ||||
| LDFLAGS += ${USER_LDFLAGS} | ||||
|  | ||||
| OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o | ||||
| OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o | ||||
| OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o conference.o help.o input.o | ||||
| OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o | ||||
| OBJ += term_mplex.o toxic.o toxic_strings.o windows.o | ||||
|  | ||||
| # Check if debug build is enabled | ||||
| RELEASE := $(shell if [ -z "$(ENABLE_RELEASE)" ] || [ "$(ENABLE_RELEASE)" = "0" ] ; then echo disabled ; else echo enabled ; fi) | ||||
| ifneq ($(RELEASE), enabled) | ||||
| 	CFLAGS += -O0 -g -DDEBUG | ||||
| 	LDFLAGS += -O0 | ||||
| else | ||||
| 	CFLAGS += -O2 -flto | ||||
| 	LDFLAGS += -O2 -flto | ||||
| endif | ||||
|  | ||||
| # Check if LLVM Address Sanitizer is enabled | ||||
| ENABLE_ASAN := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi) | ||||
| ifneq ($(ENABLE_ASAN), disabled) | ||||
| 	CFLAGS += -fsanitize=address -fno-omit-frame-pointer | ||||
| endif | ||||
|  | ||||
| # Check on wich system we are running | ||||
| UNAME_S = $(shell uname -s) | ||||
| ifeq ($(UNAME_S), Linux) | ||||
|     -include $(CFG_DIR)/systems/Linux.mk | ||||
| endif | ||||
| ifeq ($(UNAME_S), FreeBSD) | ||||
|     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||
| endif | ||||
| ifeq ($(UNAME_S), DragonFly) | ||||
|     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||
| LDFLAGS += -ldl -lrt | ||||
| endif | ||||
| ifeq ($(UNAME_S), OpenBSD) | ||||
|     -include $(CFG_DIR)/systems/FreeBSD.mk | ||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | ||||
| LDFLAGS += -lncursesw | ||||
| endif | ||||
| ifeq ($(UNAME_S), NetBSD) | ||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | ||||
| LDFLAGS += -lncursesw | ||||
| endif | ||||
| ifeq ($(UNAME_S), Darwin) | ||||
|     -include $(CFG_DIR)/systems/Darwin.mk | ||||
| endif | ||||
| ifeq ($(UNAME_S), Solaris) | ||||
|     -include $(CFG_DIR)/systems/Solaris.mk | ||||
| endif | ||||
|  | ||||
| # Check on which platform we are running | ||||
| UNAME_M = $(shell uname -m) | ||||
| @@ -72,7 +85,7 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | ||||
| 	fi | ||||
| 	@echo "  CC    $(@:$(BUILD_DIR)/%=%)" | ||||
| 	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c | ||||
| 	@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d | ||||
| 	@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c >$(BUILD_DIR)/$*.d | ||||
|  | ||||
| clean: | ||||
| 	rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic | ||||
|   | ||||
| @@ -3,13 +3,12 @@ | ||||
|        src="https://scan.coverity.com/projects/4975/badge.svg"/> | ||||
| </a> | ||||
|  | ||||
| Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application. | ||||
| Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client. | ||||
|  | ||||
| [](https://i.imgur.com/san99Z2.png) | ||||
| [](https://i.imgur.com/TwYA8L0.png) | ||||
|  | ||||
| ## Installation | ||||
| [Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br /> | ||||
| [Compile it yourself](/INSTALL.md) | ||||
| [See the install instructions](/INSTALL.md) | ||||
|  | ||||
| ## Settings | ||||
| Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more. | ||||
|   | ||||
							
								
								
									
										20
									
								
								apidoc/python/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								apidoc/python/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| # Minimal makefile for Sphinx documentation | ||||
| # | ||||
|  | ||||
| # You can set these variables from the command line. | ||||
| SPHINXOPTS    = | ||||
| SPHINXBUILD   = sphinx-build | ||||
| SPHINXPROJ    = toxic_api | ||||
| SOURCEDIR     = source | ||||
| BUILDDIR      = build | ||||
|  | ||||
| # Put it first so that "make" without argument is like "make help". | ||||
| help: | ||||
| 	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | ||||
|  | ||||
| .PHONY: help Makefile | ||||
|  | ||||
| # Catch-all target: route all unknown targets to Sphinx using the new | ||||
| # "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS). | ||||
| %: Makefile | ||||
| 	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | ||||
							
								
								
									
										157
									
								
								apidoc/python/source/conf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								apidoc/python/source/conf.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # toxic_api documentation build configuration file, created by | ||||
| # sphinx-quickstart on Tue May 16 08:58:24 2017. | ||||
| # | ||||
| # This file is execfile()d with the current directory set to its | ||||
| # containing dir. | ||||
| # | ||||
| # Note that not all possible configuration values are present in this | ||||
| # autogenerated file. | ||||
| # | ||||
| # All configuration values have a default; values that are commented out | ||||
| # serve to show the default. | ||||
|  | ||||
| # If extensions (or modules to document with autodoc) are in another directory, | ||||
| # add these directories to sys.path here. If the directory is relative to the | ||||
| # documentation root, use os.path.abspath to make it absolute, like shown here. | ||||
| # | ||||
| # import os | ||||
| # import sys | ||||
| # sys.path.insert(0, os.path.abspath('.')) | ||||
|  | ||||
|  | ||||
| # -- General configuration ------------------------------------------------ | ||||
|  | ||||
| # If your documentation needs a minimal Sphinx version, state it here. | ||||
| # | ||||
| # needs_sphinx = '1.0' | ||||
|  | ||||
| # Add any Sphinx extension module names here, as strings. They can be | ||||
| # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom | ||||
| # ones. | ||||
| extensions = [] | ||||
|  | ||||
| # Add any paths that contain templates here, relative to this directory. | ||||
| templates_path = ['_templates'] | ||||
|  | ||||
| # The suffix(es) of source filenames. | ||||
| # You can specify multiple suffix as a list of string: | ||||
| # | ||||
| # source_suffix = ['.rst', '.md'] | ||||
| source_suffix = '.rst' | ||||
|  | ||||
| # The master toctree document. | ||||
| master_doc = 'index' | ||||
|  | ||||
| # General information about the project. | ||||
| project = 'toxic_api' | ||||
| copyright = '2017, Jakob Kreuze' | ||||
| author = 'Jakob Kreuze' | ||||
|  | ||||
| # The version info for the project you're documenting, acts as replacement for | ||||
| # |version| and |release|, also used in various other places throughout the | ||||
| # built documents. | ||||
| # | ||||
| # The short X.Y version. | ||||
| version = '0.10.0' | ||||
| # The full version, including alpha/beta/rc tags. | ||||
| release = '0.10.0' | ||||
|  | ||||
| # The language for content autogenerated by Sphinx. Refer to documentation | ||||
| # for a list of supported languages. | ||||
| # | ||||
| # This is also used if you do content translation via gettext catalogs. | ||||
| # Usually you set "language" from the command line for these cases. | ||||
| language = None | ||||
|  | ||||
| # List of patterns, relative to source directory, that match files and | ||||
| # directories to ignore when looking for source files. | ||||
| # This patterns also effect to html_static_path and html_extra_path | ||||
| exclude_patterns = [] | ||||
|  | ||||
| # The name of the Pygments (syntax highlighting) style to use. | ||||
| pygments_style = 'sphinx' | ||||
|  | ||||
| # If true, `todo` and `todoList` produce output, else they produce nothing. | ||||
| todo_include_todos = False | ||||
|  | ||||
|  | ||||
| # -- Options for HTML output ---------------------------------------------- | ||||
|  | ||||
| # The theme to use for HTML and HTML Help pages.  See the documentation for | ||||
| # a list of builtin themes. | ||||
| # | ||||
| html_theme = 'alabaster' | ||||
|  | ||||
| # Theme options are theme-specific and customize the look and feel of a theme | ||||
| # further.  For a list of options available for each theme, see the | ||||
| # documentation. | ||||
| # | ||||
| # html_theme_options = {} | ||||
|  | ||||
| # Add any paths that contain custom static files (such as style sheets) here, | ||||
| # relative to this directory. They are copied after the builtin static files, | ||||
| # so a file named "default.css" will overwrite the builtin "default.css". | ||||
| html_static_path = ['_static'] | ||||
|  | ||||
|  | ||||
| # -- Options for HTMLHelp output ------------------------------------------ | ||||
|  | ||||
| # Output file base name for HTML help builder. | ||||
| htmlhelp_basename = 'toxic_apidoc' | ||||
|  | ||||
|  | ||||
| # -- Options for LaTeX output --------------------------------------------- | ||||
|  | ||||
| latex_elements = { | ||||
|     # The paper size ('letterpaper' or 'a4paper'). | ||||
|     # | ||||
|     # 'papersize': 'letterpaper', | ||||
|  | ||||
|     # The font size ('10pt', '11pt' or '12pt'). | ||||
|     # | ||||
|     # 'pointsize': '10pt', | ||||
|  | ||||
|     # Additional stuff for the LaTeX preamble. | ||||
|     # | ||||
|     # 'preamble': '', | ||||
|  | ||||
|     # Latex figure (float) alignment | ||||
|     # | ||||
|     # 'figure_align': 'htbp', | ||||
| } | ||||
|  | ||||
| # Grouping the document tree into LaTeX files. List of tuples | ||||
| # (source start file, target name, title, | ||||
| #  author, documentclass [howto, manual, or own class]). | ||||
| latex_documents = [ | ||||
|     (master_doc, 'toxic_api.tex', 'toxic\\_api Documentation', | ||||
|      'Jakob Kreuze', 'manual'), | ||||
| ] | ||||
|  | ||||
|  | ||||
| # -- Options for manual page output --------------------------------------- | ||||
|  | ||||
| # One entry per manual page. List of tuples | ||||
| # (source start file, name, description, authors, manual section). | ||||
| man_pages = [ | ||||
|     (master_doc, 'toxic_api', 'toxic_api Documentation', | ||||
|      [author], 1) | ||||
| ] | ||||
|  | ||||
|  | ||||
| # -- Options for Texinfo output ------------------------------------------- | ||||
|  | ||||
| # Grouping the document tree into Texinfo files. List of tuples | ||||
| # (source start file, target name, title, author, | ||||
| #  dir menu entry, description, category) | ||||
| texinfo_documents = [ | ||||
|     (master_doc, 'toxic_api', 'toxic_api Documentation', | ||||
|      author, 'toxic_api', 'One line description of project.', | ||||
|      'Miscellaneous'), | ||||
| ] | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										8
									
								
								apidoc/python/source/examples.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								apidoc/python/source/examples.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| ============ | ||||
| API Examples | ||||
| ============ | ||||
|  | ||||
| Fortune | ||||
| ======= | ||||
| .. literalinclude:: fortune.py | ||||
|    :language: python | ||||
							
								
								
									
										37
									
								
								apidoc/python/source/fortune.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								apidoc/python/source/fortune.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| import toxic_api | ||||
| import random | ||||
|  | ||||
| FORTUNES = [ | ||||
|     "A bug in the code is worth two in the documentation.", | ||||
|     "A bug in the hand is better than one as yet undetected.", | ||||
|     "\"A debugged program is one for which you have not yet found the " | ||||
|     "conditions that make it fail.\" -- Jerry Ogdin" | ||||
| ] | ||||
|  | ||||
| def send_fortune(args): | ||||
|     """Callback function that sends the contact of the current window a | ||||
|     given number of random fortunes. | ||||
|     """ | ||||
|     if len(args) != 1: | ||||
|         toxic_api.display("Only one argument allowed!") | ||||
|         return | ||||
|  | ||||
|     try: | ||||
|         count = int(args[0]) | ||||
|     except ValueError: | ||||
|         toxic_api.display("Argument must be a number!") | ||||
|         return | ||||
|  | ||||
|     if count < 0 or count > 20: | ||||
|         toxic_api.display("Argument is too large!") | ||||
|         return | ||||
|  | ||||
|     name = toxic_api.get_nick() | ||||
|  | ||||
|     toxic_api.send("%s has decided to send you %d fortunes:" % (name, count)) | ||||
|     for _ in range(count): | ||||
|         toxic_api.send(random.choice(FORTUNES)) | ||||
|  | ||||
|  | ||||
| toxic_api.register("/fortune", "Send a fortune to the contact of the current " | ||||
|                    "window", send_fortune) | ||||
							
								
								
									
										9
									
								
								apidoc/python/source/index.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								apidoc/python/source/index.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| Toxic Scripting Interface Documentation | ||||
| ======================================= | ||||
|  | ||||
| .. toctree:: | ||||
|    :maxdepth: 2 | ||||
|  | ||||
|    intro | ||||
|    reference | ||||
|    examples | ||||
							
								
								
									
										12
									
								
								apidoc/python/source/intro.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								apidoc/python/source/intro.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| ========================= | ||||
| Toxic Scripting Interface | ||||
| ========================= | ||||
|  | ||||
| A Python scripting interface to `Toxic <https://github.com/JFreegman/toxic>`_. | ||||
|  | ||||
|  | ||||
| Getting Started | ||||
| =============== | ||||
| Toxic is compiled with Python support by default. To access the scripting interface, simply import "toxic_api" in your script. | ||||
|  | ||||
| Scripts can be run by issuing "/run <path>" from toxic, or placing them in the "autorun_path" from your toxic configuration file. | ||||
							
								
								
									
										73
									
								
								apidoc/python/source/reference.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								apidoc/python/source/reference.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| ============= | ||||
| API Reference | ||||
| ============= | ||||
|  | ||||
| Messages | ||||
| ======== | ||||
| .. function:: display(msg) | ||||
|  | ||||
|    Display a message to the user through the current window. | ||||
|  | ||||
|    :param msg: The message to display. | ||||
|    :type msg: string | ||||
|    :rtype: none | ||||
|  | ||||
| .. function:: send(msg) | ||||
|  | ||||
|    Send a message to the user specified by the currently open conversation. | ||||
|  | ||||
|    :param msg: The message to display. | ||||
|    :type msg: string | ||||
|    :rtype: none | ||||
|  | ||||
|  | ||||
| State | ||||
| ===== | ||||
| .. function:: get_nick() | ||||
|  | ||||
|    Return the user's current nickname. | ||||
|  | ||||
|    :rtype: string | ||||
|  | ||||
| .. function:: get_status() | ||||
|  | ||||
|    Return a string representing the user's current status. Can be either "online", "away", or "busy". | ||||
|  | ||||
|    :rtype: string | ||||
|  | ||||
| .. function:: get_status_message() | ||||
|  | ||||
|    Return the user's current status message. | ||||
|  | ||||
|    :rtype: string | ||||
|  | ||||
| .. function:: get_all_friends() | ||||
|  | ||||
|    Return a list of all the user's friends. | ||||
|  | ||||
|    :rtype: list of (string, string) tuples containing the nickname followed by their public key | ||||
|  | ||||
|  | ||||
| Commands | ||||
| ======== | ||||
| .. function:: execute(command, class) | ||||
|  | ||||
|    Executes the given command. The API exports three constants for the class parameter; GLOBAL_COMMAND, CHAT_COMMAND, and GROUPCHAT_COMMAND. | ||||
|  | ||||
|    :param command: The command to execute. | ||||
|    :type command: string | ||||
|    :param class: The class of the command. | ||||
|    :type class: int | ||||
|    :rtype: none | ||||
|  | ||||
| .. function:: register(command, help, callback) | ||||
|  | ||||
|    Register a callback to be executed whenever command is run. The callback function will be called with one argument, a list of arguments from when the user calls the command. | ||||
|  | ||||
|    :param command: The command to listen for. | ||||
|    :type command: string | ||||
|    :param help: A description of the command to be shown in the help menu. | ||||
|    :type help: string | ||||
|    :param callback: The function to be called. | ||||
|    :type callback: callable | ||||
|    :rtype: none | ||||
							
								
								
									
										35
									
								
								astylerc
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								astylerc
									
									
									
									
									
								
							| @@ -1,11 +1,26 @@ | ||||
| # Bracket Style Options | ||||
| --style=kr | ||||
|         --pad-header | ||||
|         --max-code-length=120 | ||||
|                           --convert-tabs | ||||
|                           --indent-switches | ||||
|                           --pad-oper | ||||
|                           --align-pointer=name | ||||
|                                   --align-reference=name | ||||
|                                           --preserve-date | ||||
|                                           --lineend=linux | ||||
|                                                   --break-blocks | ||||
|  | ||||
| # Tab Options | ||||
| --indent=spaces=4 | ||||
|  | ||||
| # Indentation Options | ||||
| --indent-switches | ||||
|  | ||||
| # Padding Options | ||||
| --pad-header | ||||
| --break-blocks | ||||
| --pad-oper | ||||
| --unpad-paren | ||||
| --align-pointer=name | ||||
| --align-reference=name | ||||
|  | ||||
| # Formatting Options | ||||
| --add-brackets | ||||
| --convert-tabs | ||||
| --max-code-length=120 | ||||
|  | ||||
| # Other Options | ||||
| --preserve-date | ||||
| --formatted | ||||
| --lineend=linux | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| # Variables for audio call support | ||||
| AUDIO_LIBS = libtoxav openal | ||||
| AUDIO_LIBS = openal | ||||
| AUDIO_CFLAGS = -DAUDIO | ||||
| ifneq (, $(findstring audio_device.o, $(OBJ))) | ||||
|     AUDIO_OBJ = audio_call.o | ||||
| @@ -8,13 +8,14 @@ else | ||||
| endif | ||||
|  | ||||
| # Check if we can build audio support | ||||
| CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error") | ||||
| CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_AUDIO_LIBS), error) | ||||
|     LIBS += $(AUDIO_LIBS) | ||||
|     LDFLAGS += -lm | ||||
|     CFLAGS += $(AUDIO_CFLAGS) | ||||
|     OBJ += $(AUDIO_OBJ) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     MISSING_AUDIO_LIBS := $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without audio support) | ||||
|     $(warning WARNING -- You need these libraries for audio support) | ||||
|     $(warning WARNING -- $(MISSING_AUDIO_LIBS)) | ||||
|   | ||||
| @@ -1,19 +1,19 @@ | ||||
| CHECKS_DIR = $(CFG_DIR)/checks | ||||
|  | ||||
| # Check if we want build X11 support | ||||
| X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| X11 := $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(X11), disabled) | ||||
|     -include $(CHECKS_DIR)/x11.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build audio support | ||||
| AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| AUDIO := $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(AUDIO), disabled) | ||||
|     -include $(CHECKS_DIR)/audio.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build video support | ||||
| VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| VIDEO := $(shell if [ -z "$(DISABLE_VI)" ] || [ "$(DISABLE_VI)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(X11), disabled) | ||||
| ifneq ($(AUDIO), disabled) | ||||
| ifneq ($(VIDEO), disabled) | ||||
| @@ -23,30 +23,42 @@ endif | ||||
| endif | ||||
|  | ||||
| # Check if we want build sound notifications support | ||||
| SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| SND_NOTIFY := $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(SND_NOTIFY), disabled) | ||||
|     -include $(CHECKS_DIR)/sound_notifications.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build desktop notifications support | ||||
| DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| DESK_NOTIFY := $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(DESK_NOTIFY), disabled) | ||||
|     -include $(CHECKS_DIR)/desktop_notifications.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build QR export support | ||||
| QR_CODE := $(shell if [ -z "$(DISABLE_QRCODE)" ] || [ "$(DISABLE_QRCODE)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(QR_CODE), disabled) | ||||
|     -include $(CHECKS_DIR)/qr.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build QR exported as PNG support | ||||
| QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| QR_PNG := $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(QR_PNG), disabled) | ||||
|     -include $(CHECKS_DIR)/qr_png.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build Python scripting support | ||||
| PYTHON := $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi) | ||||
| ifneq ($(PYTHON), disabled) | ||||
|     -include $(CHECKS_DIR)/python.mk | ||||
| endif | ||||
|  | ||||
| # Check if we can build Toxic | ||||
| CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") | ||||
| CHECK_LIBS := $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_LIBS), error) | ||||
|     CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) | ||||
|     LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS)) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     MISSING_LIBS := $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning ERROR -- Cannot compile Toxic) | ||||
|     $(warning ERROR -- You need these libraries) | ||||
|     $(warning ERROR -- $(MISSING_LIBS)) | ||||
|   | ||||
| @@ -3,12 +3,12 @@ DESK_NOTIFY_LIBS = libnotify | ||||
| DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY | ||||
|  | ||||
| # Check if we can build desktop notifications support | ||||
| CHECK_DESK_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error") | ||||
| CHECK_DESK_NOTIFY_LIBS := $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_DESK_NOTIFY_LIBS), error) | ||||
|     LIBS += $(DESK_NOTIFY_LIBS) | ||||
|     CFLAGS += $(DESK_NOTIFY_CFLAGS) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     MISSING_DESK_NOTIFY_LIBS := $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without desktop notifications support) | ||||
|     $(warning WARNING -- You need these libraries for desktop notifications support) | ||||
|     $(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS)) | ||||
|   | ||||
							
								
								
									
										15
									
								
								cfg/checks/python.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/python.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # Variables for Python scripting support | ||||
| PYTHON3_LIBS = python3 | ||||
| PYTHON_CFLAGS = -DPYTHON | ||||
| PYTHON_OBJ = api.o python_api.o | ||||
|  | ||||
| # Check if we can build Python scripting support | ||||
| CHECK_PYTHON3_LIBS = $(shell $(PKG_CONFIG) --exists $(PYTHON3_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_PYTHON3_LIBS), error) | ||||
|     LDFLAGS += $(shell python3-config --ldflags) | ||||
|     CFLAGS += $(PYTHON_CFLAGS) $(shell python3-config --includes) | ||||
|     OBJ += $(PYTHON_OBJ) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     $(warning WARNING -- Toxic will be compiled without Python scripting support) | ||||
|     $(warning WARNING -- You need python3 installed for Python scripting support) | ||||
| endif | ||||
							
								
								
									
										15
									
								
								cfg/checks/qr.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/qr.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # Variables for QR export support | ||||
| QR_LIBS = libqrencode | ||||
| QR_CFLAGS = -DQRCODE | ||||
|  | ||||
| # Check if we can build QR export support | ||||
| CHECK_QR_LIBS = $(shell pkg-config --exists $(QR_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_QR_LIBS), error) | ||||
|     LIBS += $(QR_LIBS) | ||||
|     CFLAGS += $(QR_CFLAGS) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_QR_LIBS = $(shell for lib in $(QR_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without QR export support) | ||||
|     $(warning WARNING -- You need these libraries for QR export support) | ||||
|     $(warning WARNING -- $(MISSING_QR_LIBS)) | ||||
| endif | ||||
| @@ -1,5 +1,5 @@ | ||||
| # Variables for video call support | ||||
| VIDEO_LIBS = libtoxav vpx x11 | ||||
| VIDEO_LIBS = openal vpx x11 | ||||
| VIDEO_CFLAGS = -DVIDEO | ||||
| ifneq (, $(findstring video_device.o, $(OBJ))) | ||||
|     VIDEO_OBJ = video_call.o | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # Variables for X11 support | ||||
| X11_LIBS = x11 | ||||
| X11_CFLAGS = -DX11 | ||||
| X11_OBJ = xtra.o | ||||
| X11_OBJ = x11focus.o | ||||
|  | ||||
| # Check if we can build X11 support | ||||
| CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error") | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| # Version | ||||
| TOXIC_VERSION = 0.7.1 | ||||
| TOXIC_VERSION = 0.10.0 | ||||
| REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | ||||
| ifneq (, $(findstring error, $(REV))) | ||||
|     VERSION = $(TOXIC_VERSION) | ||||
| @@ -23,10 +23,10 @@ SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav | ||||
| SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav | ||||
|  | ||||
| # Install directories | ||||
| PREFIX = /usr/local | ||||
| PREFIX ?= /usr/local | ||||
| BINDIR = $(PREFIX)/bin | ||||
| DATADIR = $(PREFIX)/share/toxic | ||||
| MANDIR = $(PREFIX)/share/man | ||||
| MANDIR ?= $(PREFIX)/share/man | ||||
| APPDIR = $(PREFIX)/share/applications | ||||
|  | ||||
| # Platform tools | ||||
|   | ||||
| @@ -6,7 +6,7 @@ PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/o | ||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | ||||
|  | ||||
| # OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw. | ||||
| LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g | ||||
| LDFLAGS += -lncurses -lalut -ltoxcore -lcurl -lconfig -lqrencode -lpng -lopenal -g | ||||
| CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g | ||||
|  | ||||
| OSX_LIBRARIES = -lobjc -lresolv | ||||
|   | ||||
| @@ -1,4 +0,0 @@ | ||||
| # Specials options for freebsd systems | ||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | ||||
| LDFLAGS += -lncursesw -lcurl | ||||
| MANDIR = $(PREFIX)/man | ||||
| @@ -1,4 +0,0 @@ | ||||
| # Specials options for linux systems | ||||
| CFLAGS += | ||||
| LDFLAGS += -ldl -lrt -lcurl | ||||
| MANDIR = $(PREFIX)/share/man | ||||
| @@ -14,10 +14,24 @@ help: | ||||
| 	@echo "  DISABLE_AV:             Set to \"1\" to force building without audio call support" | ||||
| 	@echo "  DISABLE_SOUND_NOTIFY:   Set to \"1\" to force building without sound notification support" | ||||
| 	@echo "  DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" | ||||
| 	@echo "  DISABLE_QRCODE:         Set to \"1\" to force building without QR export support" | ||||
| 	@echo "  DISABLE_QRPNG:          Set to \"1\" to force building without QR exported as PNG support" | ||||
| 	@echo "  ENABLE_PYTHON:          Set to \"1\" to enable building with Python scripting support" | ||||
| 	@echo "  ENABLE_RELEASE:         Set to \"1\" to build without debug symbols and with full compiler optimizations" | ||||
| 	@echo "  ENABLE_ASAN:            Set to \"1\" to build with LLVM address sanitizer enabled. | ||||
| 	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS" | ||||
| 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | ||||
| 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | ||||
| 	@echo "  DESTDIR:                Specify a directory where to store installed files (mainly for packaging purpose)" | ||||
| 	@echo "  MANDIR:                 Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")" | ||||
| 	@echo | ||||
| 	@echo "-- Environment Variables --" | ||||
| 	@echo "  CFLAGS:                 Add custom flags to default CFLAGS" | ||||
| 	@echo "  LDFLAGS:                Add custom flags to default LDFLAGS" | ||||
| 	@echo "  USER_CFLAGS:            Add custom flags to default CFLAGS" | ||||
| 	@echo "  USER_LDFLAGS:           Add custom flags to default LDFLAGS" | ||||
| 	@echo "  PREFIX:                 Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" | ||||
| 	@echo "  DESTDIR:                Specify a directory where to store installed files (mainly for packaging purpose)" | ||||
| 	@echo "  MANDIR:                 Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")" | ||||
|  | ||||
| .PHONY: help | ||||
|   | ||||
| @@ -27,7 +27,7 @@ install: $(BUILD_DIR)/toxic | ||||
| 		if [ ! -e "$(DOC_DIR)/$$f" ]; then \ | ||||
| 			continue ;\ | ||||
| 		fi ;\ | ||||
| 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\ | ||||
| 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\ | ||||
| 		file=$$section/$$f ;\ | ||||
| 		mkdir -p $$section ;\ | ||||
| 		install -m 0644 $(DOC_DIR)/$$f $$file ;\ | ||||
| @@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic | ||||
| 		mv temp_file $$file ;\ | ||||
| 		sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ | ||||
| 		mv temp_file $$file ;\ | ||||
| 		gzip -f -9 $$file ;\ | ||||
| 		gzip -f -n -9 $$file ;\ | ||||
| 	done | ||||
|  | ||||
| .PHONY: install | ||||
|   | ||||
| @@ -16,7 +16,7 @@ uninstall: | ||||
| 	 | ||||
| 	@echo "Removing man pages" | ||||
| 	@for f in $(MANFILES) ; do \ | ||||
| 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\ | ||||
| 		section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\ | ||||
| 		file=$$section/$$f ;\ | ||||
| 		rm -f $$file $$file.gz ;\ | ||||
| 	done | ||||
|   | ||||
							
								
								
									
										11
									
								
								doc/toxic.1
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								doc/toxic.1
									
									
									
									
									
								
							| @@ -1,13 +1,13 @@ | ||||
| '\" t | ||||
| .\"     Title: toxic | ||||
| .\"    Author: [see the "AUTHORS" section] | ||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2016-09-20 | ||||
| .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2020-05-04 | ||||
| .\"    Manual: Toxic Manual | ||||
| .\"    Source: toxic __VERSION__ | ||||
| .\"  Language: English | ||||
| .\" | ||||
| .TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual" | ||||
| .TH "TOXIC" "1" "2020\-05\-04" "toxic __VERSION__" "Toxic Manual" | ||||
| .\" ----------------------------------------------------------------- | ||||
| .\" * Define some portability stuff | ||||
| .\" ----------------------------------------------------------------- | ||||
| @@ -78,6 +78,11 @@ instead of | ||||
| Show help message | ||||
| .RE | ||||
| .PP | ||||
| \-l, \-\-logging | ||||
| .RS 4 | ||||
| Enable toxcore logging to stderr | ||||
| .RE | ||||
| .PP | ||||
| \-n, \-\-nodes nodes\-file | ||||
| .RS 4 | ||||
| Use specified | ||||
|   | ||||
| @@ -40,6 +40,9 @@ OPTIONS | ||||
| -h, --help:: | ||||
|     Show help message | ||||
|  | ||||
| -l, --logging:: | ||||
|     Enable toxcore logging to stderr | ||||
|  | ||||
| -n, --nodes nodes-file:: | ||||
|     Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json' | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| '\" t | ||||
| .\"     Title: toxic.conf | ||||
| .\"    Author: [see the "AUTHORS" section] | ||||
| .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2016-07-21 | ||||
| .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2020-11-18 | ||||
| .\"    Manual: Toxic Manual | ||||
| .\"    Source: toxic __VERSION__ | ||||
| .\"  Language: English | ||||
| .\" | ||||
| .TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual" | ||||
| .TH "TOXIC\&.CONF" "5" "2020\-11\-18" "toxic __VERSION__" "Toxic Manual" | ||||
| .\" ----------------------------------------------------------------- | ||||
| .\" * Define some portability stuff | ||||
| .\" ----------------------------------------------------------------- | ||||
| @@ -93,6 +93,26 @@ Enable or disable acoustic alerts on events\&. true or false | ||||
| Select between native terminal colors and toxic color theme\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBcolor_bar_bg\fR | ||||
| .RS 4 | ||||
| set background color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
| .RE | ||||
| .PP | ||||
| \fBcolor_bar_fg\fR | ||||
| .RS 4 | ||||
| set foreground (text) color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
| .RE | ||||
| .PP | ||||
| \fBcolor_bar_accent\fR | ||||
| .RS 4 | ||||
| set foreground accent color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
| .RE | ||||
| .PP | ||||
| \fBcolor_bar_notify\fR | ||||
| .RS 4 | ||||
| set foreground notify (and typing) color in chat status bar\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
| .RE | ||||
| .PP | ||||
| \fBautolog\fR | ||||
| .RS 4 | ||||
| Enable or disable autologging\&. true or false | ||||
| @@ -120,7 +140,12 @@ Enable friend connection change notifications\&. true or false | ||||
| .PP | ||||
| \fBnodelist_update_freq\fR | ||||
| .RS 4 | ||||
| How often in days to update the DHT nodes list\&. (0 to disable updates) | ||||
| 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 | ||||
| @@ -128,6 +153,11 @@ How often in days to update the DHT nodes list\&. (0 to disable updates) | ||||
| Maximum lines for chat window history\&. Integer value\&. (for example: 700) | ||||
| .RE | ||||
| .PP | ||||
| \fBnotification_timeout\fR | ||||
| .RS 4 | ||||
| Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000) | ||||
| .RE | ||||
| .PP | ||||
| \fBline_join\fR | ||||
| .RS 4 | ||||
| Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&. | ||||
| @@ -207,9 +237,24 @@ Audio output device\&. Integer value\&. Number corresponds to | ||||
| /lsdev out | ||||
| .RE | ||||
| .PP | ||||
| \fBVAD_treshold\fR | ||||
| \fBVAD_threshold\fR | ||||
| .RS 4 | ||||
| Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0 | ||||
| Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0 | ||||
| .RE | ||||
| .PP | ||||
| \fBconference_audio_channels\fR | ||||
| .RS 4 | ||||
| Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) | ||||
| .RE | ||||
| .PP | ||||
| \fBchat_audio_channels\fR | ||||
| .RS 4 | ||||
| Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) | ||||
| .RE | ||||
| .PP | ||||
| \fBpush_to_talk\fR | ||||
| .RS 4 | ||||
| Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false | ||||
| .RE | ||||
| .RE | ||||
| .PP | ||||
| @@ -227,6 +272,11 @@ Default path for downloads\&. String value\&. Absolute path for downloaded files | ||||
| Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB) | ||||
| .RE | ||||
| .PP | ||||
| \fBautorun_path\fR | ||||
| .RS 4 | ||||
| Path for any scripts that should be run on startup | ||||
| .RE | ||||
| .PP | ||||
| \fBchatlogs_path\fR | ||||
| .RS 4 | ||||
| Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. | ||||
| @@ -336,16 +386,6 @@ Key combination to scroll half page down\&. | ||||
| Key combination to scroll to page bottom\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBpeer_list_up\fR | ||||
| .RS 4 | ||||
| Key combination to scroll contacts list up\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBpeer_list_down\fR | ||||
| .RS 4 | ||||
| Key combination to scroll contacts list down\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBtoggle_peerlist\fR | ||||
| .RS 4 | ||||
| Toggle the peer list on and off\&. | ||||
|   | ||||
| @@ -60,6 +60,18 @@ OPTIONS | ||||
|     *native_colors*;; | ||||
|         Select between native terminal colors and toxic color theme. true or false | ||||
|  | ||||
|     *color_bar_bg*;; | ||||
|         set background color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|  | ||||
|     *color_bar_fg*;; | ||||
|         set foreground (text) color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|  | ||||
|     *color_bar_accent*;; | ||||
|         set foreground accent color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|  | ||||
|     *color_bar_notify*;; | ||||
|         set foreground notify (and typing) color in chat status bar. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|  | ||||
|     *autolog*;; | ||||
|         Enable or disable autologging. true or false | ||||
|  | ||||
| @@ -76,11 +88,17 @@ OPTIONS | ||||
|         Enable friend connection change notifications. true or false | ||||
|  | ||||
|     *nodelist_update_freq*;; | ||||
|         How often in days to update the DHT nodes list. (0 to disable updates) | ||||
|         How often in days to update the DHT nodes list. (integer; 0 to disable) | ||||
|  | ||||
|     *autosave_freq*;; | ||||
|         How often in seconds to auto-save the Tox data file. (integer; 0 to disable) | ||||
|  | ||||
|     *history_size*;; | ||||
|         Maximum lines for chat window history. Integer value. (for example: 700) | ||||
|  | ||||
|     *notification_timeout*;; | ||||
|         Time in milliseconds to display a notification. Integer value. (for example: 3000) | ||||
|  | ||||
|     *line_join*;; | ||||
|         Indicator for when someone connects or joins a group. | ||||
|         Three characters max for line_ settings. | ||||
| @@ -129,9 +147,18 @@ OPTIONS | ||||
|     *output_device*;; | ||||
|         Audio output device. Integer value. Number corresponds to `/lsdev out` | ||||
|  | ||||
|     *VAD_treshold*;; | ||||
|         Voice Activity Detection treshold.  Float value. Recommended values are | ||||
|         around 40.0 | ||||
|     *VAD_threshold*;; | ||||
|         Voice Activity Detection threshold.  Float value. Recommended values are | ||||
|         1.0-40.0 | ||||
|  | ||||
|     *conference_audio_channels*;; | ||||
|         Number of channels for conference audio broadcast. Integer value. 1 (mono) or 2 (stereo) | ||||
|  | ||||
|     *chat_audio_channels*;; | ||||
|         Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo) | ||||
|  | ||||
|     *push_to_talk*;; | ||||
|         Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false | ||||
|  | ||||
| *tox*:: | ||||
|     Configuration related to paths. | ||||
| @@ -143,6 +170,9 @@ OPTIONS | ||||
|     *avatar_path*;; | ||||
|         Path for your avatar (file must be a .png and cannot exceed 16.3 KiB) | ||||
|  | ||||
|     *autorun_path*;; | ||||
|         Path for any scripts that should be run on startup | ||||
|  | ||||
|     *chatlogs_path*;; | ||||
|         Default path for chatlogs. String value. Absolute path for chatlog files. | ||||
|  | ||||
| @@ -212,12 +242,6 @@ OPTIONS | ||||
|     *page_bottom*;; | ||||
|         Key combination to scroll to page bottom. | ||||
|  | ||||
|     *peer_list_up*;; | ||||
|         Key combination to scroll contacts list up. | ||||
|  | ||||
|     *peer_list_down*;; | ||||
|         Key combination to scroll contacts list down. | ||||
|  | ||||
|     *toggle_peerlist*;; | ||||
|         Toggle the peer list on and off. | ||||
|  | ||||
|   | ||||
| @@ -9,20 +9,32 @@ ui = { | ||||
|   alerts=true; | ||||
|  | ||||
|   // Output a bell when receiving a message (see manpage) | ||||
|   bell_on_message=true | ||||
|   bell_on_message=true; | ||||
|  | ||||
|   // Output a bell when receiving a filetransfer (see manpage) | ||||
|   bell_on_filetrans=true | ||||
|   bell_on_filetrans=true; | ||||
|  | ||||
|   // Don't output a bell when a filetransfer was accepted (see manpage) | ||||
|   bell_on_filetrans_accept=false | ||||
|   bell_on_filetrans_accept=false; | ||||
|  | ||||
|   // Output a bell when receiving a group/call invite (see manpage) | ||||
|   bell_on_invite=true | ||||
|   bell_on_invite=true; | ||||
|  | ||||
|   // true to use native terminal colours, false to use toxic default colour theme | ||||
|   native_colors=false; | ||||
|  | ||||
|   // set background color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|   color_bar_bg="blue"; | ||||
|  | ||||
|   // set foreground (text) color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|   color_bar_fg="white"; | ||||
|  | ||||
|   // set foreground accent color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|   color_bar_accent="cyan"; | ||||
|  | ||||
|   // set foreground notify (and typing) color in chat status bar (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|   color_bar_notify="yellow"; | ||||
|  | ||||
|   // true to enable autologging, false to disable | ||||
|   autolog=false; | ||||
|  | ||||
| @@ -30,7 +42,7 @@ ui = { | ||||
|   time_format=24; | ||||
|  | ||||
|   // 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 | ||||
|   show_typing_other=true; | ||||
| @@ -47,20 +59,26 @@ ui = { | ||||
|   // How often in days to update the DHT nodes list. (0 to disable updates) | ||||
|   nodeslist_update_freq=7; | ||||
|  | ||||
|   // How often in seconds to auto-save the Tox data file. (0 to disable periodic auto-saves) | ||||
|   autosave_freq=600; | ||||
|  | ||||
|   // maximum lines for chat window history | ||||
|   history_size=700; | ||||
|  | ||||
|   // Indicator for display when someone connects or joins a group. | ||||
|   // time in milliseconds to display a notification | ||||
|   notification_timeout=6000; | ||||
|  | ||||
|   // Indicator for display when someone connects or joins a group | ||||
|   line_join="-->"; | ||||
|  | ||||
|   // Indicator for display when someone disconnects or leaves a group. | ||||
|   // Indicator for display when someone disconnects or leaves a group | ||||
|   line_quit="<--"; | ||||
|  | ||||
|   // Indicator for alert messages. | ||||
|   line_alert="-!-"; | ||||
|  | ||||
|   // Indicator for normal messages. | ||||
|   line_normal="---"; | ||||
|   line_normal="-"; | ||||
|  | ||||
|   // true to change status based on screen/tmux attach/detach, false to disable | ||||
|   mplex_away=true; | ||||
| @@ -76,8 +94,17 @@ audio = { | ||||
|   // preferred audio output device; numbers correspond to /lsdev out | ||||
|   output_device=0; | ||||
|  | ||||
|   // default VAD treshold; float (recommended values are around 40) | ||||
|   VAD_treshold=40.0; | ||||
|   // default VAD threshold; float (recommended values are 1.0-40.0) | ||||
|   VAD_threshold=5.0; | ||||
|  | ||||
|   // Number of channels to use for conference audio broadcasts; 1 for mono, 2 for stereo. | ||||
|   conference_audio_channels=1; | ||||
|  | ||||
|   // Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo. | ||||
|   chat_audio_channels=2; | ||||
|  | ||||
|   // toggle conference push-to-talk | ||||
|   push_to_talk=false; | ||||
| }; | ||||
|  | ||||
| tox = { | ||||
| @@ -87,6 +114,9 @@ tox = { | ||||
|   // Path for your avatar (file must be a .png and cannot exceed 64 KiB) | ||||
|   // avatar_path="/home/USERNAME/Pictures/youravatar.png"; | ||||
|  | ||||
|   // Path for scripts that should be run on startup | ||||
|   // autorun_path="/home/USERNAME/toxic_scripts/"; | ||||
|  | ||||
|   // Path for chatlogs | ||||
|   // chatlogs_path="/home/USERNAME/toxic_chatlogs/"; | ||||
| }; | ||||
| @@ -113,9 +143,6 @@ keys = { | ||||
|   half_page_up="Ctrl+F"; | ||||
|   half_page_down="Ctrl+V"; | ||||
|   page_bottom="Ctrl+H"; | ||||
|   peer_list_up="Ctrl+["; | ||||
|   peer_list_down="Ctrl+]"; | ||||
|   toggle_peerlist="Ctrl+b"; | ||||
|   toggle_peerlist="Ctrl+B"; | ||||
|   toggle_paste_mode="Ctrl+T"; | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										217
									
								
								src/api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/api.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| /*  api.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net> | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <dirent.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| #include <tox/tox.h> | ||||
|  | ||||
| #include "execute.h" | ||||
| #include "friendlist.h" | ||||
| #include "line_info.h" | ||||
| #include "message_queue.h" | ||||
| #include "misc_tools.h" | ||||
| #include "settings.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #include "python_api.h" | ||||
|  | ||||
| Tox              *user_tox; | ||||
| static WINDOW    *cur_window; | ||||
| static ToxWindow *self_window; | ||||
|  | ||||
| extern FriendsList Friends; | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| void api_display(const char *const msg) | ||||
| { | ||||
|     if (msg == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     self_window = get_active_window(); | ||||
|     line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
| } | ||||
|  | ||||
| FriendsList api_get_friendslist(void) | ||||
| { | ||||
|     return Friends; | ||||
| } | ||||
|  | ||||
| char *api_get_nick(void) | ||||
| { | ||||
|     size_t   len  = tox_self_get_name_size(user_tox); | ||||
|     uint8_t *name = malloc(len + 1); | ||||
|  | ||||
|     if (name == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     tox_self_get_name(user_tox, name); | ||||
|     name[len] = '\0'; | ||||
|     return (char *) name; | ||||
| } | ||||
|  | ||||
| Tox_User_Status api_get_status(void) | ||||
| { | ||||
|     return tox_self_get_status(user_tox); | ||||
| } | ||||
|  | ||||
| char *api_get_status_message(void) | ||||
| { | ||||
|     size_t   len    = tox_self_get_status_message_size(user_tox); | ||||
|     uint8_t *status = malloc(len + 1); | ||||
|  | ||||
|     if (status == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     tox_self_get_status_message(user_tox, status); | ||||
|     status[len] = '\0'; | ||||
|     return (char *) status; | ||||
| } | ||||
|  | ||||
| void api_send(const char *msg) | ||||
| { | ||||
|     if (msg == NULL || self_window->chatwin->cqueue == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *name = api_get_nick(); | ||||
|  | ||||
|     if (name == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     self_window = get_active_window(); | ||||
|  | ||||
|     strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line)); | ||||
|     add_line_to_hist(self_window->chatwin); | ||||
|     int id = line_info_add(self_window, true, name, NULL, OUT_MSG, 0, 0, "%s", msg); | ||||
|     cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id); | ||||
|     free(name); | ||||
| } | ||||
|  | ||||
| void api_execute(const char *input, int mode) | ||||
| { | ||||
|     self_window = get_active_window(); | ||||
|     execute(cur_window, self_window, user_tox, input, mode); | ||||
| } | ||||
|  | ||||
| int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     return do_python_command(num_args, args); | ||||
| } | ||||
|  | ||||
| int num_registered_handlers(void) | ||||
| { | ||||
|     return python_num_registered_handlers(); | ||||
| } | ||||
|  | ||||
| int help_max_width(void) | ||||
| { | ||||
|     return python_help_max_width(); | ||||
| } | ||||
|  | ||||
| void draw_handler_help(WINDOW *win) | ||||
| { | ||||
|     python_draw_handler_help(win); | ||||
| } | ||||
|  | ||||
| void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     FILE       *fp; | ||||
|     const char *error_str; | ||||
|  | ||||
|     cur_window  = window; | ||||
|     self_window = self; | ||||
|  | ||||
|     if (argc != 1) { | ||||
|         if (argc < 1) { | ||||
|             error_str = "Path must be specified."; | ||||
|         } else { | ||||
|             error_str = "Only one argument allowed."; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     fp = fopen(argv[1], "r"); | ||||
|  | ||||
|     if (fp == NULL) { | ||||
|         error_str = "Path does not exist."; | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     run_python(fp, argv[1]); | ||||
|     fclose(fp); | ||||
| } | ||||
|  | ||||
| void invoke_autoruns(WINDOW *window, ToxWindow *self) | ||||
| { | ||||
|     char abspath_buf[PATH_MAX + 256]; | ||||
|     char err_buf[PATH_MAX + 128]; | ||||
|  | ||||
|     if (user_settings->autorun_path[0] == '\0') { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     DIR *d = opendir(user_settings->autorun_path); | ||||
|  | ||||
|     if (d == NULL) { | ||||
|         snprintf(err_buf, sizeof(err_buf), "Autorun path does not exist: %s", user_settings->autorun_path); | ||||
|         api_display(err_buf); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     struct dirent *dir = NULL; | ||||
|  | ||||
|     cur_window  = window; | ||||
|  | ||||
|     self_window = self; | ||||
|  | ||||
|     while ((dir = readdir(d)) != NULL) { | ||||
|         size_t path_len = strlen(dir->d_name); | ||||
|  | ||||
|         if (!strcmp(dir->d_name + path_len - 3, ".py")) { | ||||
|             snprintf(abspath_buf, sizeof(abspath_buf), "%s%s", user_settings->autorun_path, dir->d_name); | ||||
|             FILE *fp = fopen(abspath_buf, "r"); | ||||
|  | ||||
|             if (fp == NULL) { | ||||
|                 snprintf(err_buf, sizeof(err_buf), "Invalid path: %s", abspath_buf); | ||||
|                 api_display(err_buf); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             run_python(fp, abspath_buf); | ||||
|             fclose(fp); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     closedir(d); | ||||
| } | ||||
| #endif /* PYTHON */ | ||||
							
								
								
									
										43
									
								
								src/api.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/api.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /*  api.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net> | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef API_H | ||||
| #define API_H | ||||
|  | ||||
| #include "friendlist.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| void api_display(const char *const msg); | ||||
| FriendsList api_get_friendslist(void); | ||||
| char *api_get_nick(void); | ||||
| Tox_User_Status api_get_status(void); | ||||
| char *api_get_status_message(void); | ||||
| void api_send(const char *msg); | ||||
| void api_execute(const char *input, int mode); | ||||
| int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]); | ||||
| int num_registered_handlers(void); | ||||
| int help_max_width(void); | ||||
| void draw_handler_help(WINDOW *win); | ||||
| void invoke_autoruns(WINDOW *w, ToxWindow *self); | ||||
| void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| #endif /* API_H */ | ||||
							
								
								
									
										1064
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										1064
									
								
								src/audio_call.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -27,9 +27,7 @@ | ||||
|  | ||||
| #include "audio_device.h" | ||||
|  | ||||
| #define MAX_CALLS 10 | ||||
|  | ||||
| typedef enum _AudioError { | ||||
| typedef enum AudioError { | ||||
|     ae_None = 0, | ||||
|     ae_StartingCaptureDevice = 1 << 0, | ||||
|     ae_StartingOutputDevice = 1 << 1, | ||||
| @@ -37,7 +35,7 @@ typedef enum _AudioError { | ||||
| } AudioError; | ||||
|  | ||||
| #ifdef VIDEO | ||||
| typedef enum _VideoError { | ||||
| typedef enum VideoError { | ||||
|     ve_None = 0, | ||||
|     ve_StartingCaptureDevice = 1 << 0, | ||||
|     ve_StartingOutputDevice = 1 << 1, | ||||
| @@ -46,14 +44,27 @@ typedef enum _VideoError { | ||||
|  | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| /* Status transitions: | ||||
|  * None -> Pending (call invitation made or received); | ||||
|  * Pending -> None (invitation rejected or failed); | ||||
|  * Pending -> Active (call starts); | ||||
|  * Active -> None (call ends). | ||||
|  */ | ||||
| typedef enum CallStatus { | ||||
|     cs_None = 0, | ||||
|     cs_Pending, | ||||
|     cs_Active | ||||
| } CallStatus; | ||||
|  | ||||
| typedef struct Call { | ||||
|     pthread_t ttid; /* Transmission thread id */ | ||||
|     bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */ | ||||
|     uint32_t in_idx, out_idx; /* Audio Index */ | ||||
| #ifdef VIDEO | ||||
|     uint32_t vin_idx, vout_idx; /* Video Index */ | ||||
| #endif /* VIDEO */ | ||||
|     pthread_mutex_t mutex; | ||||
|     CallStatus status; | ||||
|     uint32_t state; /* ToxAV call state, valid when `status == cs_Active` */ | ||||
|     uint32_t in_idx, out_idx; /* Audio device index, or -1 if not open */ | ||||
|     uint32_t audio_bit_rate; /* Bit rate for sending audio */ | ||||
|  | ||||
|     uint32_t vin_idx, vout_idx; /* Video device index, or -1 if not open */ | ||||
|     uint32_t video_width, video_height; | ||||
|     uint32_t video_bit_rate; /* Bit rate for sending video; 0 for no video */ | ||||
| } Call; | ||||
|  | ||||
| struct CallControl { | ||||
| @@ -65,29 +76,34 @@ struct CallControl { | ||||
|     ToxAV *av; | ||||
|     ToxWindow *prompt; | ||||
|  | ||||
|     Call calls[MAX_CALLS]; | ||||
|     uint32_t call_state; | ||||
|     bool pending_call; | ||||
|     Call *calls; | ||||
|     uint32_t max_calls; | ||||
|  | ||||
|     bool audio_enabled; | ||||
|     bool video_enabled; | ||||
|  | ||||
|     uint32_t audio_bit_rate; | ||||
|     int32_t audio_frame_duration; | ||||
|     uint32_t audio_sample_rate; | ||||
|     uint8_t audio_channels; | ||||
|     uint32_t default_audio_bit_rate; | ||||
|  | ||||
|     uint32_t video_bit_rate; | ||||
|     int32_t video_frame_duration; | ||||
|     uint32_t default_video_width, default_video_height; | ||||
|     uint32_t default_video_bit_rate; | ||||
| }; | ||||
|  | ||||
| } CallControl; | ||||
|  | ||||
| struct CallControl CallControl; | ||||
| extern struct CallControl CallControl; | ||||
|  | ||||
| /* You will have to pass pointer to first member of 'windows' declared in windows.c */ | ||||
| ToxAV *init_audio(ToxWindow *self, Tox *tox); | ||||
| void terminate_audio(); | ||||
| int start_transmission(ToxWindow *self, Call *call); | ||||
| int stop_transmission(Call *call, uint32_t friend_number); | ||||
| void terminate_audio(void); | ||||
|  | ||||
| bool init_call(Call *call); | ||||
|  | ||||
| void place_call(ToxWindow *self); | ||||
| void stop_current_call(ToxWindow *self); | ||||
|  | ||||
| void init_friend_AV(uint32_t index); | ||||
| void del_friend_AV(uint32_t index); | ||||
|  | ||||
| #endif /* AUDIO_CALL_H */ | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -30,8 +30,9 @@ | ||||
| #define AUDIO_DEVICE_H | ||||
|  | ||||
| #define OPENAL_BUFS 5 | ||||
| #define MAX_OPENAL_DEVICES 32 | ||||
| #define MAX_DEVICES 32 | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #include "windows.h" | ||||
|  | ||||
| typedef enum DeviceType { | ||||
| @@ -52,44 +53,45 @@ typedef enum DeviceError { | ||||
|     de_AlError = -9, | ||||
| } DeviceError; | ||||
|  | ||||
| typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data); | ||||
| typedef void (*DataHandleCallback)(const int16_t *, uint32_t size, void *data); | ||||
|  | ||||
|  | ||||
| #ifdef AUDIO | ||||
| DeviceError init_devices(ToxAV *av); | ||||
| #else | ||||
| DeviceError init_devices(); | ||||
| #endif /* AUDIO */ | ||||
| DeviceError init_devices(void); | ||||
|  | ||||
| DeviceError terminate_devices(); | ||||
|  | ||||
| /* Callback handles ready data from INPUT device */ | ||||
| DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, | ||||
|                                      void *data, bool enable_VAD); | ||||
| void *get_device_callback_data(uint32_t device_idx); | ||||
| void get_al_device_names(void); | ||||
| DeviceError terminate_devices(void); | ||||
|  | ||||
| /* toggle device mute */ | ||||
| DeviceError device_mute(DeviceType type, uint32_t device_idx); | ||||
|  | ||||
| #ifdef AUDIO | ||||
| DeviceError device_set_VAD_treshold(uint32_t device_idx, float value); | ||||
| #endif | ||||
| bool device_is_muted(DeviceType type, uint32_t device_idx); | ||||
|  | ||||
| DeviceError device_set_VAD_threshold(uint32_t device_idx, float value); | ||||
|  | ||||
| float device_get_VAD_threshold(uint32_t device_idx); | ||||
|  | ||||
| DeviceError set_source_position(uint32_t device_idx, float x, float y, float z); | ||||
|  | ||||
| DeviceError set_al_device(DeviceType type, int32_t selection); | ||||
|  | ||||
| DeviceError set_primary_device(DeviceType type, int32_t selection); | ||||
| DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration, | ||||
|                                 uint8_t channels); | ||||
| /* Start device */ | ||||
| DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate, | ||||
|                         uint32_t frame_duration, uint8_t channels); | ||||
| DeviceError open_input_device(uint32_t *device_idx, | ||||
|                               DataHandleCallback cb, void *cb_data, bool enable_VAD, | ||||
|                               uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); | ||||
| DeviceError open_output_device(uint32_t *device_idx, | ||||
|                                uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); | ||||
|  | ||||
| /* Stop device */ | ||||
| DeviceError close_device(DeviceType type, uint32_t device_idx); | ||||
|  | ||||
| /* Write data to device */ | ||||
| /* Write data to output device */ | ||||
| DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels, | ||||
|                       uint32_t sample_rate); | ||||
|  | ||||
| void print_devices(ToxWindow *self, DeviceType type); | ||||
| void get_primary_device_name(DeviceType type, char *buf, int size); | ||||
| /* return current input volume as float in range 0.0-100.0 */ | ||||
| float get_input_volume(void); | ||||
|  | ||||
| void print_al_devices(ToxWindow *self, DeviceType type); | ||||
|  | ||||
| DeviceError selection_valid(DeviceType type, int32_t selection); | ||||
| #endif /* AUDIO_DEVICE_H */ | ||||
|   | ||||
| @@ -20,36 +20,35 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <limits.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <limits.h> | ||||
|  | ||||
| #ifdef __APPLE__ | ||||
| #include <sys/types.h> | ||||
| #include <sys/dir.h> | ||||
| #else | ||||
| #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 "execute.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size) | ||||
| static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matches) | ||||
| { | ||||
|     if (m) | ||||
|     if (m) { | ||||
|         execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE); | ||||
|     } | ||||
|  | ||||
|     const char *L = (char *) list; | ||||
|     int i; | ||||
|     for (size_t i = 0; i < n_matches; ++i) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", list[i]); | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < n_items; ++i) | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]); | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");   /* formatting */ | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
| } | ||||
|  | ||||
| /* puts match in match buffer. if more than one match, add first n chars that are identical. | ||||
| @@ -57,19 +56,19 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items | ||||
|  * | ||||
|  * Returns the length of the match. | ||||
|  */ | ||||
| static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n) | ||||
| 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); | ||||
|  | ||||
|     if (n_items == 1) { | ||||
|         return snprintf(match, match_sz, "%s", matches[0]); | ||||
|     } | ||||
|  | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < MAX_STR_SIZE; ++i) { | ||||
|     for (size_t i = 0; i < max_size; ++i) { | ||||
|         char ch1 = matches[0][i]; | ||||
|         int j; | ||||
|  | ||||
|         for (j = 0; j < n; ++j) { | ||||
|         for (size_t j = 0; j < n_items; ++j) { | ||||
|             char ch2 = matches[j][i]; | ||||
|  | ||||
|             if (ch1 != ch2 || !ch1) { | ||||
| @@ -83,49 +82,58 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char | ||||
|     return snprintf(match, match_sz, "%s", matches[0]); | ||||
| } | ||||
|  | ||||
| /* looks for all instances in list that begin with the last entered word in line according to pos, | ||||
|    then fills line with the complete word. e.g. "Hello jo" would complete the line | ||||
|    with "Hello john". If multiple matches, prints out all the matches and semi-completes line. | ||||
|  | ||||
|    list is a pointer to the list of strings being compared, n_items is the number of items | ||||
|    in the list, and size is the size of each item in the list. | ||||
|  | ||||
|    Returns the difference between the old len and new len of line on success, -1 if error */ | ||||
| int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
| /* | ||||
|  * Looks for all instances in list that begin with the last entered word in line according to pos, | ||||
|  * then fills line with the complete word. e.g. "Hello jo" would complete the line | ||||
|  * with "Hello john". If multiple matches, prints out all the matches and semi-completes line. | ||||
|  * | ||||
|  * `list` is a pointer to `n_items` strings. Each string in the list must be <= MAX_STR_SIZE. | ||||
|  * | ||||
|  * dir_search should be true if the line being completed is a file path. | ||||
|  * | ||||
|  * Returns the difference between the old len and new len of line on success. | ||||
|  * Returns -1 on error. | ||||
|  * | ||||
|  * Note: This function should not be called directly. Use complete_line() and complete_path() instead. | ||||
|  */ | ||||
| static int complete_line_helper(ToxWindow *self, const char **list, const size_t n_items, bool dir_search) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) | ||||
|     if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (ctx->len >= MAX_STR_SIZE) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     const char *L = (char *) list; | ||||
|     const char *endchrs = " "; | ||||
|     char ubuf[MAX_STR_SIZE]; | ||||
|  | ||||
|     /* work with multibyte string copy of buf for simplicity */ | ||||
|     if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) | ||||
|     if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) { | ||||
|         return -1; | ||||
|  | ||||
|     /* TODO: generalize this */ | ||||
|     bool dir_search =    !strncmp(ubuf, "/sendfile", strlen("/sendfile")) | ||||
|                          || !strncmp(ubuf, "/avatar", strlen("/avatar")); | ||||
|     } | ||||
|  | ||||
|     /* isolate substring from space behind pos to pos */ | ||||
|     char tmp[MAX_STR_SIZE]; | ||||
|     snprintf(tmp, sizeof(tmp), "%s", ubuf); | ||||
|     tmp[ctx->pos] = '\0'; | ||||
|     memcpy(tmp, ubuf, ctx->pos); | ||||
|     tmp[ctx->pos] = 0; | ||||
|  | ||||
|     const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' '); | ||||
|     const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' '); | ||||
|     char *sub = calloc(1, strlen(ubuf) + 1); | ||||
|  | ||||
|     if (sub == NULL) | ||||
|         exit_toxic_err("failed in complete_line", FATALERR_MEMORY); | ||||
|     if (sub == NULL) { | ||||
|         exit_toxic_err("failed in complete_line_helper", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     if (!s && !dir_search) { | ||||
|         strcpy(sub, tmp); | ||||
|  | ||||
|         if (sub[0] != '/') | ||||
|         if (sub[0] != '/') { | ||||
|             endchrs = ": "; | ||||
|         } | ||||
|     } else if (s) { | ||||
|         strcpy(sub, &s[1]); | ||||
|  | ||||
| @@ -133,50 +141,60 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|             int sub_len = strlen(sub); | ||||
|             int si = char_rfind(sub, '/', sub_len); | ||||
|  | ||||
|             if (si || *sub == '/') | ||||
|             if (si || *sub == '/') { | ||||
|                 memmove(sub, &sub[si + 1], sub_len - si); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (!sub[0]) { | ||||
|     if (!sub[0] && !(dir_search && n_items == 1)) { | ||||
|         free(sub); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     int s_len = strlen(sub); | ||||
|     size_t n_matches = 0; | ||||
|  | ||||
|     char **matches = (char **) malloc_ptr_array(n_items, MAX_STR_SIZE); | ||||
|  | ||||
|     if (matches == NULL) { | ||||
|         free(sub); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int s_len = strlen(sub); | ||||
|     int n_matches = 0; | ||||
|     char matches[n_items][MAX_STR_SIZE]; | ||||
|     int i = 0; | ||||
|  | ||||
|     /* put all list matches in matches array */ | ||||
|     for (i = 0; i < n_items; ++i) { | ||||
|         char str[MAX_CMDNAME_SIZE + 1]; | ||||
|         snprintf(str, sizeof(str), "%s", &L[i * size]); | ||||
|  | ||||
|         if (strncasecmp(str, sub, s_len) == 0) | ||||
|             strcpy(matches[n_matches++], str); | ||||
|     for (size_t i = 0; i < n_items; ++i) { | ||||
|         if (strncasecmp(list[i], sub, s_len) == 0) { | ||||
|             snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     free(sub); | ||||
|  | ||||
|     if (!n_matches) | ||||
|     if (!n_matches) { | ||||
|         free_ptr_array((void **) matches); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (!dir_search && n_matches > 1) | ||||
|         print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); | ||||
|     if (!dir_search && n_matches > 1) { | ||||
|         print_ac_matches(self, NULL, matches, n_matches); | ||||
|     } | ||||
|  | ||||
|     char match[MAX_STR_SIZE]; | ||||
|     size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches); | ||||
|     size_t match_len = get_str_match(self, match, sizeof(match), (const char **) matches, n_matches, MAX_STR_SIZE); | ||||
|  | ||||
|     free_ptr_array((void **) matches); | ||||
|  | ||||
|     if (match_len == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (dir_search) { | ||||
|         if (n_matches == 1) | ||||
|             endchrs = char_rfind(match, '.', match_len) ? "\"" : "/"; | ||||
|         else | ||||
|         if (n_matches == 1) { | ||||
|             endchrs = char_rfind(match, '.', match_len) ? "" : "/"; | ||||
|         } else { | ||||
|             endchrs = ""; | ||||
|         } | ||||
|     } else if (n_matches > 1) { | ||||
|         endchrs = ""; | ||||
|     } | ||||
| @@ -186,8 +204,9 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|     int strt = ctx->pos - s_len; | ||||
|     int diff = match_len - s_len + n_endchrs; | ||||
|  | ||||
|     if (ctx->len + diff >= MAX_STR_SIZE) | ||||
|     if (ctx->len + diff >= MAX_STR_SIZE) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char tmpend[MAX_STR_SIZE]; | ||||
|     snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]); | ||||
| @@ -197,6 +216,31 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|     } | ||||
|  | ||||
|     strcpy(&ubuf[strt], match); | ||||
|  | ||||
|     /* If path points to a file with no extension don't append a forward slash */ | ||||
|     if (dir_search && *endchrs == '/') { | ||||
|         const char *path_start = strchr(ubuf + 1, '/'); | ||||
|  | ||||
|         if (!path_start) { | ||||
|             path_start = strchr(ubuf + 1, ' '); | ||||
|  | ||||
|             if (!path_start) { | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (strlen(path_start) < 2) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         ++path_start; | ||||
|  | ||||
|         if (file_type(path_start) == FILE_TYPE_REGULAR) { | ||||
|             endchrs = ""; | ||||
|             diff -= n_endchrs; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     strcpy(&ubuf[strt + match_len], endchrs); | ||||
|     strcpy(&ubuf[strt + match_len + n_endchrs], tmpend); | ||||
|  | ||||
| @@ -215,54 +259,82 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) | ||||
|     return diff; | ||||
| } | ||||
|  | ||||
| /* transforms a tab complete starting with the shorthand "~" into the full home directory.*/ | ||||
| static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen) | ||||
| int complete_line(ToxWindow *self, const char **list, size_t n_items) | ||||
| { | ||||
|     return complete_line_helper(self, list, n_items, false); | ||||
| } | ||||
|  | ||||
| static int complete_path(ToxWindow *self, const char **list, const size_t n_items) | ||||
| { | ||||
|     return complete_line_helper(self, list, n_items, true); | ||||
| } | ||||
|  | ||||
| /* Transforms a tab complete starting with the shorthand "~" into the full home directory. */ | ||||
| static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     char homedir[MAX_STR_SIZE] = {0}; | ||||
|     get_home_dir(homedir, sizeof(homedir)); | ||||
|  | ||||
|     char newline[MAX_STR_SIZE]; | ||||
|     snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1); | ||||
|     snprintf(path, pathsize, "%s", &newline[cmdlen]); | ||||
|     char newline[MAX_STR_SIZE + 1]; | ||||
|     snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1); | ||||
|     snprintf(path, pathsize, "%s", &newline[cmdlen - 1]); | ||||
|  | ||||
|     wchar_t wline[MAX_STR_SIZE]; | ||||
|  | ||||
|     if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) | ||||
|     if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int newlen = wcslen(wline); | ||||
|  | ||||
|     if (ctx->len + newlen >= MAX_STR_SIZE) | ||||
|     if (ctx->len + newlen >= MAX_STR_SIZE) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     wmemcpy(ctx->line, wline, newlen + 1); | ||||
|     ctx->pos = newlen; | ||||
|     ctx->len = ctx->pos; | ||||
| } | ||||
|  | ||||
| /*  attempts to match /command "<incomplete-dir>" line to matching directories. | ||||
| /* | ||||
|  * Return true if the first `p_len` chars in `s` are equal to `p` and `s` is a valid directory name. | ||||
|  */ | ||||
| static bool is_partial_match(const char *s, const char *p, size_t p_len) | ||||
| { | ||||
|     if (s == NULL || p == NULL) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if only one match, auto-complete line. | ||||
|     return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ | ||||
| #define MAX_DIRS 512 | ||||
|     return strncmp(s, p, p_len) == 0 && strcmp(".", s) != 0 && strcmp("..", s) != 0; | ||||
| } | ||||
|  | ||||
| /* Attempts to match /command "<incomplete-dir>" line to matching directories. | ||||
|  * If there is only one match the line is auto-completed. | ||||
|  * | ||||
|  * Returns the diff between old len and new len of ctx->line on success. | ||||
|  * Returns -1 if no matches or more than one match. | ||||
|  */ | ||||
| #define MAX_DIRS 75 | ||||
| int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||
| { | ||||
|     char b_path[MAX_STR_SIZE]; | ||||
|     char b_name[MAX_STR_SIZE]; | ||||
|     char b_path[MAX_STR_SIZE + 1]; | ||||
|     char b_name[MAX_STR_SIZE + 1]; | ||||
|     char b_cmd[MAX_STR_SIZE]; | ||||
|     const wchar_t *tmpline = &line[wcslen(cmd) + 2];   /* start after "/command \"" */ | ||||
|     const wchar_t *tmpline = &line[wcslen(cmd) + 1];   /* start after "/command " */ | ||||
|  | ||||
|     if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) | ||||
|     if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path) - 1) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) | ||||
|     if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (b_path[0] == '~') | ||||
|         complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2); | ||||
|     if (b_path[0] == '~') { | ||||
|         complete_home_dir(self, b_path, sizeof(b_path) - 1, b_cmd, strlen(b_cmd) + 2); | ||||
|     } | ||||
|  | ||||
|     int si = char_rfind(b_path, '/', strlen(b_path)); | ||||
|  | ||||
| @@ -270,40 +342,52 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||
|         b_path[0] = '.'; | ||||
|         b_path[1] = '\0'; | ||||
|     } else if (!si && b_path[0] != '/') {    /* look for matches in pwd */ | ||||
|         char tmp[MAX_STR_SIZE]; | ||||
|         snprintf(tmp, sizeof(tmp), ".%s", b_path); | ||||
|         snprintf(b_path, sizeof(b_path), "%s", tmp); | ||||
|         memmove(b_path + 1, b_path, sizeof(b_path) - 1); | ||||
|         b_path[0] = '.'; | ||||
|     } | ||||
|  | ||||
|     snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); | ||||
|     b_path[si + 1] = '\0'; | ||||
|     int b_name_len = strlen(b_name); | ||||
|     size_t b_name_len = strlen(b_name); | ||||
|     DIR *dp = opendir(b_path); | ||||
|  | ||||
|     if (dp == NULL) | ||||
|     if (dp == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char **dirnames = (char **) malloc_ptr_array(MAX_DIRS, NAME_MAX + 1); | ||||
|  | ||||
|     if (dirnames == NULL) { | ||||
|         closedir(dp); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char dirnames[MAX_DIRS][NAME_MAX]; | ||||
|     struct dirent *entry; | ||||
|  | ||||
|     int dircount = 0; | ||||
|  | ||||
|     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { | ||||
|         if (strncmp(entry->d_name, b_name, b_name_len) == 0 | ||||
|                 && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) { | ||||
|             snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name); | ||||
|         if (is_partial_match(entry->d_name, b_name, b_name_len)) { | ||||
|             snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name); | ||||
|             ++dircount; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     closedir(dp); | ||||
|  | ||||
|     if (dircount == 0) | ||||
|     if (dircount == 0) { | ||||
|         free_ptr_array((void **) dirnames); | ||||
|         return -1; | ||||
|  | ||||
|     if (dircount > 1) { | ||||
|         qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr); | ||||
|         print_matches(self, m, dirnames, dircount, NAME_MAX); | ||||
|     } | ||||
|  | ||||
|     return complete_line(self, dirnames, dircount, NAME_MAX); | ||||
|     if (dircount > 1) { | ||||
|         qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper); | ||||
|         print_ac_matches(self, m, dirnames, dircount); | ||||
|     } | ||||
|  | ||||
|     int ret = complete_path(self, (const char **) dirnames, dircount); | ||||
|  | ||||
|     free_ptr_array((void **) dirnames); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|   | ||||
| @@ -23,20 +23,30 @@ | ||||
| #ifndef AUTOCOMPLETE_H | ||||
| #define AUTOCOMPLETE_H | ||||
|  | ||||
| /* looks for all instances in list that begin with the last entered word in line according to pos, | ||||
|    then fills line with the complete word. e.g. "Hello jo" would complete the line | ||||
|    with "Hello john". If multiple matches, prints out all the matches and semi-completes line. | ||||
| #include "windows.h" | ||||
|  | ||||
|    list is a pointer to the list of strings being compared, n_items is the number of items | ||||
|    in the list, and size is the size of each item in the list. | ||||
| /* | ||||
|  * Looks for all instances in list that begin with the last entered word in line according to pos, | ||||
|  * then fills line with the complete word. e.g. "Hello jo" would complete the line | ||||
|  * with "Hello john". If multiple matches, prints out all the matches and semi-completes line. | ||||
|  * | ||||
| * `list` is a pointer to `n_items` strings. | ||||
|  * | ||||
|  * dir_search should be true if the line being completed is a file path. | ||||
|  * | ||||
|  * Returns the difference between the old len and new len of line on success. | ||||
|  * Returns -1 on error. | ||||
|  * | ||||
|  * Note: This function should not be called directly. Use complete_line() and complete_path() instead. | ||||
|  */ | ||||
| int complete_line(ToxWindow *self, const char **list, size_t n_items); | ||||
|  | ||||
|    Returns the difference between the old len and new len of line on success, -1 if error */ | ||||
| int complete_line(ToxWindow *self, const void *list, int n_items, int size); | ||||
|  | ||||
| /*  attempts to match /command "<incomplete-dir>" line to matching directories. | ||||
|  | ||||
|     if only one match, auto-complete line. | ||||
|     return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ | ||||
| /* Attempts to match /command "<incomplete-dir>" line to matching directories. | ||||
|  * If there is only one match the line is auto-completed. | ||||
|  * | ||||
|  * Returns the diff between old len and new len of ctx->line on success. | ||||
|  * Returns -1 if no matches or more than one match. | ||||
|  */ | ||||
| int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd); | ||||
|  | ||||
| #endif  /* #define AUTOCOMPLETE_H */ | ||||
| #endif /* AUTOCOMPLETE_H */ | ||||
|   | ||||
							
								
								
									
										117
									
								
								src/avatars.c
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								src/avatars.c
									
									
									
									
									
								
							| @@ -20,14 +20,14 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "misc_tools.h" | ||||
| #include "avatars.h" | ||||
| #include "file_transfers.h" | ||||
| #include "friendlist.h" | ||||
| #include "avatars.h" | ||||
| #include "misc_tools.h" | ||||
|  | ||||
| extern FriendsList Friends; | ||||
|  | ||||
| @@ -39,40 +39,76 @@ static struct Avatar { | ||||
|     off_t size; | ||||
| } Avatar; | ||||
|  | ||||
| /* Compares the first size bytes of fp to signature. | ||||
|  * | ||||
|  * Returns 0 if they are the same | ||||
|  * Returns 1 if they differ | ||||
|  * Returns -1 on error. | ||||
|  * | ||||
|  * On success this function will seek back to the beginning of fp. | ||||
|  */ | ||||
| static int check_file_signature(const unsigned char *signature, size_t size, FILE *fp) | ||||
| { | ||||
|     char *buf = malloc(size); | ||||
|  | ||||
|     if (buf == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (fread(buf, size, 1, fp) != 1) { | ||||
|         free(buf); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int ret = memcmp(signature, buf, size); | ||||
|  | ||||
|     free(buf); | ||||
|  | ||||
|     if (fseek(fp, 0L, SEEK_SET) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return ret == 0 ? 0 : 1; | ||||
| } | ||||
|  | ||||
| static void avatar_clear(void) | ||||
| { | ||||
|     memset(&Avatar, 0, sizeof(struct Avatar)); | ||||
|     Avatar = (struct Avatar) { | ||||
|         0 | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /* Sends avatar to friendnum. | ||||
| /* Sends avatar to friendnumber. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int avatar_send(Tox *m, uint32_t friendnum) | ||||
| int avatar_send(Tox *m, uint32_t friendnumber) | ||||
| { | ||||
|     TOX_ERR_FILE_SEND err; | ||||
|     uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, | ||||
|                                      NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); | ||||
|     Tox_Err_File_Send err; | ||||
|     uint32_t filenumber = tox_file_send(m, friendnumber, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, | ||||
|                                         NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); | ||||
|  | ||||
|     if (Avatar.size == 0) | ||||
|     if (Avatar.size == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (err != TOX_ERR_FILE_SEND_OK) { | ||||
|         fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err); | ||||
|         fprintf(stderr, "tox_file_send failed for friendnumber %u (error %d)\n", friendnumber, err); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR); | ||||
|     struct FileTransfer *ft = new_file_transfer(NULL, friendnumber, filenumber, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR); | ||||
|  | ||||
|     if (!ft) | ||||
|     if (!ft) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     ft->file = fopen(Avatar.path, "r"); | ||||
|  | ||||
|     if (ft->file == NULL) | ||||
|     if (ft->file == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name); | ||||
|     ft->file_size = Avatar.size; | ||||
| @@ -83,11 +119,10 @@ int avatar_send(Tox *m, uint32_t friendnum) | ||||
| /* Sends avatar to all friends */ | ||||
| static void avatar_send_all(Tox *m) | ||||
| { | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < Friends.max_idx; ++i) { | ||||
|         if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) | ||||
|     for (size_t i = 0; i < Friends.max_idx; ++i) { | ||||
|         if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) { | ||||
|             avatar_send(m, Friends.list[i].num); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -98,15 +133,17 @@ static void avatar_send_all(Tox *m) | ||||
|  */ | ||||
| int avatar_set(Tox *m, const char *path, size_t path_len) | ||||
| { | ||||
|     if (path_len == 0 || path_len >= sizeof(Avatar.path)) | ||||
|     if (path_len == 0 || path_len >= sizeof(Avatar.path)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     FILE *fp = fopen(path, "rb"); | ||||
|  | ||||
|     if (fp == NULL) | ||||
|     if (fp == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; | ||||
|     unsigned char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; | ||||
|  | ||||
|     if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) { | ||||
|         fclose(fp); | ||||
| @@ -117,8 +154,9 @@ int avatar_set(Tox *m, const char *path, size_t path_len) | ||||
|  | ||||
|     off_t size = file_size(path); | ||||
|  | ||||
|     if (size == 0 || size > MAX_AVATAR_FILE_SIZE) | ||||
|     if (size == 0 || size > MAX_AVATAR_FILE_SIZE) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     get_file_name(Avatar.name, sizeof(Avatar.name), path); | ||||
|     Avatar.name_len = strlen(Avatar.name); | ||||
| @@ -142,7 +180,14 @@ void avatar_unset(Tox *m) | ||||
|     avatar_send_all(m); | ||||
| } | ||||
|  | ||||
| void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control) | ||||
| void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status) | ||||
| { | ||||
|     if (connection_status == TOX_CONNECTION_NONE) { | ||||
|         kill_avatar_file_transfers_friend(m, friendnumber); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control) | ||||
| { | ||||
|     switch (control) { | ||||
|         case TOX_FILE_CONTROL_RESUME: | ||||
| @@ -166,8 +211,9 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co | ||||
|  | ||||
| void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length) | ||||
| { | ||||
|     if (ft->state != FILE_TRANSFER_STARTED) | ||||
|     if (ft->state != FILE_TRANSFER_STARTED) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (length == 0) { | ||||
|         close_file_transfer(NULL, m, ft, -1, NULL, silent); | ||||
| @@ -188,20 +234,29 @@ void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, | ||||
|         ft->position = position; | ||||
|     } | ||||
|  | ||||
|     uint8_t send_data[length]; | ||||
|     size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file); | ||||
|     uint8_t *send_data = malloc(length); | ||||
|  | ||||
|     if (send_length != length) { | ||||
|     if (send_data == NULL) { | ||||
|         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_FILE_SEND_CHUNK err; | ||||
|     tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err); | ||||
|     size_t send_length = fread(send_data, 1, length, ft->file); | ||||
|  | ||||
|     if (err != TOX_ERR_FILE_SEND_CHUNK_OK) | ||||
|     if (send_length != length) { | ||||
|         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         free(send_data); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Tox_Err_File_Send_Chunk err; | ||||
|     tox_file_send_chunk(m, ft->friendnumber, ft->filenumber, position, send_data, send_length, &err); | ||||
|  | ||||
|     free(send_data); | ||||
|  | ||||
|     if (err != TOX_ERR_FILE_SEND_CHUNK_OK) { | ||||
|         fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err); | ||||
|     } | ||||
|  | ||||
|     ft->position += send_length; | ||||
|     ft->last_keep_alive = get_unix_time(); | ||||
| } | ||||
|   | ||||
| @@ -23,6 +23,8 @@ | ||||
| #ifndef AVATARS_H | ||||
| #define AVATARS_H | ||||
|  | ||||
| #include "file_transfers.h" | ||||
|  | ||||
| #define MAX_AVATAR_FILE_SIZE 65536 | ||||
|  | ||||
| /* Sends avatar to friendnum. | ||||
| @@ -47,6 +49,7 @@ int avatar_set(Tox *m, const char *path, size_t length); | ||||
| void avatar_unset(Tox *m); | ||||
|  | ||||
| void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length); | ||||
| void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control); | ||||
| void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control); | ||||
| void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status); | ||||
|  | ||||
| #endif /* AVATARS_H */ | ||||
|   | ||||
| @@ -20,20 +20,24 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <arpa/inet.h> | ||||
| #include <limits.h> | ||||
| #include <netinet/in.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stdbool.h> | ||||
| #include <limits.h> | ||||
| #include <sys/socket.h> | ||||
|  | ||||
| #include <curl/curl.h> | ||||
| #include <tox/tox.h> | ||||
|  | ||||
| #include "line_info.h" | ||||
| #include "windows.h" | ||||
| #include "misc_tools.h" | ||||
| #include "configdir.h" | ||||
| #include "curl_util.h" | ||||
| #include "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; | ||||
| @@ -104,11 +108,45 @@ static struct DHT_Nodes { | ||||
|     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. | ||||
|  */ | ||||
| #define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping)) | ||||
| 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, | ||||
| @@ -237,6 +275,7 @@ on_exit: | ||||
|  * 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) | ||||
| { | ||||
| @@ -250,26 +289,34 @@ static int update_DHT_nodeslist(const char *nodes_path) | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     struct Recv_Curl_Data recv_data; | ||||
|     struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data)); | ||||
|  | ||||
|     memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); | ||||
|     if (recv_data == NULL) { | ||||
|         fclose(fp); | ||||
|         return -5; | ||||
|     } | ||||
|  | ||||
|     if (curl_fetch_nodes_JSON(&recv_data) == -1) { | ||||
|     if (curl_fetch_nodes_JSON(recv_data) == -1) { | ||||
|         free(recv_data); | ||||
|         fclose(fp); | ||||
|         return -2; | ||||
|     } | ||||
|  | ||||
|     if (recv_data.length == 0) { | ||||
|     if (recv_data->length == 0) { | ||||
|         free(recv_data); | ||||
|         fclose(fp); | ||||
|         return -3; | ||||
|     } | ||||
|  | ||||
|     if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) { | ||||
|     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; | ||||
| } | ||||
|  | ||||
| @@ -377,7 +424,7 @@ static int extract_node(const char *line, struct Node *node) | ||||
|  | ||||
|     long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN); | ||||
|  | ||||
|     if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) { | ||||
|     if (last_pinged <= 0 || node_is_offline(last_pinged)) { | ||||
|         return -3; | ||||
|     } | ||||
|  | ||||
| @@ -426,6 +473,8 @@ static int extract_node(const char *line, struct Node *node) | ||||
| /* 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)); | ||||
|  | ||||
| @@ -477,7 +526,7 @@ void *load_nodeslist_thread(void *data) | ||||
|     /* If nodeslist does not contain any valid entries we set the last_scan value | ||||
|      * to 0 so that it will fetch a new list the next time this function is called. | ||||
|      */ | ||||
|     if (idx == 0) { | ||||
|     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 | ||||
| @@ -556,7 +605,7 @@ static void DHT_bootstrap(Tox *m) | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         TOX_ERR_BOOTSTRAP err; | ||||
|         Tox_Err_Bootstrap err; | ||||
|         tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err); | ||||
|  | ||||
|         if (err != TOX_ERR_BOOTSTRAP_OK) { | ||||
| @@ -577,7 +626,7 @@ static void DHT_bootstrap(Tox *m) | ||||
| void do_tox_connection(Tox *m) | ||||
| { | ||||
|     static time_t last_bootstrap_time = 0; | ||||
|     bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE; | ||||
|     bool connected = prompt_selfConnectionStatus() != TOX_CONNECTION_NONE; | ||||
|  | ||||
|     if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) { | ||||
|         DHT_bootstrap(m); | ||||
|   | ||||
| @@ -39,4 +39,4 @@ void do_tox_connection(Tox *m); | ||||
|  */ | ||||
| int load_DHT_nodeslist(void); | ||||
|  | ||||
| #endif  /* BOOTSTRAP_H */ | ||||
| #endif /* BOOTSTRAP_H */ | ||||
|   | ||||
							
								
								
									
										1019
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										1019
									
								
								src/chat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -23,13 +23,13 @@ | ||||
| #ifndef CHAT_H | ||||
| #define CHAT_H | ||||
|  | ||||
| #include "windows.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| /* set CTRL to -1 if we don't want to send a control signal. | ||||
|    set msg to NULL if we don't want to display a message */ | ||||
| void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL); | ||||
| void kill_chat_window(ToxWindow *self, Tox *m); | ||||
| ToxWindow new_chat(Tox *m, int32_t friendnum); | ||||
| ToxWindow *new_chat(Tox *m, int32_t friendnum); | ||||
|  | ||||
| #endif /* end of include guard: CHAT_H */ | ||||
|   | ||||
| @@ -23,23 +23,25 @@ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "chat.h" | ||||
| #include "conference.h" | ||||
| #include "execute.h" | ||||
| #include "file_transfers.h" | ||||
| #include "friendlist.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "misc_tools.h" | ||||
| #include "friendlist.h" | ||||
| #include "execute.h" | ||||
| #include "line_info.h" | ||||
| #include "groupchat.h" | ||||
| #include "chat.h" | ||||
| #include "file_transfers.h" | ||||
|  | ||||
| extern ToxWindow *prompt; | ||||
| extern FriendsList Friends; | ||||
|  | ||||
| void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 2) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -48,7 +50,7 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | ||||
|     long int idx = strtol(argv[2], NULL, 10); | ||||
|  | ||||
|     if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -60,17 +62,17 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | ||||
|     } else if (strcasecmp(inoutstr, "out") == 0) { | ||||
|         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); | ||||
|     } else { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!ft) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -78,112 +80,145 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar | ||||
|     close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent); | ||||
| } | ||||
|  | ||||
| void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     long int groupnum = strtol(argv[1], NULL, 10); | ||||
|     long int conferencenum = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number."); | ||||
|     if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (tox_invite_friend(m, self->num, groupnum) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group."); | ||||
|     Tox_Err_Conference_Invite err; | ||||
|  | ||||
|     if (!tox_conference_invite(m, self->num, conferencenum, &err)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum); | ||||
| } | ||||
|  | ||||
| void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *groupkey = Friends.list[self->num].group_invite.key; | ||||
|     uint16_t length = Friends.list[self->num].group_invite.length; | ||||
|     uint8_t type = Friends.list[self->num].group_invite.type; | ||||
|     const char *conferencekey = Friends.list[self->num].conference_invite.key; | ||||
|     uint16_t length = Friends.list[self->num].conference_invite.length; | ||||
|     uint8_t type = Friends.list[self->num].conference_invite.type; | ||||
|  | ||||
|     if (!Friends.list[self->num].group_invite.pending) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite."); | ||||
|     if (!Friends.list[self->num].conference_invite.pending) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int groupnum = -1; | ||||
|     uint32_t conferencenum; | ||||
|  | ||||
|     if (type == TOX_GROUPCHAT_TYPE_TEXT) | ||||
|         groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length); | ||||
|     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||
|         Tox_Err_Conference_Join err; | ||||
|         conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err); | ||||
|  | ||||
|     /*#ifdef AUDIO | ||||
|         else | ||||
|             groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length, | ||||
|                                                NULL, NULL); | ||||
|     #endif*/ | ||||
|         if (err != TOX_ERR_CONFERENCE_JOIN_OK) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); | ||||
|             return; | ||||
|         } | ||||
|     } else if (type == TOX_CONFERENCE_TYPE_AV) { | ||||
| #ifdef AUDIO | ||||
|         conferencenum = toxav_join_av_groupchat(m, self->num, (const uint8_t *) conferencekey, length, | ||||
|                                                 audio_conference_callback, NULL); | ||||
|  | ||||
|     if (groupnum == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize."); | ||||
|         if (conferencenum == (uint32_t) -1) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| #else | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||
|         return; | ||||
| #endif | ||||
|     } else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (init_groupchat_win(prompt, m, groupnum, type) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||
|         tox_del_groupchat(m, groupnum); | ||||
|     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); | ||||
|         tox_conference_delete(m, conferencenum, NULL); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #ifdef AUDIO | ||||
|  | ||||
|     if (type == TOX_CONFERENCE_TYPE_AV) { | ||||
|         if (!init_conference_audio_input(m, conferencenum)) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File ID required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     long int idx = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||
|  | ||||
|     if (!ft) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (ft->state != FILE_TRANSFER_PENDING) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if ((ft->file = fopen(ft->file_path, "a")) == NULL) { | ||||
|         const char *msg =  "File transfer failed: Invalid file path."; | ||||
|         const char *msg =  "File transfer failed: Invalid download path."; | ||||
|         close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_FILE_CONTROL err; | ||||
|     tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err); | ||||
|     Tox_Err_File_Control err; | ||||
|     tox_file_control(m, self->num, ft->filenumber, TOX_FILE_CONTROL_RESUME, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_FILE_CONTROL_OK) | ||||
|     if (err != TOX_ERR_FILE_CONTROL_OK) { | ||||
|         goto on_recv_error; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] as: '%s'", idx, ft->file_path); | ||||
|  | ||||
|     /* prep progress bar line */ | ||||
|     char progline[MAX_STR_SIZE]; | ||||
|     init_progress_bar(progline); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); | ||||
|  | ||||
|     ft->line_id = self->chatwin->hst->line_end->id + 2; | ||||
|     ft->state = FILE_TRANSFER_STARTED; | ||||
| @@ -194,63 +229,58 @@ on_recv_error: | ||||
|  | ||||
|     switch (err) { | ||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found."); | ||||
|             return; | ||||
|  | ||||
|         case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online."); | ||||
|             return; | ||||
|  | ||||
|         case TOX_ERR_FILE_CONTROL_NOT_FOUND: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber."); | ||||
|             return; | ||||
|  | ||||
|         case TOX_ERR_FILE_CONTROL_SENDQ: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error."); | ||||
|             return; | ||||
|  | ||||
|         default: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err); | ||||
|             return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     const char *errmsg = NULL; | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argv[1][0] != '\"') { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* remove opening and closing quotes */ | ||||
|     char path[MAX_STR_SIZE]; | ||||
|     snprintf(path, sizeof(path), "%s", &argv[1][1]); | ||||
|     int path_len = strlen(path) - 1; | ||||
|     path[path_len] = '\0'; | ||||
|     snprintf(path, sizeof(path), "%s", argv[1]); | ||||
|     int path_len = strlen(path); | ||||
|  | ||||
|     if (path_len >= MAX_STR_SIZE) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     FILE *file_to_send = fopen(path, "r"); | ||||
|  | ||||
|     if (file_to_send == NULL) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File not found."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     off_t filesize = file_size(path); | ||||
|  | ||||
|     if (filesize == 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file."); | ||||
|         fclose(file_to_send); | ||||
|         return; | ||||
|     } | ||||
| @@ -258,12 +288,13 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | ||||
|     char file_name[TOX_MAX_FILENAME_LENGTH]; | ||||
|     size_t namelen = get_file_name(file_name, sizeof(file_name), path); | ||||
|  | ||||
|     TOX_ERR_FILE_SEND err; | ||||
|     Tox_Err_File_Send err; | ||||
|     uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL, | ||||
|                                      (uint8_t *) file_name, namelen, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_FILE_SEND_OK) | ||||
|     if (err != TOX_ERR_FILE_SEND_OK) { | ||||
|         goto on_send_error; | ||||
|     } | ||||
|  | ||||
|     struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA); | ||||
|  | ||||
| @@ -279,7 +310,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | ||||
|  | ||||
|     char sizestr[32]; | ||||
|     bytes_convert_str(sizestr, sizeof(sizestr), filesize); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr); | ||||
|  | ||||
|     return; | ||||
|  | ||||
| @@ -307,7 +338,7 @@ on_send_error: | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg); | ||||
|     tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); | ||||
|     fclose(file_to_send); | ||||
| } | ||||
|   | ||||
| @@ -23,12 +23,12 @@ | ||||
| #ifndef CHAT_COMMANDS_H | ||||
| #define CHAT_COMMANDS_H | ||||
|  | ||||
| #include "windows.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_invite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| @@ -41,11 +41,13 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ | ||||
| void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #ifdef VIDEO | ||||
| void cmd_vcall(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_res(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| #endif /* #define CHAT_COMMANDS_H */ | ||||
| #endif /* CHAT_COMMANDS_H */ | ||||
|   | ||||
							
								
								
									
										1418
									
								
								src/conference.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1418
									
								
								src/conference.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										118
									
								
								src/conference.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/conference.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| /*  conference.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef CONFERENCE_H | ||||
| #define CONFERENCE_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH | ||||
| #define SIDEBAR_WIDTH 16 | ||||
|  | ||||
| typedef struct ConferencePeer { | ||||
|     bool       active; | ||||
|  | ||||
|     uint8_t    pubkey[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint32_t   peernum;    /* index in chat->peer_list */ | ||||
|  | ||||
|     char       name[TOX_MAX_NAME_LENGTH]; | ||||
|     size_t     name_length; | ||||
|  | ||||
|     bool       sending_audio; | ||||
|     uint32_t   audio_out_idx; | ||||
|     time_t     last_audio_time; | ||||
| } ConferencePeer; | ||||
|  | ||||
| typedef struct AudioInputCallbackData { | ||||
|     Tox *tox; | ||||
|     uint32_t conferencenum; | ||||
| } AudioInputCallbackData; | ||||
|  | ||||
| #define PUBKEY_STRING_SIZE (2 * TOX_PUBLIC_KEY_SIZE + 1) | ||||
| typedef struct NameListEntry { | ||||
|     char name[TOX_MAX_NAME_LENGTH]; | ||||
|     char pubkey_str[PUBKEY_STRING_SIZE]; | ||||
|     uint32_t peernum; | ||||
| } NameListEntry; | ||||
|  | ||||
|  | ||||
| typedef struct { | ||||
|     int chatwin; | ||||
|     bool active; | ||||
|     uint8_t type; | ||||
|     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ | ||||
|     time_t start_time; | ||||
|  | ||||
|     char title[CONFERENCE_MAX_TITLE_LENGTH + 1]; | ||||
|     size_t title_length; | ||||
|  | ||||
|     ConferencePeer *peer_list; | ||||
|     uint32_t max_idx; | ||||
|  | ||||
|     NameListEntry *name_list; | ||||
|     uint32_t num_peers; | ||||
|  | ||||
|     bool push_to_talk_enabled; | ||||
|     time_t ptt_last_pushed; | ||||
|  | ||||
|     bool audio_enabled; | ||||
|     time_t last_sent_audio; | ||||
|     uint32_t audio_in_idx; | ||||
|     AudioInputCallbackData audio_input_callback_data; | ||||
| } ConferenceChat; | ||||
|  | ||||
| /* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */ | ||||
| void free_conference(ToxWindow *self, uint32_t conferencenum); | ||||
|  | ||||
| int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length); | ||||
|  | ||||
| /* destroys and re-creates conference window with or without the peerlist */ | ||||
| void redraw_conference_win(ToxWindow *self); | ||||
|  | ||||
| void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length); | ||||
| void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title); | ||||
| int conference_enable_logging(ToxWindow *self, Tox *m, uint32_t conferencenum, struct chatlog *log); | ||||
|  | ||||
| /* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum | ||||
|  * of `maxpeers`. | ||||
|  * Maches each peer whose name or pubkey begins with `prefix`. | ||||
|  * If `prefix` is exactly the pubkey of a peer, matches only that peer. | ||||
|  * return number of entries placed in `entries`. | ||||
|  */ | ||||
| uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries, | ||||
|         uint32_t maxpeers); | ||||
|  | ||||
| bool init_conference_audio_input(Tox *tox, uint32_t conferencenum); | ||||
| bool enable_conference_audio(Tox *tox, uint32_t conferencenum); | ||||
| bool disable_conference_audio(Tox *tox, uint32_t conferencenum); | ||||
| bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled); | ||||
| void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, | ||||
|                                const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t | ||||
|                                sample_rate, void *userdata); | ||||
|  | ||||
| bool conference_mute_self(uint32_t conferencenum); | ||||
| bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum); | ||||
| bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold); | ||||
| float conference_get_VAD_threshold(uint32_t conferencenum); | ||||
|  | ||||
| #endif /* CONFERENCE_H */ | ||||
							
								
								
									
										210
									
								
								src/conference_commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								src/conference_commands.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| /*  conference_commands.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "conference.h" | ||||
| #include "line_info.h" | ||||
| #include "log.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| static void print_err(ToxWindow *self, const char *error_str) | ||||
| { | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); | ||||
| } | ||||
|  | ||||
| void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     Tox_Err_Conference_Title err; | ||||
|     char title[CONFERENCE_MAX_TITLE_LENGTH + 1]; | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         size_t tlen = tox_conference_get_title_size(m, self->num, &err); | ||||
|  | ||||
|         if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) { | ||||
|             print_err(self, "Title is not set"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) { | ||||
|             print_err(self, "Title is not set"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         title[tlen] = '\0'; | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     size_t len = strlen(argv[1]); | ||||
|  | ||||
|     if (len >= sizeof(title)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     snprintf(title, sizeof(title), "%s", argv[1]); | ||||
|  | ||||
|     if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     conference_rename_log_path(m, self->num, title);  // must be called first | ||||
|  | ||||
|     conference_set_title(self, self->num, title, len); | ||||
|  | ||||
|     char selfnick[TOX_MAX_NAME_LENGTH]; | ||||
|     tox_self_get_name(m, (uint8_t *) selfnick); | ||||
|  | ||||
|     size_t sn_len = tox_self_get_name_size(m); | ||||
|     selfnick[sn_len] = '\0'; | ||||
|  | ||||
|     line_info_add(self, true, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title); | ||||
|  | ||||
|     char tmp_event[MAX_STR_SIZE + 20]; | ||||
|     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); | ||||
|     write_to_log(tmp_event, selfnick, self->chatwin->log, true); | ||||
| } | ||||
|  | ||||
| #ifdef AUDIO | ||||
| void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     bool enable; | ||||
|  | ||||
|     if (argc == 1 && !strcasecmp(argv[1], "on")) { | ||||
|         enable = true; | ||||
|     } else if (argc == 1 && !strcasecmp(argv[1], "off")) { | ||||
|         enable = false; | ||||
|     } else { | ||||
|         print_err(self, "Please specify: on | off"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) { | ||||
|         print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk." | ||||
|                   : "Disabled conference audio"); | ||||
|     } else { | ||||
|         print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         if (conference_mute_self(self->num)) { | ||||
|             print_err(self, "Toggled self audio mute status"); | ||||
|         } else { | ||||
|             print_err(self, "No audio input to mute"); | ||||
|         } | ||||
|     } else { | ||||
|         NameListEntry *entries[16]; | ||||
|         uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16); | ||||
|  | ||||
|         if (n == 0) { | ||||
|             print_err(self, "No such peer"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (n > 1) { | ||||
|             print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):"); | ||||
|  | ||||
|             for (uint32_t i = 0; i < n; ++i) { | ||||
|                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name); | ||||
|             } | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (conference_mute_peer(m, self->num, entries[0]->peernum)) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name); | ||||
|         } else { | ||||
|             print_err(self, "Peer is not on the call"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f", | ||||
|                       (double) conference_get_VAD_threshold(self->num)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argc > 1) { | ||||
|         print_err(self, "Only one argument allowed."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *end; | ||||
|     float value = strtof(argv[1], &end); | ||||
|  | ||||
|     if (*end) { | ||||
|         print_err(self, "Invalid input"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (conference_set_VAD_threshold(self->num, value)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value); | ||||
|     } else { | ||||
|         print_err(self, "Failed to set conference audio input sensitivity."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     bool enable; | ||||
|  | ||||
|     if (argc == 1 && !strcasecmp(argv[1], "on")) { | ||||
|         enable = true; | ||||
|     } else if (argc == 1 && !strcasecmp(argv[1], "off")) { | ||||
|         enable = false; | ||||
|     } else { | ||||
|         print_err(self, "Please specify: on | off"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!toggle_conference_push_to_talk(self->num, enable)) { | ||||
|         print_err(self, "Failed to toggle push to talk."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled"); | ||||
| } | ||||
| #endif /* AUDIO */ | ||||
							
								
								
									
										35
									
								
								src/conference_commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/conference_commands.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /*  conference_commands.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef CONFERENCE_COMMANDS_H | ||||
| #define CONFERENCE_COMMANDS_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| #endif /* CONFERENCE_COMMANDS_H */ | ||||
| @@ -20,18 +20,18 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <errno.h> | ||||
| #include <unistd.h> | ||||
| #include <pwd.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "configdir.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
|  | ||||
| /* get the user's home directory. */ | ||||
| void get_home_dir(char *home, int size) | ||||
| @@ -48,8 +48,9 @@ void get_home_dir(char *home, int size) | ||||
|     } else { | ||||
|         hmstr = getenv("HOME"); | ||||
|  | ||||
|         if (hmstr == NULL) | ||||
|         if (hmstr == NULL) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         snprintf(buf, sizeof(buf), "%s", hmstr); | ||||
|         hmstr = buf; | ||||
| @@ -77,8 +78,9 @@ char *get_user_config_dir(void) | ||||
|     len = strlen(home) + strlen("/Library/Application Support") + 1; | ||||
|     user_config_dir = malloc(len); | ||||
|  | ||||
|     if (user_config_dir == NULL) | ||||
|     if (user_config_dir == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     snprintf(user_config_dir, len, "%s/Library/Application Support", home); | ||||
| # else /* __APPLE__ */ | ||||
| @@ -89,8 +91,9 @@ char *get_user_config_dir(void) | ||||
|         len = strlen(home) + strlen("/.config") + 1; | ||||
|         user_config_dir = malloc(len); | ||||
|  | ||||
|         if (user_config_dir == NULL) | ||||
|         if (user_config_dir == NULL) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         snprintf(user_config_dir, len, "%s/.config", home); | ||||
|     } else { | ||||
| @@ -112,14 +115,16 @@ int create_user_config_dirs(char *path) | ||||
|     struct stat buf; | ||||
|     int mkdir_err = mkdir(path, 0700); | ||||
|  | ||||
|     if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) | ||||
|     if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1); | ||||
|     char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1); | ||||
|  | ||||
|     if (fullpath == NULL || logpath == NULL) | ||||
|     if (fullpath == NULL || logpath == NULL) { | ||||
|         exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     strcpy(fullpath, path); | ||||
|     strcat(fullpath, CONFIGDIR); | ||||
|   | ||||
| @@ -53,4 +53,4 @@ void get_home_dir(char *home, int size); | ||||
|  */ | ||||
| int create_user_config_dirs(char *path); | ||||
|  | ||||
| #endif /* #define CONFIGDIR_H */ | ||||
| #endif /* CONFIGDIR_H */ | ||||
|   | ||||
| @@ -36,8 +36,9 @@ | ||||
|  */ | ||||
| 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) | ||||
|     if (proxy_type == TOX_PROXY_TYPE_NONE) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (proxy_address == NULL || port == 0) { | ||||
|         return -1; | ||||
|   | ||||
| @@ -23,6 +23,8 @@ | ||||
| #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" | ||||
|  | ||||
| @@ -52,4 +54,4 @@ int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uin | ||||
|  */ | ||||
| size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer); | ||||
|  | ||||
| #endif  /* CURL_UTIL_H */ | ||||
| #endif /* CURL_UTIL_H */ | ||||
|   | ||||
							
								
								
									
										204
									
								
								src/execute.c
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								src/execute.c
									
									
									
									
									
								
							| @@ -20,19 +20,20 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "execute.h" | ||||
| #include "api.h" | ||||
| #include "chat_commands.h" | ||||
| #include "execute.h" | ||||
| #include "global_commands.h" | ||||
| #include "group_commands.h" | ||||
| #include "conference_commands.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| struct cmd_func { | ||||
|     const char *name; | ||||
| @@ -47,11 +48,13 @@ static struct cmd_func global_commands[] = { | ||||
|     { "/connect",   cmd_connect       }, | ||||
|     { "/decline",   cmd_decline       }, | ||||
|     { "/exit",      cmd_quit          }, | ||||
|     { "/group",     cmd_groupchat     }, | ||||
|     { "/conference", cmd_conference    }, | ||||
|     { "/help",      cmd_prompt_help   }, | ||||
|     { "/log",       cmd_log           }, | ||||
|     { "/myid",      cmd_myid          }, | ||||
| #ifdef QRCODE | ||||
|     { "/myqr",      cmd_myqr          }, | ||||
| #endif /* QRCODE */ | ||||
|     { "/nick",      cmd_nick          }, | ||||
|     { "/note",      cmd_note          }, | ||||
|     { "/nospam",    cmd_nospam        }, | ||||
| @@ -64,88 +67,149 @@ static struct cmd_func global_commands[] = { | ||||
|     { "/sdev",      cmd_change_device }, | ||||
| #endif /* AUDIO */ | ||||
| #ifdef VIDEO | ||||
|     { "/lsvdev",    cmd_list_video_devices }, | ||||
|     { "/svdev" ,    cmd_change_video_device }, | ||||
|     { "/lsvdev",    cmd_list_video_devices  }, | ||||
|     { "/svdev",     cmd_change_video_device }, | ||||
| #endif /* VIDEO */ | ||||
| #ifdef PYTHON | ||||
|     { "/run",       cmd_run           }, | ||||
| #endif /* PYTHON */ | ||||
|     { NULL,         NULL              }, | ||||
| }; | ||||
|  | ||||
| static struct cmd_func chat_commands[] = { | ||||
|     { "/cancel",    cmd_cancelfile  }, | ||||
|     { "/invite",    cmd_groupinvite }, | ||||
|     { "/join",      cmd_join_group  }, | ||||
|     { "/savefile",  cmd_savefile    }, | ||||
|     { "/sendfile",  cmd_sendfile    }, | ||||
|     { "/cancel",    cmd_cancelfile        }, | ||||
|     { "/invite",    cmd_conference_invite }, | ||||
|     { "/join",      cmd_conference_join   }, | ||||
|     { "/savefile",  cmd_savefile          }, | ||||
|     { "/sendfile",  cmd_sendfile          }, | ||||
| #ifdef AUDIO | ||||
|     { "/call",      cmd_call        }, | ||||
|     { "/answer",    cmd_answer      }, | ||||
|     { "/reject",    cmd_reject      }, | ||||
|     { "/hangup",    cmd_hangup      }, | ||||
|     { "/mute",      cmd_mute        }, | ||||
|     { "/sense",     cmd_sense       }, | ||||
|     { "/call",      cmd_call              }, | ||||
|     { "/answer",    cmd_answer            }, | ||||
|     { "/reject",    cmd_reject            }, | ||||
|     { "/hangup",    cmd_hangup            }, | ||||
|     { "/mute",      cmd_mute              }, | ||||
|     { "/sense",     cmd_sense             }, | ||||
|     { "/bitrate",   cmd_bitrate           }, | ||||
| #endif /* AUDIO */ | ||||
| #ifdef VIDEO | ||||
|     { "/video",     cmd_video       }, | ||||
|     { "/vcall",     cmd_vcall             }, | ||||
|     { "/video",     cmd_video             }, | ||||
|     { "/res",       cmd_res               }, | ||||
| #endif /* VIDEO */ | ||||
|     { NULL,         NULL            }, | ||||
|     { NULL,         NULL                  }, | ||||
| }; | ||||
|  | ||||
| static struct cmd_func group_commands[] = { | ||||
|     { "/title",     cmd_set_title   }, | ||||
| static struct cmd_func conference_commands[] = { | ||||
|     { "/title",     cmd_conference_set_title }, | ||||
|  | ||||
| #ifdef AUDIO | ||||
|     { "/mute",      cmd_mute        }, | ||||
|     { "/sense",     cmd_sense       }, | ||||
|     { "/audio",     cmd_enable_audio }, | ||||
|     { "/mute",      cmd_conference_mute   }, | ||||
|     { "/ptt",       cmd_conference_push_to_talk }, | ||||
|     { "/sense",     cmd_conference_sense  }, | ||||
| #endif /* AUDIO */ | ||||
|     { NULL,         NULL            }, | ||||
|     { NULL,         NULL             }, | ||||
| }; | ||||
|  | ||||
| /* Parses input command and puts args into arg array. | ||||
|    Returns number of arguments on success, -1 on failure. */ | ||||
| static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #define SPECIAL_COMMANDS 7 | ||||
| #else | ||||
| #define SPECIAL_COMMANDS 6 | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| /* Special commands are commands that only take one argument even if it contains spaces */ | ||||
| static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = { | ||||
|     "/avatar", | ||||
|     "/nick", | ||||
|     "/note", | ||||
| #ifdef PYTHON | ||||
|     "/run", | ||||
| #endif /* PYTHON */ | ||||
|     "/sendfile", | ||||
|     "/title", | ||||
|     "/mute", | ||||
| }; | ||||
|  | ||||
| /* Returns true if input command is in the special_commands array. */ | ||||
| static bool is_special_command(const char *input) | ||||
| { | ||||
|     const int s = char_find(0, input, ' '); | ||||
|  | ||||
|     for (int i = 0; i < SPECIAL_COMMANDS; ++i) { | ||||
|         if (strncmp(input, special_commands[i], s) == 0) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| /* Parses commands in the special_commands array. Unlike parse_command, this function | ||||
|  * does not split the input string at spaces. | ||||
|  * | ||||
|  * Returns the number of arguments. | ||||
|  */ | ||||
| static int parse_special_command(const char *input, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     int len = strlen(input); | ||||
|     int s = char_find(0, input, ' '); | ||||
|  | ||||
|     memcpy(args[0], input, s); | ||||
|     args[0][s++] = '\0';    // increment to remove space after "/command " | ||||
|  | ||||
|     if (s >= len) { | ||||
|         return 1;  // No additional args | ||||
|     } | ||||
|  | ||||
|     memcpy(args[1], input + s, len - s); | ||||
|     args[1][len - s] = '\0'; | ||||
|  | ||||
|     return 2; | ||||
| } | ||||
|  | ||||
| /* Parses input command and puts args into arg array. | ||||
|  * | ||||
|  * Returns the number of arguments. | ||||
|  */ | ||||
| static int parse_command(const char *input, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (is_special_command(input)) { | ||||
|         return parse_special_command(input, args); | ||||
|     } | ||||
|  | ||||
|     char *cmd = strdup(input); | ||||
|  | ||||
|     if (cmd == NULL) | ||||
|     if (cmd == NULL) { | ||||
|         exit_toxic_err("failed in parse_command", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     int num_args = 0; | ||||
|     int i = 0;    /* index of last char in an argument */ | ||||
|  | ||||
|     /* characters wrapped in double quotes count as one arg */ | ||||
|     while (num_args < MAX_NUM_ARGS) { | ||||
|         int qt_ofst = 0;    /* set to 1 to offset index for quote char at end of arg */ | ||||
|         int i = char_find(0, cmd, ' ');    // index of last char in an argument | ||||
|         memcpy(args[num_args], cmd, i); | ||||
|         args[num_args++][i] = '\0'; | ||||
|  | ||||
|         if (*cmd == '\"') { | ||||
|             qt_ofst = 1; | ||||
|             i = char_find(1, cmd, '\"'); | ||||
|  | ||||
|             if (cmd[i] == '\0') { | ||||
|                 const char *errmsg = "Invalid argument. Did you forget a closing \"?"; | ||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|                 free(cmd); | ||||
|                 return -1; | ||||
|             } | ||||
|         } else { | ||||
|             i = char_find(0, cmd, ' '); | ||||
|         } | ||||
|  | ||||
|         memcpy(args[num_args], cmd, i + qt_ofst); | ||||
|         args[num_args++][i + qt_ofst] = '\0'; | ||||
|  | ||||
|         if (cmd[i] == '\0')    /* no more args */ | ||||
|         if (cmd[i] == '\0') {  // no more args | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         char tmp[MAX_STR_SIZE]; | ||||
|         snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]); | ||||
|         strcpy(cmd, tmp);    /* tmp will always fit inside cmd */ | ||||
|         strcpy(cmd, tmp);    // tmp will always fit inside cmd | ||||
|     } | ||||
|  | ||||
|     free(cmd); | ||||
|     return num_args; | ||||
| } | ||||
|  | ||||
| /* Matches command to respective function. Returns 0 on match, 1 on no match */ | ||||
| /* Matches command to respective function. | ||||
|  * | ||||
|  * Returns 0 on match. | ||||
|  * Returns 1 on no match | ||||
|  */ | ||||
| static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, | ||||
|                       char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
| @@ -163,35 +227,49 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c | ||||
|  | ||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | ||||
| { | ||||
|     if (string_is_empty(input)) | ||||
|     if (string_is_empty(input)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char args[MAX_NUM_ARGS][MAX_STR_SIZE]; | ||||
|     int num_args = parse_command(w, self, input, args); | ||||
|     int num_args = parse_command(input, args); | ||||
|  | ||||
|     if (num_args == -1) | ||||
|     if (num_args <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Try to match input command to command functions. If non-global command mode is specified, | ||||
|        try specified mode's commands first, then upon failure try global commands. | ||||
|  | ||||
|        Note: Global commands must come last in case of duplicate command names */ | ||||
|      * try specified mode's commands first, then upon failure try global commands. | ||||
|      * | ||||
|      * Note: Global commands must come last in case of duplicate command names | ||||
|      */ | ||||
|     switch (mode) { | ||||
|         case CHAT_COMMAND_MODE: | ||||
|             if (do_command(w, self, m, num_args, chat_commands, args) == 0) | ||||
|             if (do_command(w, self, m, num_args, chat_commands, args) == 0) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|  | ||||
|         case GROUPCHAT_COMMAND_MODE: | ||||
|             if (do_command(w, self, m, num_args, group_commands, args) == 0) | ||||
|         case CONFERENCE_COMMAND_MODE: | ||||
|             if (do_command(w, self, m, num_args, conference_commands, args) == 0) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     if (do_command(w, self, m, num_args, global_commands, args) == 0) | ||||
|     if (do_command(w, self, m, num_args, global_commands, args) == 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command."); | ||||
| #ifdef PYTHON | ||||
|  | ||||
|     if (do_plugin_command(num_args, args) == 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid command."); | ||||
| } | ||||
|   | ||||
| @@ -31,9 +31,9 @@ | ||||
| enum { | ||||
|     GLOBAL_COMMAND_MODE, | ||||
|     CHAT_COMMAND_MODE, | ||||
|     GROUPCHAT_COMMAND_MODE, | ||||
|     CONFERENCE_COMMAND_MODE, | ||||
| }; | ||||
|  | ||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); | ||||
|  | ||||
| #endif /* #define EXECUTE_H */ | ||||
| #endif /* EXECUTE_H */ | ||||
|   | ||||
| @@ -20,23 +20,24 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "friendlist.h" | ||||
| #include "file_transfers.h" | ||||
| #include "friendlist.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern FriendsList Friends; | ||||
|  | ||||
| /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | ||||
| #define NUM_PROG_MARKS 50 | ||||
| #define STR_BUF_SIZE 30 | ||||
|  | ||||
| /* creates initial progress line that will be updated during file transfer. | ||||
|    Assumes progline has room for at least MAX_STR_SIZE bytes */ | ||||
| @@ -45,8 +46,9 @@ void init_progress_bar(char *progline) | ||||
|     strcpy(progline, "0% ["); | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < NUM_PROG_MARKS; ++i) | ||||
|     for (i = 0; i < NUM_PROG_MARKS; ++i) { | ||||
|         strcat(progline, "-"); | ||||
|     } | ||||
|  | ||||
|     strcat(progline, "] 0.0 B/s"); | ||||
| } | ||||
| @@ -54,42 +56,58 @@ void init_progress_bar(char *progline) | ||||
| /* prints a progress bar for file transfers. */ | ||||
| void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id) | ||||
| { | ||||
|     if (bps < 0 || pct_done < 0 || pct_done > 100) | ||||
|     if (bps < 0 || pct_done < 0 || pct_done > 100) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char pct_str[24]; | ||||
|     char pct_str[STR_BUF_SIZE]; | ||||
|     snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done); | ||||
|  | ||||
|     char bps_str[24]; | ||||
|     char bps_str[STR_BUF_SIZE]; | ||||
|     bytes_convert_str(bps_str, sizeof(bps_str), bps); | ||||
|  | ||||
|     char prog_line[NUM_PROG_MARKS + 1] = {0}; | ||||
|     char prog_line[NUM_PROG_MARKS + 1]; | ||||
|     prog_line[0] = 0; | ||||
|  | ||||
|     int n = pct_done / (100 / NUM_PROG_MARKS); | ||||
|     int i, j; | ||||
|  | ||||
|     for (i = 0; i < n; ++i) | ||||
|     for (i = 0; i < n; ++i) { | ||||
|         strcat(prog_line, "="); | ||||
|     } | ||||
|  | ||||
|     if (pct_done < 100) | ||||
|     if (pct_done < 100) { | ||||
|         strcpy(prog_line + n, ">"); | ||||
|     } | ||||
|  | ||||
|     for (j = i; j < NUM_PROG_MARKS - 1; ++j) | ||||
|     for (j = i; j < NUM_PROG_MARKS - 1; ++j) { | ||||
|         strcat(prog_line, "-"); | ||||
|     } | ||||
|  | ||||
|     char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7]; | ||||
|     snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str); | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     snprintf(full_line, line_buf_size, "%s [%s] %s/s", pct_str, prog_line, bps_str); | ||||
|  | ||||
|     line_info_set(self, line_id, full_line); | ||||
|  | ||||
|     free(full_line); | ||||
| } | ||||
|  | ||||
| static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft) | ||||
| static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft) | ||||
| { | ||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) | ||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Timeout must be set to 1 second to show correct bytes per second */ | ||||
|     if (!timed_out(ft->last_line_progress, 1)) | ||||
|     if (!timed_out(ft->last_line_progress, 1)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     double remain = ft->file_size - ft->position; | ||||
|     double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100; | ||||
| @@ -100,33 +118,38 @@ static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer | ||||
| } | ||||
|  | ||||
| /* refreshes active file transfer status bars. */ | ||||
| void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum) | ||||
| void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber) | ||||
| { | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < MAX_FILES; ++i) { | ||||
|         refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]); | ||||
|         refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]); | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         refresh_progress_helper(self, &Friends.list[friendnumber].file_receiver[i]); | ||||
|         refresh_progress_helper(self, &Friends.list[friendnumber].file_sender[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Returns a pointer to friendnum's FileTransfer struct associated with filenum. | ||||
|  * Returns NULL if filenum is invalid. | ||||
|  */ | ||||
| struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum) | ||||
| static void clear_file_transfer(struct FileTransfer *ft) | ||||
| { | ||||
|     size_t i; | ||||
|     *ft = (struct FileTransfer) { | ||||
|         0 | ||||
|     }; | ||||
| } | ||||
|  | ||||
|     for (i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i]; | ||||
| /* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber. | ||||
|  * Returns NULL if filenumber is invalid. | ||||
|  */ | ||||
| struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber) | ||||
| { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i]; | ||||
|  | ||||
|         if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) | ||||
|         if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) { | ||||
|             return ft_send; | ||||
|         } | ||||
|  | ||||
|         struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i]; | ||||
|         struct FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i]; | ||||
|  | ||||
|         if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum) | ||||
|         if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) { | ||||
|             return ft_recv; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| @@ -135,21 +158,21 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen | ||||
| /* Returns a pointer to the FileTransfer struct associated with index with the direction specified. | ||||
|  * Returns NULL on failure. | ||||
|  */ | ||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | ||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index, | ||||
|         FILE_TRANSFER_DIRECTION direction) | ||||
| { | ||||
|     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) | ||||
|     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < MAX_FILES; ++i) { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? | ||||
|                                       &Friends.list[friendnum].file_sender[i] : | ||||
|                                       &Friends.list[friendnum].file_receiver[i]; | ||||
|                                       &Friends.list[friendnumber].file_sender[i] : | ||||
|                                       &Friends.list[friendnumber].file_receiver[i]; | ||||
|  | ||||
|         if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) | ||||
|         if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) { | ||||
|             return ft; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| @@ -158,23 +181,19 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t | ||||
| /* Returns a pointer to an unused file sender. | ||||
|  * Returns NULL if all file senders are in use. | ||||
|  */ | ||||
| static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) | ||||
| static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, uint8_t type) | ||||
| { | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i]; | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; | ||||
|  | ||||
|         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|             memset(ft, 0, sizeof(struct FileTransfer)); | ||||
|             clear_file_transfer(ft); | ||||
|             ft->window = window; | ||||
|             ft->index = i; | ||||
|             ft->friendnum = friendnum; | ||||
|             ft->filenum = filenum; | ||||
|             ft->friendnumber = friendnumber; | ||||
|             ft->filenumber = filenumber; | ||||
|             ft->file_type = type; | ||||
|             ft->last_keep_alive = get_unix_time(); | ||||
|             ft->state = FILE_TRANSFER_PENDING; | ||||
|             ft->direction = FILE_TRANSFER_SEND; | ||||
|             return ft; | ||||
|         } | ||||
|     } | ||||
| @@ -185,23 +204,20 @@ static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnu | ||||
| /* Returns a pointer to an unused file receiver. | ||||
|  * Returns NULL if all file receivers are in use. | ||||
|  */ | ||||
| static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) | ||||
| static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, | ||||
|         uint8_t type) | ||||
| { | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i]; | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i]; | ||||
|  | ||||
|         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|             memset(ft, 0, sizeof(struct FileTransfer)); | ||||
|             clear_file_transfer(ft); | ||||
|             ft->window = window; | ||||
|             ft->index = i; | ||||
|             ft->friendnum = friendnum; | ||||
|             ft->filenum = filenum; | ||||
|             ft->friendnumber = friendnumber; | ||||
|             ft->filenumber = filenumber; | ||||
|             ft->file_type = type; | ||||
|             ft->last_keep_alive = get_unix_time(); | ||||
|             ft->state = FILE_TRANSFER_PENDING; | ||||
|             ft->direction = FILE_TRANSFER_RECV; | ||||
|             return ft; | ||||
|         } | ||||
|     } | ||||
| @@ -212,14 +228,16 @@ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friend | ||||
| /* Initializes an unused file transfer and returns its pointer. | ||||
|  * Returns NULL on failure. | ||||
|  */ | ||||
| struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, | ||||
| struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, | ||||
|                                        FILE_TRANSFER_DIRECTION direction, uint8_t type) | ||||
| { | ||||
|     if (direction == FILE_TRANSFER_RECV) | ||||
|         return new_file_receiver(window, friendnum, filenum, type); | ||||
|     if (direction == FILE_TRANSFER_RECV) { | ||||
|         return new_file_receiver(window, friendnumber, filenumber, type); | ||||
|     } | ||||
|  | ||||
|     if (direction == FILE_TRANSFER_SEND) | ||||
|         return new_file_sender(window, friendnum, filenum, type); | ||||
|     if (direction == FILE_TRANSFER_SEND) { | ||||
|         return new_file_sender(window, friendnumber, filenumber, type); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
| @@ -233,45 +251,59 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui | ||||
| void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, | ||||
|                          Notification sound_type) | ||||
| { | ||||
|     if (!ft) | ||||
|     if (!ft) { | ||||
|         return; | ||||
|  | ||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) | ||||
|         return; | ||||
|  | ||||
|     if (ft->file) | ||||
|         fclose(ft->file); | ||||
|  | ||||
|     if (CTRL >= 0) | ||||
|         tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL); | ||||
|  | ||||
|     if (message && self) { | ||||
|         if (self->active_box != -1 && sound_type != silent) | ||||
|             box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message); | ||||
|         else | ||||
|             box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message); | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message); | ||||
|     } | ||||
|  | ||||
|     memset(ft, 0, sizeof(struct FileTransfer)); | ||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (ft->file) { | ||||
|         fclose(ft->file); | ||||
|     } | ||||
|  | ||||
|     if (CTRL >= 0) { | ||||
|         tox_file_control(m, ft->friendnumber, ft->filenumber, (Tox_File_Control) CTRL, NULL); | ||||
|     } | ||||
|  | ||||
|     if (message && self) { | ||||
|         if (self->active_box != -1 && sound_type != silent) { | ||||
|             box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message); | ||||
|         } else { | ||||
|             box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message); | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", message); | ||||
|     } | ||||
|  | ||||
|     clear_file_transfer(ft); | ||||
| } | ||||
|  | ||||
| /* Kills all active file transfers for friendnum */ | ||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum) | ||||
| /* Kills active outgoing avatar file transfers for friendnumber */ | ||||
| void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber) | ||||
| { | ||||
|     size_t i; | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; | ||||
|  | ||||
|     for (i = 0; i < MAX_FILES; ++i) { | ||||
|         close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         if (ft->file_type == TOX_FILE_KIND_AVATAR) { | ||||
|             close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Kills all active file transfers for friendnumber */ | ||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber) | ||||
| { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void kill_all_file_transfers(Tox *m) | ||||
| { | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < Friends.max_idx; ++i) | ||||
|     for (size_t i = 0; i < Friends.max_idx; ++i) { | ||||
|         kill_all_file_transfers_friend(m, Friends.list[i].num); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,9 +25,9 @@ | ||||
|  | ||||
| #include <limits.h> | ||||
|  | ||||
| #include "notify.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "notify.h" | ||||
|  | ||||
| #define KiB 1024 | ||||
| #define MiB 1048576       /* 1024^2 */ | ||||
| @@ -51,18 +51,16 @@ struct FileTransfer { | ||||
|     ToxWindow *window; | ||||
|     FILE *file; | ||||
|     FILE_TRANSFER_STATE state; | ||||
|     FILE_TRANSFER_DIRECTION direction; | ||||
|     uint8_t file_type; | ||||
|     char file_name[TOX_MAX_FILENAME_LENGTH + 1]; | ||||
|     char file_path[PATH_MAX + 1];    /* Not used by senders */ | ||||
|     double   bps; | ||||
|     uint32_t filenum; | ||||
|     uint32_t friendnum; | ||||
|     uint32_t filenumber; | ||||
|     uint32_t friendnumber; | ||||
|     size_t   index; | ||||
|     uint64_t file_size; | ||||
|     uint64_t position; | ||||
|     time_t   last_line_progress;   /* The last time we updated the progress bar */ | ||||
|     time_t   last_keep_alive;  /* The last time we sent or received data */ | ||||
|     uint32_t line_id; | ||||
|     uint8_t  file_id[TOX_FILE_ID_LENGTH]; | ||||
| }; | ||||
| @@ -75,24 +73,24 @@ void init_progress_bar(char *progline); | ||||
| void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id); | ||||
|  | ||||
| /* refreshes active file transfer status bars. */ | ||||
| void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum); | ||||
| void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber); | ||||
|  | ||||
| /* Returns a pointer to friendnum's FileTransfer struct associated with filenum. | ||||
|  * Returns NULL if filenum is invalid. | ||||
| /* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber. | ||||
|  * Returns NULL if filenumber is invalid. | ||||
|  */ | ||||
| struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum); | ||||
| struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber); | ||||
|  | ||||
|  | ||||
| /* Returns a pointer to the FileTransfer struct associated with index with the direction specified. | ||||
|  * Returns NULL on failure. | ||||
|  */ | ||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, | ||||
| struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index, | ||||
|         FILE_TRANSFER_DIRECTION direction); | ||||
|  | ||||
| /* Initializes an unused file transfer and returns its pointer. | ||||
|  * Returns NULL on failure. | ||||
|  */ | ||||
| struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, | ||||
| struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, | ||||
|                                        FILE_TRANSFER_DIRECTION direction, uint8_t type); | ||||
|  | ||||
| /* Closes file transfer ft. | ||||
| @@ -103,9 +101,12 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui | ||||
| void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, | ||||
|                          Notification sound_type); | ||||
|  | ||||
| /* Kills all active file transfers for friendnum */ | ||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum); | ||||
| /* Kills active outgoing avatar file transfers for friendnumber */ | ||||
| void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber); | ||||
|  | ||||
| /* Kills all active file transfers for friendnumber */ | ||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber); | ||||
|  | ||||
| void kill_all_file_transfers(Tox *m); | ||||
|  | ||||
| #endif  /* #define FILE_TRANSFERS_H */ | ||||
| #endif /* FILE_TRANSFERS_H */ | ||||
|   | ||||
							
								
								
									
										618
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										618
									
								
								src/friendlist.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -25,9 +25,9 @@ | ||||
|  | ||||
| #include <time.h> | ||||
|  | ||||
| #include "file_transfers.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "file_transfers.h" | ||||
|  | ||||
| struct LastOnline { | ||||
|     uint64_t last_on; | ||||
| @@ -35,7 +35,7 @@ struct LastOnline { | ||||
|     char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */ | ||||
| }; | ||||
|  | ||||
| struct GroupChatInvite { | ||||
| struct ConferenceInvite { | ||||
|     char *key; | ||||
|     uint16_t length; | ||||
|     uint8_t type; | ||||
| @@ -44,20 +44,20 @@ struct GroupChatInvite { | ||||
|  | ||||
| typedef struct { | ||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|     int namelength; | ||||
|     uint16_t namelength; | ||||
|     char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; | ||||
|     size_t statusmsg_len; | ||||
|     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint32_t num; | ||||
|     int chatwin; | ||||
|     bool active; | ||||
|     TOX_CONNECTION connection_status; | ||||
|     Tox_Connection connection_status; | ||||
|     bool is_typing; | ||||
|     bool logging_on;    /* saves preference for friend irrespective of global settings */ | ||||
|     uint8_t status; | ||||
|     Tox_User_Status status; | ||||
|  | ||||
|     struct LastOnline last_online; | ||||
|     struct GroupChatInvite group_invite; | ||||
|     struct ConferenceInvite conference_invite; | ||||
|  | ||||
|     struct FileTransfer file_receiver[MAX_FILES]; | ||||
|     struct FileTransfer file_sender[MAX_FILES]; | ||||
| @@ -65,7 +65,7 @@ typedef struct { | ||||
|  | ||||
| typedef struct { | ||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|     int namelength; | ||||
|     uint16_t namelength; | ||||
|     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint32_t num; | ||||
|     bool active; | ||||
| @@ -81,14 +81,24 @@ typedef struct { | ||||
|     ToxicFriend *list; | ||||
| } FriendsList; | ||||
|  | ||||
| ToxWindow new_friendlist(void); | ||||
| ToxWindow *new_friendlist(void); | ||||
| void friendlist_onInit(ToxWindow *self, Tox *m); | ||||
| void disable_chatwin(uint32_t f_num); | ||||
| int get_friendnum(uint8_t *name); | ||||
| int load_blocklist(char *data); | ||||
| void kill_friendlist(void); | ||||
| void kill_friendlist(ToxWindow *self); | ||||
| void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort); | ||||
| Tox_User_Status get_friend_status(uint32_t friendnumber); | ||||
| Tox_Connection get_friend_connection_status(uint32_t friendnumber); | ||||
|  | ||||
| /* sorts friendlist_index first by connection status then alphabetically */ | ||||
| void sort_friendlist_index(void); | ||||
|  | ||||
| /* | ||||
|  * Returns true if friend associated with `public_key` is in the block list. | ||||
|  * | ||||
|  * `public_key` must be at least TOX_PUBLIC_KEY_SIZE bytes. | ||||
|  */ | ||||
| bool friend_is_blocked(const char *public_key); | ||||
|  | ||||
| #endif /* end of include guard: FRIENDLIST_H */ | ||||
|   | ||||
| @@ -23,20 +23,20 @@ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "misc_tools.h" | ||||
| #include "friendlist.h" | ||||
| #include "log.h" | ||||
| #include "line_info.h" | ||||
| #include "groupchat.h" | ||||
| #include "prompt.h" | ||||
| #include "help.h" | ||||
| #include "term_mplex.h" | ||||
| #include "avatars.h" | ||||
| #include "conference.h" | ||||
| #include "friendlist.h" | ||||
| #include "help.h" | ||||
| #include "line_info.h" | ||||
| #include "log.h" | ||||
| #include "misc_tools.h" | ||||
| #include "name_lookup.h" | ||||
| #include "prompt.h" | ||||
| #include "qr_code.h" | ||||
| #include "term_mplex.h" | ||||
| #include "toxic.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern char *DATA_FILE; | ||||
| extern ToxWindow *prompt; | ||||
| @@ -46,54 +46,58 @@ extern FriendRequests FrndRequests; | ||||
| /* command functions */ | ||||
| void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     long int req = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!FrndRequests.request[req].active) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_FRIEND_ADD err; | ||||
|     Tox_Err_Friend_Add err; | ||||
|     uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_FRIEND_ADD_OK) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err); | ||||
|         return; | ||||
|     } else { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); | ||||
|         on_friendadded(m, friendnum, true); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); | ||||
|         on_friend_added(m, friendnum, true); | ||||
|     } | ||||
|  | ||||
|     memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); | ||||
|     FrndRequests.request[req] = (struct friend_request) { | ||||
|         0 | ||||
|     }; | ||||
|  | ||||
|     int i; | ||||
|  | ||||
|     for (i = FrndRequests.max_idx; i > 0; --i) { | ||||
|         if (FrndRequests.request[i - 1].active) | ||||
|         if (FrndRequests.request[i - 1].active) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     FrndRequests.max_idx = i; | ||||
|     --FrndRequests.num_requests; | ||||
|  | ||||
| } | ||||
|  | ||||
| void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg) | ||||
| { | ||||
|     const char *errmsg; | ||||
|  | ||||
|     TOX_ERR_FRIEND_ADD err; | ||||
|     uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err); | ||||
|     Tox_Err_Friend_Add err; | ||||
|     uint32_t f_num = tox_friend_add(m, (const uint8_t *) id_bin, (const uint8_t *) msg, strlen(msg), &err); | ||||
|  | ||||
|     switch (err) { | ||||
|         case TOX_ERR_FRIEND_ADD_TOO_LONG: | ||||
| @@ -126,24 +130,26 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_OK: | ||||
|             errmsg = "Friend request sent."; | ||||
|             on_friendadded(m, f_num, true); | ||||
|             on_friend_added(m, f_num, true); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_NULL: | ||||
|  | ||||
|         /* fallthrough */ | ||||
|         default: | ||||
|             errmsg = "Faile to add friend: Unknown error."; | ||||
|             errmsg = "Failed to add friend: Unknown error."; | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
| } | ||||
|  | ||||
| void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -152,7 +158,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|  | ||||
|     if (argc > 1) { | ||||
|         if (argv[2][0] != '\"') { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -186,13 +192,18 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|             xx[2] = '\0'; | ||||
|  | ||||
|             if (sscanf(xx, "%02x", &x) != 1) { | ||||
|                 line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID."); | ||||
|                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID."); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             id_bin[i] = x; | ||||
|         } | ||||
|  | ||||
|         if (friend_is_blocked(id_bin)) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         cmd_add_helper(self, m, id_bin, msg); | ||||
|     } else {    /* assume id is a username@domain address and do http name server lookup */ | ||||
|         name_lookup(self, m, id_bin, id, msg); | ||||
| @@ -201,24 +212,20 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX | ||||
|  | ||||
| void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (argc < 2 || strlen(argv[1]) < 3) { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc != 1 || strlen(argv[1]) < 3) { | ||||
|         avatar_unset(m); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argv[1][0] != '\"') { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* remove opening and closing quotes */ | ||||
|     char path[MAX_STR_SIZE]; | ||||
|     snprintf(path, sizeof(path), "%s", &argv[1][1]); | ||||
|     int len = strlen(path) - 1; | ||||
|     snprintf(path, sizeof(path), "%s", argv[1]); | ||||
|     int len = strlen(path); | ||||
|  | ||||
|     if (len <= 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid path."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -227,25 +234,31 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
|     get_file_name(filename, sizeof(filename), path); | ||||
|  | ||||
|     if (avatar_set(m, path, len) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||
|                       "Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.", | ||||
|                       MAX_AVATAR_FILE_SIZE); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename); | ||||
| } | ||||
|  | ||||
| void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     line_info_clear(self->chatwin->hst); | ||||
|     force_refresh(window); | ||||
| } | ||||
|  | ||||
| void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc != 3) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -256,32 +269,32 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | ||||
|     long int port = strtol(port_str, NULL, 10); | ||||
|  | ||||
|     if (port <= 0 || port > MAX_PORT_RANGE) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; | ||||
|  | ||||
|     if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     TOX_ERR_BOOTSTRAP err; | ||||
|     Tox_Err_Bootstrap err; | ||||
|     tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err); | ||||
|     tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err); | ||||
|  | ||||
|     switch (err) { | ||||
|         case TOX_ERR_BOOTSTRAP_BAD_HOST: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP."); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_BOOTSTRAP_BAD_PORT: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port."); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_BOOTSTRAP_NULL: | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
| @@ -291,155 +304,181 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) | ||||
|  | ||||
| void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     long int req = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!FrndRequests.request[req].active) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); | ||||
|     FrndRequests.request[req] = (struct friend_request) { | ||||
|         0 | ||||
|     }; | ||||
|  | ||||
|     int i; | ||||
|  | ||||
|     for (i = FrndRequests.max_idx; i > 0; --i) { | ||||
|         if (FrndRequests.request[i - 1].active) | ||||
|         if (FrndRequests.request[i - 1].active) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     FrndRequests.max_idx = i; | ||||
|     --FrndRequests.num_requests; | ||||
| } | ||||
|  | ||||
| void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     uint8_t type; | ||||
|  | ||||
|     if (!strcasecmp(argv[1], "audio")) | ||||
|         type = TOX_GROUPCHAT_TYPE_AV; | ||||
|     else if (!strcasecmp(argv[1], "text")) | ||||
|         type = TOX_GROUPCHAT_TYPE_TEXT; | ||||
|     else { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio"); | ||||
|     if (!strcasecmp(argv[1], "audio")) { | ||||
|         type = TOX_CONFERENCE_TYPE_AV; | ||||
|     } else if (!strcasecmp(argv[1], "text")) { | ||||
|         type = TOX_CONFERENCE_TYPE_TEXT; | ||||
|     } else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int groupnum = -1; | ||||
|     uint32_t conferencenum = 0; | ||||
|  | ||||
|     if (type == TOX_GROUPCHAT_TYPE_TEXT) | ||||
|         groupnum = tox_add_groupchat(m); | ||||
|     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||
|         Tox_Err_Conference_New err; | ||||
|  | ||||
|     /*#ifdef AUDIO | ||||
|         else | ||||
|             groupnum = toxav_add_av_groupchat(m, NULL, NULL); | ||||
|     #endif*/ | ||||
|         conferencenum = tox_conference_new(m, &err); | ||||
|  | ||||
|     if (groupnum == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize."); | ||||
|         if (err != TOX_ERR_CONFERENCE_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; | ||||
|         } | ||||
|  | ||||
| #else | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||
|         return; | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); | ||||
|         tox_conference_delete(m, conferencenum, NULL); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (init_groupchat_win(prompt, m, groupnum, type) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); | ||||
|         tox_del_groupchat(m, groupnum); | ||||
|         return; | ||||
| #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."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum); | ||||
| #endif | ||||
|  | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum); | ||||
| } | ||||
|  | ||||
| void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     const char *msg; | ||||
|     struct chatlog *log = self->chatwin->log; | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         if (log->log_on) | ||||
|         if (log->log_on) { | ||||
|             msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)"; | ||||
|         else | ||||
|         } else { | ||||
|             msg = "Logging for this window is OFF; type \"/log on\" to enable."; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *swch = argv[1]; | ||||
|  | ||||
|     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { | ||||
|         char myid[TOX_ADDRESS_SIZE]; | ||||
|         tox_self_get_address(m, (uint8_t *) myid); | ||||
|  | ||||
|         int log_ret = -1; | ||||
|  | ||||
|         if (self->is_chat) { | ||||
|             Friends.list[self->num].logging_on = true; | ||||
|             log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT); | ||||
|         } else if (self->is_prompt) { | ||||
|             log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT); | ||||
|         } else if (self->is_groupchat) { | ||||
|             log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP); | ||||
|         } | ||||
|  | ||||
|         msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log."; | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
|     } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { | ||||
|         if (self->is_chat) | ||||
|         if (self->type == WINDOW_TYPE_CHAT) { | ||||
|             Friends.list[self->num].logging_on = false; | ||||
|         } | ||||
|  | ||||
|         log_disable(log); | ||||
|  | ||||
|         msg = "Logging disabled."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
| } | ||||
|  | ||||
| void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||
|     char bin_id[TOX_ADDRESS_SIZE]; | ||||
|     tox_self_get_address(m, (uint8_t *) bin_id); | ||||
|  | ||||
|     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string); | ||||
| } | ||||
|  | ||||
| #ifdef QRCODE | ||||
| void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||
|     char bin_id[TOX_ADDRESS_SIZE]; | ||||
|     tox_self_get_address(m, (uint8_t *) bin_id); | ||||
|  | ||||
|     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -449,68 +488,97 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | ||||
|     nick[nick_len] = '\0'; | ||||
|  | ||||
|     size_t data_file_len = strlen(DATA_FILE); | ||||
|     char dir[data_file_len + 1]; | ||||
|     char *dir = malloc(data_file_len + 1); | ||||
|  | ||||
|     if (dir == NULL) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir); | ||||
|  | ||||
| #ifdef QRPNG | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'"); | ||||
|         free(dir); | ||||
|         return; | ||||
|     } else if (!strcmp(argv[1], "txt")) { | ||||
|  | ||||
| #endif /* QRPNG */ | ||||
|         char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1]; | ||||
|         snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); | ||||
|         size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT); | ||||
|         char *qr_path = malloc(qr_path_buf_size); | ||||
|  | ||||
|         if (ID_to_QRcode_txt(id_string, qr_path) == -1) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|         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; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||
|         snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); | ||||
|  | ||||
|         if (ID_to_QRcode_txt(id_string, qr_path) == -1) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|             free(dir); | ||||
|             free(qr_path); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||
|  | ||||
|         free(qr_path); | ||||
|  | ||||
| #ifdef QRPNG | ||||
|     } else if (!strcmp(argv[1], "png")) { | ||||
|         char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1]; | ||||
|         snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); | ||||
|         size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT_PNG); | ||||
|         char *qr_path = malloc(qr_path_buf_size); | ||||
|  | ||||
|         if (ID_to_QRcode_png(id_string, qr_path) == -1) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|         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; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||
|         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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]); | ||||
|         free(dir); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #endif /* QRPNG */ | ||||
|  | ||||
|     free(dir); | ||||
| } | ||||
| #endif /* QRCODE */ | ||||
|  | ||||
| void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char nick[MAX_STR_SIZE]; | ||||
|     size_t len = 0; | ||||
|  | ||||
|     if (argv[1][0] == '\"') {    /* remove opening and closing quotes */ | ||||
|         snprintf(nick, sizeof(nick), "%s", &argv[1][1]); | ||||
|         len = strlen(nick) - 1; | ||||
|         nick[len] = '\0'; | ||||
|     } else { | ||||
|         snprintf(nick, sizeof(nick), "%s", argv[1]); | ||||
|         len = strlen(nick); | ||||
|     } | ||||
|     snprintf(nick, sizeof(nick), "%s", argv[1]); | ||||
|     size_t len = strlen(nick); | ||||
|  | ||||
|     if (!valid_nick(nick)) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid name."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -525,24 +593,14 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | ||||
|  | ||||
| void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argv[1][0] != '\"') { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* remove opening and closing quotes and replace linebreaks with spaces */ | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|     snprintf(msg, sizeof(msg), "%s", &argv[1][1]); | ||||
|     int len = strlen(msg) - 1; | ||||
|     msg[len] = '\0'; | ||||
|     strsubst(msg, '\n', ' '); | ||||
|  | ||||
|     prompt_update_statusmessage(prompt, m, msg); | ||||
|     prompt_update_statusmessage(prompt, m, argv[1]); | ||||
| } | ||||
|  | ||||
| void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| @@ -553,7 +611,7 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
|         nospam = strtol(argv[1], NULL, 16); | ||||
|  | ||||
|         if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value."); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| @@ -561,29 +619,44 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
|     uint32_t old_nospam = tox_self_get_nospam(m); | ||||
|     tox_self_set_nospam(m, (uint32_t) nospam); | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:"); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:"); | ||||
|     cmd_myid(window, self, m, 0, NULL); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, | ||||
|     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, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'", | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'", | ||||
|                   old_nospam); | ||||
| } | ||||
|  | ||||
| void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     help_init_menu(self); | ||||
| } | ||||
|  | ||||
| void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|     UNUSED_VAR(self); | ||||
|  | ||||
|     exit_toxic_success(m); | ||||
| } | ||||
|  | ||||
| void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     if (FrndRequests.num_requests == 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -591,8 +664,9 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | ||||
|     int count = 0; | ||||
|  | ||||
|     for (i = 0; i < FrndRequests.max_idx; ++i) { | ||||
|         if (!FrndRequests.request[i].active) | ||||
|         if (!FrndRequests.request[i].active) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0}; | ||||
|  | ||||
| @@ -602,62 +676,49 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv | ||||
|             strcat(id, d); | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg); | ||||
|  | ||||
|         if (++count < FrndRequests.num_requests) | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|         if (++count < FrndRequests.num_requests) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     bool have_note = false; | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     const char *errmsg; | ||||
|  | ||||
|     lock_status (); | ||||
|     lock_status(); | ||||
|  | ||||
|     if (argc >= 2) { | ||||
|         have_note = true; | ||||
|     } else if (argc < 1) { | ||||
|     if (argc < 1) { | ||||
|         errmsg = "Require a status. Statuses are: online, busy and away."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     const char *status_str = argv[1]; | ||||
|     TOX_USER_STATUS status; | ||||
|     Tox_User_Status status; | ||||
|  | ||||
|     if (!strcasecmp(status_str, "online")) | ||||
|     if (!strcasecmp(status_str, "online")) { | ||||
|         status = TOX_USER_STATUS_NONE; | ||||
|     else if (!strcasecmp(status_str, "away")) | ||||
|     } else if (!strcasecmp(status_str, "away")) { | ||||
|         status = TOX_USER_STATUS_AWAY; | ||||
|     else if (!strcasecmp(status_str, "busy")) | ||||
|     } else if (!strcasecmp(status_str, "busy")) { | ||||
|         status = TOX_USER_STATUS_BUSY; | ||||
|     else { | ||||
|     } else { | ||||
|         errmsg = "Invalid status. Valid statuses are: online, busy and away."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     tox_self_set_status(m, status); | ||||
|     prompt_update_status(prompt, status); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str); | ||||
|  | ||||
|     if (have_note) { | ||||
|         if (argv[2][0] != '\"') { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes."); | ||||
|             goto finish; | ||||
|         } | ||||
|  | ||||
|         /* remove opening and closing quotes */ | ||||
|         char msg[MAX_STR_SIZE]; | ||||
|         snprintf(msg, sizeof(msg), "%s", &argv[2][1]); | ||||
|         int len = strlen(msg) - 1; | ||||
|         msg[len] = '\0'; | ||||
|  | ||||
|         prompt_update_statusmessage(prompt, m, msg); | ||||
|     } | ||||
|  | ||||
| finish: | ||||
|     unlock_status (); | ||||
|     unlock_status(); | ||||
| } | ||||
|   | ||||
| @@ -23,8 +23,8 @@ | ||||
| #ifndef GLOBAL_COMMANDS_H | ||||
| #define GLOBAL_COMMANDS_H | ||||
|  | ||||
| #include "windows.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| @@ -32,10 +32,12 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ | ||||
| void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #ifdef QRCODE | ||||
| void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* QRCODE */ | ||||
| void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| @@ -56,4 +58,8 @@ void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv) | ||||
| void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| #endif /* #define GLOBAL_COMMANDS_H */ | ||||
| #ifdef PYTHON | ||||
| void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| #endif /* GLOBAL_COMMANDS_H */ | ||||
|   | ||||
| @@ -1,79 +0,0 @@ | ||||
| /*  group_commands.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "log.h" | ||||
|  | ||||
| void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     char title[MAX_STR_SIZE]; | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH); | ||||
|  | ||||
|         if (tlen != -1) { | ||||
|             title[tlen] = '\0'; | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); | ||||
|         } else { | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set"); | ||||
|         } | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argv[1][0] != '\"') { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* remove opening and closing quotes */ | ||||
|     snprintf(title, sizeof(title), "%s", &argv[1][1]); | ||||
|     int len = strlen(title) - 1; | ||||
|     title[len] = '\0'; | ||||
|  | ||||
|     if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     set_window_title(self, title, len); | ||||
|  | ||||
|     char timefrmt[TIME_STR_SIZE]; | ||||
|     char selfnick[TOX_MAX_NAME_LENGTH]; | ||||
|  | ||||
|     get_time_str(timefrmt, sizeof(timefrmt)); | ||||
|  | ||||
|     tox_self_get_name(m, (uint8_t *) selfnick); | ||||
|     size_t sn_len = tox_self_get_name_size(m); | ||||
|     selfnick[sn_len] = '\0'; | ||||
|  | ||||
|     line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title); | ||||
|  | ||||
|     char tmp_event[MAX_STR_SIZE]; | ||||
|     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); | ||||
|     write_to_log(tmp_event, selfnick, self->chatwin->log, true); | ||||
| } | ||||
							
								
								
									
										923
									
								
								src/groupchat.c
									
									
									
									
									
								
							
							
						
						
									
										923
									
								
								src/groupchat.c
									
									
									
									
									
								
							| @@ -1,923 +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 | user_settings->bell_on_message, 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 | user_settings->bell_on_message, 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; | ||||
|     time_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 (ctx->pastemode && key == '\r') | ||||
|         key = '\n'; | ||||
|  | ||||
|     if (ltr || key == '\n') {    /* 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 == '\r') { | ||||
|         rm_trailing_spaces_buf(ctx); | ||||
|  | ||||
|         if (!wstring_is_empty(ctx->line)) { | ||||
|             add_line_to_hist(ctx); | ||||
|  | ||||
|             wstrsubst(ctx->line, L'¶', L'\n'); | ||||
|  | ||||
|             char line[MAX_STR_SIZE]; | ||||
|  | ||||
|             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) | ||||
|                 memset(&line, 0, sizeof(line)); | ||||
|  | ||||
|             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 (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); | ||||
|  | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         int num_peers = groupchats[self->num].num_peers; | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|         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); | ||||
|  | ||||
|             pthread_mutex_lock(&Winthread.lock); | ||||
|             int peer = i + groupchats[self->num].side_pos; | ||||
|             pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|             /* truncate nick to fit in side panel without modifying list */ | ||||
|             char tmpnck[TOX_MAX_NAME_LENGTH]; | ||||
|             int maxlen = SIDEBAR_WIDTH - 2; | ||||
|  | ||||
|             pthread_mutex_lock(&Winthread.lock); | ||||
|             memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen); | ||||
|             pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|             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 */ | ||||
|     time_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 */ | ||||
							
								
								
									
										178
									
								
								src/help.c
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								src/help.c
									
									
									
									
									
								
							| @@ -22,24 +22,34 @@ | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| #include "windows.h" | ||||
| #include "toxic.h" | ||||
| #include "help.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #include "api.h" | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #define HELP_MENU_HEIGHT 10 | ||||
| #else | ||||
| #define HELP_MENU_HEIGHT 9 | ||||
| #endif /* PYTHON */ | ||||
| #define HELP_MENU_WIDTH 26 | ||||
|  | ||||
| void help_init_menu(ToxWindow *self) | ||||
| { | ||||
|     if (self->help->win) | ||||
|     if (self->help->win) { | ||||
|         delwin(self->help->win); | ||||
|     } | ||||
|  | ||||
|     int y2, x2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) | ||||
|     if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3); | ||||
|     self->help->active = true; | ||||
| @@ -49,19 +59,24 @@ void help_init_menu(ToxWindow *self) | ||||
| static void help_exit(ToxWindow *self) | ||||
| { | ||||
|     delwin(self->help->win); | ||||
|     memset(self->help, 0, sizeof(Help)); | ||||
|  | ||||
|     *(self->help) = (struct Help) { | ||||
|         0 | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static void help_init_window(ToxWindow *self, int height, int width) | ||||
| { | ||||
|     if (self->help->win) | ||||
|     if (self->help->win) { | ||||
|         delwin(self->help->win); | ||||
|     } | ||||
|  | ||||
|     int y2, x2; | ||||
|     getmaxyx(stdscr, y2, x2); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) | ||||
|     if (y2 <= 0 || x2 <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     height = MIN(height, y2); | ||||
|     width = MIN(width, x2); | ||||
| @@ -89,11 +104,18 @@ static void help_draw_menu(ToxWindow *self) | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "hat commands\n"); | ||||
|  | ||||
|     wprintw(win, " g"); | ||||
|     wprintw(win, " c"); | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "r"); | ||||
|     wprintw(win, "o"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "oup commands\n"); | ||||
|     wprintw(win, "nference commands\n"); | ||||
|  | ||||
| #ifdef PYTHON | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, " p"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "lugin commands\n"); | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, " f"); | ||||
| @@ -112,14 +134,15 @@ static void help_draw_menu(ToxWindow *self) | ||||
|     wprintw(win, "it menu\n"); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wrefresh(win); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| static void help_draw_bottom_menu(WINDOW *win) | ||||
| { | ||||
|     int y2, x2; | ||||
|     getmaxyx(win, y2, x2); | ||||
|     (void) x2; | ||||
|  | ||||
|     UNUSED_VAR(x2); | ||||
|  | ||||
|     wmove(win, y2 - 2, 1); | ||||
|  | ||||
| @@ -156,13 +179,15 @@ static void help_draw_global(ToxWindow *self) | ||||
|     wprintw(win, "  /nick <nick>               : Set your nickname\n"); | ||||
|     wprintw(win, "  /nospam <value>            : Change part of your Tox ID to stop spam\n"); | ||||
|     wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n"); | ||||
|     wprintw(win, "  /group <type>              : Create a group chat where type: text | audio\n"); | ||||
|     wprintw(win, "  /conference <type>         : Create a conference where type: text | audio\n"); | ||||
|     wprintw(win, "  /myid                      : Print your Tox ID\n"); | ||||
| #ifdef QRCODE | ||||
| #ifdef QRPNG | ||||
|     wprintw(win, "  /myqr <txt> or <png>       : Print your Tox ID's QR code to a file.\n"); | ||||
| #else | ||||
|     wprintw(win, "  /myqr                      : Print your Tox ID's QR code to a file.\n"); | ||||
| #endif /* QRPNG */ | ||||
| #endif /* QRCODE */ | ||||
|     wprintw(win, "  /clear                     : Clear window history\n"); | ||||
|     wprintw(win, "  /close                     : Close the current chat window\n"); | ||||
|     wprintw(win, "  /quit or /exit             : Exit Toxic\n"); | ||||
| @@ -185,10 +210,18 @@ static void help_draw_global(ToxWindow *self) | ||||
|     wprintw(win, "  /svdev <type> <id>         : Set active video device\n"); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| #ifdef PYTHON | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, "\n Scripting:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|  | ||||
|     wprintw(win, "  /run <path>                : Load and run the script at path\n"); | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wrefresh(win); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| static void help_draw_chat(ToxWindow *self) | ||||
| @@ -201,8 +234,8 @@ static void help_draw_chat(ToxWindow *self) | ||||
|     wprintw(win, "Chat Commands:\n"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|  | ||||
|     wprintw(win, "  /invite <n>                : Invite contact to a group chat\n"); | ||||
|     wprintw(win, "  /join                      : Join a pending group chat\n"); | ||||
|     wprintw(win, "  /invite <n>                : Invite contact to a conference \n"); | ||||
|     wprintw(win, "  /join                      : Join a pending conference\n"); | ||||
|     wprintw(win, "  /sendfile <path>           : Send a file\n"); | ||||
|     wprintw(win, "  /savefile <id>             : Receive a file\n"); | ||||
|     wprintw(win, "  /cancel <type> <id>        : Cancel file transfer where type: in|out\n"); | ||||
| @@ -219,19 +252,22 @@ static void help_draw_chat(ToxWindow *self) | ||||
|     wprintw(win, "  /sdev <type> <id>          : Change active device\n"); | ||||
|     wprintw(win, "  /mute <type>               : Mute active device if in call\n"); | ||||
|     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n"); | ||||
|     wprintw(win, "  /bitrate <n>               : Set the audio encoding bitrate\n"); | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #ifdef VIDEO | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, "\n Video:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|     wprintw(win, "  /video                     : Toggle video call\n"); | ||||
|     wprintw(win, "  /res <width> <height>      : Set video resolution\n"); | ||||
|     wprintw(win, "  /vcall                     : Video call\n"); | ||||
|     wprintw(win, "  /video                     : Toggle video in call\n"); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wrefresh(win); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| static void help_draw_keys(ToxWindow *self) | ||||
| @@ -248,8 +284,8 @@ static void help_draw_keys(ToxWindow *self) | ||||
|     wprintw(win, "  Page Up and Page Down     : Scroll window history one line\n"); | ||||
|     wprintw(win, "  Ctrl+F and Ctrl+V         : Scroll window history half a page\n"); | ||||
|     wprintw(win, "  Ctrl+H                    : Move to the bottom of window history\n"); | ||||
|     wprintw(win, "  Ctrl+[ and Ctrl+]         : Scroll peer list in groupchats\n"); | ||||
|     wprintw(win, "  Ctrl+B                    : Toggle the groupchat peerlist\n"); | ||||
|     wprintw(win, "  Ctrl+up and Ctrl+down     : Scroll peer list in conference\n"); | ||||
|     wprintw(win, "  Ctrl+B                    : Toggle the conference peerlist\n"); | ||||
|     wprintw(win, "  Ctrl+J                    : Insert new line\n"); | ||||
|     wprintw(win, "  Ctrl+T                    : Toggle paste mode\n\n"); | ||||
|     wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n"); | ||||
| @@ -257,27 +293,57 @@ static void help_draw_keys(ToxWindow *self) | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wrefresh(win); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| static void help_draw_group(ToxWindow *self) | ||||
| static void help_draw_conference(ToxWindow *self) | ||||
| { | ||||
|     WINDOW *win = self->help->win; | ||||
|  | ||||
|     wmove(win, 1, 1); | ||||
|  | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|     wprintw(win, "Group commands:\n"); | ||||
|     wprintw(win, "Conference commands:\n"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|  | ||||
|     wprintw(win, "  /title <msg>               : Set group title (show current title if no msg)\n\n"); | ||||
|     wprintw(win, "  /title <msg>               : Show/set conference title\n"); | ||||
| #ifdef AUDIO | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, "\n Audio:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|     wprintw(win, "  /audio <on> or <off>       : Enable/disable audio in an audio conference\n"); | ||||
|     wprintw(win, "  /mute                      : Toggle self audio mute status\n"); | ||||
|     wprintw(win, "  /mute <nick> or <pubkey>   : Toggle peer audio mute status\n"); | ||||
|     wprintw(win, "  /ptt <on> or <off>         : Toggle audio input Push-To-Talk (F2 to activate)\n"); | ||||
|     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n\n"); | ||||
| #endif | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wrefresh(win); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| #ifdef PYTHON | ||||
| static void help_draw_plugin(ToxWindow *self) | ||||
| { | ||||
|     WINDOW *win = self->help->win; | ||||
|  | ||||
|     wmove(win, 1, 1); | ||||
|  | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|     wprintw(win, "Plugin commands:\n"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|  | ||||
|     draw_handler_help(win); | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| static void help_draw_contacts(ToxWindow *self) | ||||
| { | ||||
|     WINDOW *win = self->help->win; | ||||
| @@ -297,55 +363,72 @@ static void help_draw_contacts(ToxWindow *self) | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wrefresh(win); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| void help_onKey(ToxWindow *self, wint_t key) | ||||
| { | ||||
|     int height; | ||||
|  | ||||
|     switch (key) { | ||||
|         case 'x': | ||||
|         case L'x': | ||||
|         case T_KEY_ESC: | ||||
|             help_exit(self); | ||||
|             break; | ||||
|  | ||||
|         case 'c': | ||||
|         case L'c': | ||||
| #ifdef VIDEO | ||||
|             help_init_window(self, 22, 80); | ||||
|             help_init_window(self, 25, 80); | ||||
| #elif AUDIO | ||||
|             help_init_window(self, 19, 80); | ||||
|             help_init_window(self, 20, 80); | ||||
| #else | ||||
|             help_init_window(self, 10, 80); | ||||
| #endif | ||||
|             self->help->type = HELP_CHAT; | ||||
|             break; | ||||
|  | ||||
|         case 'g': | ||||
|         case L'g': | ||||
|             height = 22; | ||||
| #ifdef VIDEO | ||||
|             help_init_window(self, 30, 80); | ||||
|             height += 8; | ||||
| #elif AUDIO | ||||
|             help_init_window(self, 26, 80); | ||||
| #else | ||||
|             help_init_window(self, 22, 80); | ||||
|             height += 4; | ||||
| #endif | ||||
| #ifdef PYTHON | ||||
|             height += 2; | ||||
| #endif | ||||
|             help_init_window(self, height, 80); | ||||
|             self->help->type = HELP_GLOBAL; | ||||
|             break; | ||||
|  | ||||
|         case 'r': | ||||
|             help_init_window(self, 6, 80); | ||||
|             self->help->type = HELP_GROUP; | ||||
|         case L'o': | ||||
|             height = 6; | ||||
| #ifdef AUDIO | ||||
|             height += 7; | ||||
| #endif | ||||
|             help_init_window(self, height, 80); | ||||
|             self->help->type = HELP_CONFERENCE; | ||||
|             break; | ||||
|  | ||||
|         case 'f': | ||||
| #ifdef PYTHON | ||||
|  | ||||
|         case L'p': | ||||
|             help_init_window(self, 4 + num_registered_handlers(), help_max_width()); | ||||
|             self->help->type = HELP_PLUGIN; | ||||
|             break; | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
|         case L'f': | ||||
|             help_init_window(self, 10, 80); | ||||
|             self->help->type = HELP_CONTACTS; | ||||
|             break; | ||||
|  | ||||
|         case 'k': | ||||
|         case L'k': | ||||
|             help_init_window(self, 15, 80); | ||||
|             self->help->type = HELP_KEYS; | ||||
|             break; | ||||
|  | ||||
|         case 'm': | ||||
|         case L'm': | ||||
|             help_init_menu(self); | ||||
|             self->help->type = HELP_MENU; | ||||
|             break; | ||||
| @@ -354,8 +437,6 @@ void help_onKey(ToxWindow *self, wint_t key) | ||||
|  | ||||
| void help_onDraw(ToxWindow *self) | ||||
| { | ||||
|     curs_set(0); | ||||
|  | ||||
|     switch (self->help->type) { | ||||
|         case HELP_MENU: | ||||
|             help_draw_menu(self); | ||||
| @@ -377,8 +458,15 @@ void help_onDraw(ToxWindow *self) | ||||
|             help_draw_contacts(self); | ||||
|             break; | ||||
|  | ||||
|         case HELP_GROUP: | ||||
|             help_draw_group(self); | ||||
|         case HELP_CONFERENCE: | ||||
|             help_draw_conference(self); | ||||
|             break; | ||||
|  | ||||
| #ifdef PYTHON | ||||
|  | ||||
|         case HELP_PLUGIN: | ||||
|             help_draw_plugin(self); | ||||
|             break; | ||||
| #endif /* PYTHON */ | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,13 +30,16 @@ typedef enum { | ||||
|     HELP_MENU, | ||||
|     HELP_GLOBAL, | ||||
|     HELP_CHAT, | ||||
|     HELP_GROUP, | ||||
|     HELP_CONFERENCE, | ||||
|     HELP_KEYS, | ||||
|     HELP_CONTACTS, | ||||
| #ifdef PYTHON | ||||
|     HELP_PLUGIN, | ||||
| #endif | ||||
| } HELP_TYPES; | ||||
|  | ||||
| void help_onDraw(ToxWindow *self); | ||||
| void help_init_menu(ToxWindow *self); | ||||
| void help_onKey(ToxWindow *self, wint_t key); | ||||
|  | ||||
| #endif /* #define HELP_H */ | ||||
| #endif /* HELP_H */ | ||||
|   | ||||
							
								
								
									
										115
									
								
								src/input.c
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								src/input.c
									
									
									
									
									
								
							| @@ -26,25 +26,26 @@ | ||||
|  | ||||
| #include <wchar.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "conference.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "groupchat.h" | ||||
| #include "settings.h" | ||||
| #include "toxic.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| /* add a char to input field and buffer */ | ||||
| void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
| void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     /* this is the only place we need to do this check */ | ||||
|     if (key == '\n') | ||||
|     if (key == '\n') { | ||||
|         key = L'¶'; | ||||
|     } | ||||
|  | ||||
|     int cur_len = wcwidth(key); | ||||
|  | ||||
| @@ -77,42 +78,45 @@ static void input_backspace(ToxWindow *self, int x, int mx_x) | ||||
|     int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0; | ||||
|     int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0; | ||||
|  | ||||
|     if (ctx->start && (x >= mx_x - cur_len)) | ||||
|     if (ctx->start && (x >= mx_x - cur_len)) { | ||||
|         ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); | ||||
|     else if (ctx->start) | ||||
|     } else if (ctx->start) { | ||||
|         ctx->start = MAX(0, ctx->start - cur_len); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* delete a char via delete key from input field and buffer */ | ||||
| static void input_delete(ToxWindow *self) | ||||
| { | ||||
|     if (del_char_buf_frnt(self->chatwin) == -1) | ||||
|     if (del_char_buf_frnt(self->chatwin) == -1) { | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* delete last typed word */ | ||||
| static void input_del_word(ToxWindow *self, int x, int mx_x) | ||||
| static void input_del_word(ToxWindow *self) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (del_word_buf(ctx) == -1) { | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* deletes entire line before cursor from input field and buffer */ | ||||
| static void input_discard(ToxWindow *self) | ||||
| { | ||||
|     if (discard_buf(self->chatwin) == -1) | ||||
|     if (discard_buf(self->chatwin) == -1) { | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* deletes entire line after cursor from input field and buffer */ | ||||
| static void input_kill(ChatContext *ctx) | ||||
| { | ||||
|     if (kill_buf(ctx) == -1) | ||||
|     if (kill_buf(ctx) == -1) { | ||||
|         sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void input_yank(ToxWindow *self, int x, int mx_x) | ||||
| @@ -134,7 +138,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x) | ||||
| } | ||||
|  | ||||
| /* moves cursor/line position to end of line in input field and buffer */ | ||||
| static void input_mv_end(ToxWindow *self, int y, int mx_x) | ||||
| static void input_mv_end(ToxWindow *self, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
| @@ -149,8 +153,9 @@ static void input_mv_home(ToxWindow *self) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos <= 0) | ||||
|     if (ctx->pos <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     ctx->pos = 0; | ||||
|     ctx->start = 0; | ||||
| @@ -161,18 +166,44 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos <= 0) | ||||
|     if (ctx->pos <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0; | ||||
|     int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0; | ||||
|  | ||||
|     --ctx->pos; | ||||
|  | ||||
|     if (ctx->start && (x >= mx_x - cur_len)) | ||||
|     if (ctx->start > 0 && (x >= mx_x - cur_len)) { | ||||
|         int s_len = wcwidth(ctx->line[ctx->start - 1]); | ||||
|         ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); | ||||
|     else if (ctx->start) | ||||
|     } else if (ctx->start > 0) { | ||||
|         ctx->start = MAX(0, ctx->start - cur_len); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* moves the cursor to the beginning of the previous word in input field and buffer */ | ||||
| static void input_skip_left(ToxWindow *self, int x, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int count = 0; | ||||
|  | ||||
|     do { | ||||
|         --ctx->pos; | ||||
|         count += wcwidth(ctx->line[ctx->pos]); | ||||
|     } while (ctx->pos > 0 && (ctx->line[ctx->pos - 1] != L' ' || ctx->line[ctx->pos] == L' ')); | ||||
|  | ||||
|     if (ctx->start > 0 && (x >= mx_x - count)) { | ||||
|         int s_len = wcwidth(ctx->line[ctx->start - 1]); | ||||
|         ctx->start = MAX(0, ctx->start - 1 + (s_len - count)); | ||||
|     } else if (ctx->start > 0) { | ||||
|         ctx->start = MAX(0, ctx->start - count); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* moves cursor/line position right in input field and buffer */ | ||||
| @@ -180,8 +211,9 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos >= ctx->len) | ||||
|     if (ctx->pos >= ctx->len) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     ++ctx->pos; | ||||
|  | ||||
| @@ -193,6 +225,29 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* moves the cursor to the end of the next word in input field and buffer */ | ||||
| static void input_skip_right(ToxWindow *self, int x, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos >= ctx->len) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int count = 0; | ||||
|  | ||||
|     do { | ||||
|         count += wcwidth(ctx->line[ctx->pos]); | ||||
|         ++ctx->pos; | ||||
|     } while (ctx->pos < ctx->len && !(ctx->line[ctx->pos] == L' ' && ctx->line[ctx->pos - 1] != L' ')); | ||||
|  | ||||
|     int newpos = x + count; | ||||
|  | ||||
|     if (newpos >= mx_x) { | ||||
|         ctx->start += (1 + (newpos - mx_x)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* puts a line history item in input field and buffer */ | ||||
| static void input_history(ToxWindow *self, wint_t key, int mx_x) | ||||
| { | ||||
| @@ -205,7 +260,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x) | ||||
|  | ||||
| /* Handles non-printable input keys that behave the same for all types of chat windows. | ||||
|    return true if key matches a function, false otherwise */ | ||||
| bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
| bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x) | ||||
| { | ||||
|     bool match = true; | ||||
|  | ||||
| @@ -232,7 +287,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
|             break; | ||||
|  | ||||
|         case T_KEY_C_W: | ||||
|             input_del_word(self, x, mx_x); | ||||
|             input_del_word(self); | ||||
|             break; | ||||
|  | ||||
|         case KEY_HOME: | ||||
| @@ -242,7 +297,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
|  | ||||
|         case KEY_END: | ||||
|         case T_KEY_C_E: | ||||
|             input_mv_end(self, y, mx_x); | ||||
|             input_mv_end(self, mx_x); | ||||
|             break; | ||||
|  | ||||
|         case KEY_LEFT: | ||||
| @@ -262,6 +317,14 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
|             force_refresh(self->chatwin->history); | ||||
|             break; | ||||
|  | ||||
|         case T_KEY_C_LEFT: | ||||
|             input_skip_left(self, x, mx_x); | ||||
|             break; | ||||
|  | ||||
|         case T_KEY_C_RIGHT: | ||||
|             input_skip_right(self, x, mx_x); | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|             match = false; | ||||
|             break; | ||||
| @@ -271,9 +334,9 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) | ||||
|        maybe convert entire function to if/else and make them all customizable keys? */ | ||||
|     if (!match) { | ||||
|         if (key == user_settings->key_toggle_peerlist) { | ||||
|             if (self->is_groupchat) { | ||||
|             if (self->type == WINDOW_TYPE_CONFERENCE) { | ||||
|                 self->show_peerlist ^= 1; | ||||
|                 redraw_groupchat_win(self); | ||||
|                 redraw_conference_win(self); | ||||
|             } | ||||
|  | ||||
|             match = true; | ||||
|   | ||||
| @@ -24,10 +24,10 @@ | ||||
| #define INPUT_H | ||||
|  | ||||
| /* add a char to input field and buffer for given chatcontext */ | ||||
| void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y); | ||||
| void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x); | ||||
|  | ||||
| /* Handles non-printable input keys that behave the same for all types of chat windows. | ||||
|    return true if key matches a function, false otherwise */ | ||||
| bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y); | ||||
| bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x); | ||||
|  | ||||
| #endif /* #define INPUT_H */ | ||||
| #endif /* INPUT_H */ | ||||
|   | ||||
							
								
								
									
										607
									
								
								src/line_info.c
									
									
									
									
									
								
							
							
						
						
									
										607
									
								
								src/line_info.c
									
									
									
									
									
								
							| @@ -20,19 +20,19 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdarg.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdarg.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "conference.h" | ||||
| #include "line_info.h" | ||||
| #include "groupchat.h" | ||||
| #include "settings.h" | ||||
| #include "notify.h" | ||||
| #include "message_queue.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "settings.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| @@ -40,12 +40,13 @@ void line_info_init(struct history *hst) | ||||
| { | ||||
|     hst->line_root = calloc(1, sizeof(struct line_info)); | ||||
|  | ||||
|     if (hst->line_root == NULL) | ||||
|     if (hst->line_root == NULL) { | ||||
|         exit_toxic_err("failed in line_info_init", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     hst->line_start = hst->line_root; | ||||
|     hst->line_end = hst->line_start; | ||||
|     hst->queue_sz = 0; | ||||
|     hst->queue_size = 0; | ||||
| } | ||||
|  | ||||
| /* resets line_start (moves to end of chat history) */ | ||||
| @@ -53,26 +54,28 @@ void line_info_reset_start(ToxWindow *self, struct history *hst) | ||||
| { | ||||
|     struct line_info *line = hst->line_end; | ||||
|  | ||||
|     if (line->prev == NULL) | ||||
|     if (line == NULL || line->prev == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int y2, x2; | ||||
|     int y2; | ||||
|     int x2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|     UNUSED_VAR(x2); | ||||
|  | ||||
|     int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; | ||||
|     int top_offst = self->is_chat || self->is_prompt ? 2 : 0; | ||||
|     int max_y = (y2 - CHATBOX_HEIGHT - top_offst); | ||||
|     int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||
|     int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst; | ||||
|  | ||||
|     int curlines = 0; | ||||
|     int nxtlines = line->newlines + (line->len / (x2 - side_offst)); | ||||
|     uint16_t curlines = 0; | ||||
|  | ||||
|     do { | ||||
|         curlines += 1 + nxtlines; | ||||
|         curlines += line->format_lines; | ||||
|         line = line->prev; | ||||
|         nxtlines = line->newlines + (line->len / (x2 - side_offst)); | ||||
|     } while (line->prev && curlines + nxtlines < max_y); | ||||
|     } while (line->prev && curlines + line->format_lines <= max_y); | ||||
|  | ||||
|     hst->line_start = line; | ||||
|  | ||||
|     self->scroll_pause = false; | ||||
| } | ||||
|  | ||||
| void line_info_cleanup(struct history *hst) | ||||
| @@ -85,11 +88,10 @@ void line_info_cleanup(struct history *hst) | ||||
|         tmp1 = tmp2; | ||||
|     } | ||||
|  | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < hst->queue_sz; ++i) { | ||||
|         if (hst->queue[i]) | ||||
|     for (size_t i = 0; i < hst->queue_size; ++i) { | ||||
|         if (hst->queue[i]) { | ||||
|             free(hst->queue[i]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     free(hst); | ||||
| @@ -114,39 +116,250 @@ static void line_info_root_fwd(struct history *hst) | ||||
| /* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */ | ||||
| static struct line_info *line_info_ret_queue(struct history *hst) | ||||
| { | ||||
|     if (hst->queue_sz <= 0) | ||||
|     if (hst->queue_size == 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     struct line_info *line = hst->queue[0]; | ||||
|  | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < hst->queue_sz; ++i) | ||||
|     for (size_t i = 0; i < hst->queue_size; ++i) { | ||||
|         hst->queue[i] = hst->queue[i + 1]; | ||||
|     } | ||||
|  | ||||
|     --hst->queue_sz; | ||||
|     --hst->queue_size; | ||||
|  | ||||
|     return line; | ||||
| } | ||||
|  | ||||
| /* creates new line_info line and puts it in the queue. */ | ||||
| void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, | ||||
|                    uint8_t bold, uint8_t colour, const char *msg, ...) | ||||
| /* Prints a maximum of `n` chars from `s` to `win`. | ||||
|  * | ||||
|  * Return 1 if the string contains a newline byte. | ||||
|  * Return 0 if string does not contain a newline byte. | ||||
|  * Return -1 if printing was aborted. | ||||
|  */ | ||||
| static int print_n_chars(WINDOW *win, const char *s, size_t n, int max_y) | ||||
| { | ||||
|     if (!self) | ||||
|         return; | ||||
|     bool newline = false; | ||||
|     char ch; | ||||
|  | ||||
|     for (size_t i = 0; i < n && (ch = s[i]); ++i) { | ||||
|         if (ch == '\n') { | ||||
|             newline = true; | ||||
|  | ||||
|             int x; | ||||
|             int y; | ||||
|             UNUSED_VAR(x); | ||||
|             getyx(win, y, x); | ||||
|  | ||||
|             // make sure cursor will wrap correctly after newline to prevent display bugs | ||||
|             if (y + 1 >= max_y) { | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (win) { | ||||
|             wprintw(win, "%c", ch); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return newline; | ||||
| } | ||||
|  | ||||
| /* Returns the index of the last space character in `s` found before `limit`. | ||||
|  * Returns -1 if no space is found. | ||||
|  */ | ||||
| static int rspace_index(const char *s, int limit) | ||||
| { | ||||
|     for (int i = limit; i >= 0; --i) { | ||||
|         if (s[i] == ' ') { | ||||
|             return i; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| /* Returns the first index in `s` containing a newline byte found before `limit`. | ||||
|  * Returns -1 if no newline  is found. | ||||
|  */ | ||||
| static int newline_index(const char *s, int limit) | ||||
| { | ||||
|     char ch; | ||||
|  | ||||
|     for (int i = 0; i < limit && (ch = s[i]); ++i) { | ||||
|         if (ch == '\n') { | ||||
|             return i; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| /* Returns the number of newline bytes in `s` */ | ||||
| static unsigned int newline_count(const char *s) | ||||
| { | ||||
|     char ch; | ||||
|     unsigned int count = 0; | ||||
|  | ||||
|     for (size_t i = 0; (ch = s[i]); ++i) { | ||||
|         if (ch == '\n') { | ||||
|             ++count; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return count; | ||||
| } | ||||
|  | ||||
| /* Prints `line` message to window, wrapping at the last word that fits on the current line. | ||||
|  * This function updates the `format_lines` field of `line` according to current window dimensions. | ||||
|  * | ||||
|  * If `win` is null nothing will be printed to the window. This is useful to set the | ||||
|  * `format_lines` field on initialization. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 if not all characters in line's message were printed to screen. | ||||
|  */ | ||||
| static int print_wrap(WINDOW *win, struct line_info *line, int max_x, int max_y) | ||||
| { | ||||
|     int x; | ||||
|     int y; | ||||
|     UNUSED_VAR(y); | ||||
|  | ||||
|     const char *msg = line->msg; | ||||
|     uint16_t length = line->msg_len; | ||||
|     uint16_t lines = 0; | ||||
|     const int x_start = line->len - line->msg_len - 1;  // manually keep track of x position because ncurses sucks | ||||
|     int x_limit = max_x - x_start; | ||||
|  | ||||
|     if (x_limit <= 1) { | ||||
|         fprintf(stderr, "Warning: x_limit <= 0 in print_wrap(): %d\n", x_limit); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     while (msg) { | ||||
|         getyx(win, y, x); | ||||
|  | ||||
|         // next line would print past window limit so we abort; we don't want to update format_lines | ||||
|         if (x > x_start) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         if (length < x_limit) { | ||||
|             int p_ret = print_n_chars(win, msg, length, max_y); | ||||
|  | ||||
|             if (p_ret == 1) { | ||||
|                 lines += newline_count(msg); | ||||
|             } else if (p_ret == -1) { | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             ++lines; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         int newline_idx = newline_index(msg, x_limit - 1); | ||||
|  | ||||
|         if (newline_idx >= 0) { | ||||
|             if (print_n_chars(win, msg, newline_idx + 1, max_y) == -1) { | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             msg += (newline_idx + 1); | ||||
|             length -= (newline_idx + 1); | ||||
|             x_limit = max_x; // if we find a newline we stop adding column padding for rest of message | ||||
|             ++lines; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         int space_idx = rspace_index(msg, x_limit - 1); | ||||
|  | ||||
|         if (space_idx >= 1) { | ||||
|             if (print_n_chars(win, msg, space_idx, max_y) == -1) { | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             msg += space_idx + 1; | ||||
|             length -= (space_idx + 1); | ||||
|  | ||||
|             if (win) { | ||||
|                 waddch(win, '\n'); | ||||
|             } | ||||
|         } else { | ||||
|             if (print_n_chars(win, msg, x_limit, max_y) == -1) { | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             msg += x_limit; | ||||
|             length -= x_limit; | ||||
|         } | ||||
|  | ||||
|         // Add padding to the start of the next line | ||||
|         if (win && x_limit < max_x) { | ||||
|             for (size_t i = 0; i < x_start; ++i) { | ||||
|                 waddch(win, ' '); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         ++lines; | ||||
|     } | ||||
|  | ||||
|     if (win && line->noread_flag) { | ||||
|         getyx(win, y, x); | ||||
|  | ||||
|         if (x >= max_x - 1 || x == x_start) { | ||||
|             ++lines; | ||||
|         } | ||||
|  | ||||
|         wattron(win, COLOR_PAIR(RED)); | ||||
|         wprintw(win, " x"); | ||||
|         wattroff(win, COLOR_PAIR(RED)); | ||||
|     } | ||||
|  | ||||
|     line->format_lines = lines; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void line_info_init_line(ToxWindow *self, struct line_info *line) | ||||
| { | ||||
|     int y2; | ||||
|     int x2; | ||||
|     UNUSED_VAR(y2); | ||||
|  | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT; | ||||
|     const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2; | ||||
|  | ||||
|     print_wrap(NULL, line, max_x, max_y); | ||||
| } | ||||
|  | ||||
| /* creates new line_info line and puts it in the queue. | ||||
|  * | ||||
|  * Returns the id of the new line. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type, | ||||
|                   uint8_t bold, uint8_t colour, const char *msg, ...) | ||||
| { | ||||
|     if (!self) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     struct history *hst = self->chatwin->hst; | ||||
|  | ||||
|     if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) | ||||
|         return; | ||||
|     if (hst->queue_size >= MAX_LINE_INFO_QUEUE) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     struct line_info *new_line = calloc(1, sizeof(struct line_info)); | ||||
|  | ||||
|     if (new_line == NULL) | ||||
|     if (new_line == NULL) { | ||||
|         exit_toxic_err("failed in line_info_add", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0}; | ||||
|     char frmt_msg[MAX_LINE_INFO_MSG_SIZE]; | ||||
|     frmt_msg[0] = 0; | ||||
|  | ||||
|     va_list args; | ||||
|     va_start(args, msg); | ||||
| @@ -161,33 +374,33 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons | ||||
|  | ||||
|         /* fallthrough */ | ||||
|         case OUT_ACTION: | ||||
|             len += strlen(user_settings->line_normal) + 2; | ||||
|             len += strlen(user_settings->line_normal) + 2; // two spaces | ||||
|             break; | ||||
|  | ||||
|         case IN_MSG: | ||||
|  | ||||
|         /* fallthrough */ | ||||
|         case OUT_MSG: | ||||
|             len += strlen(user_settings->line_normal) + 3; | ||||
|             len += strlen(user_settings->line_normal) + 3; // two spaces and a ':' char | ||||
|             break; | ||||
|  | ||||
|         case CONNECTION: | ||||
|             len += strlen(user_settings->line_join) + 2; | ||||
|             len += strlen(user_settings->line_join) + 2;  // two spaces | ||||
|             break; | ||||
|  | ||||
|         case DISCONNECTION: | ||||
|             len += strlen(user_settings->line_quit) + 2; | ||||
|             len += strlen(user_settings->line_quit) + 2;  // two spaces | ||||
|             break; | ||||
|  | ||||
|         case SYS_MSG: | ||||
|             break; | ||||
|  | ||||
|         case NAME_CHANGE: | ||||
|             len += strlen(user_settings->line_alert) + 1; | ||||
|             len += strlen(user_settings->line_alert) + 2;  // two spaces | ||||
|             break; | ||||
|  | ||||
|         case PROMPT: | ||||
|             ++len; | ||||
|             len += 2;  // '$' char and a space | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
| @@ -195,20 +408,16 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     uint16_t msg_len = 0; | ||||
|  | ||||
|     if (frmt_msg[0]) { | ||||
|         snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg); | ||||
|         len += strlen(new_line->msg); | ||||
|  | ||||
|         int i; | ||||
|  | ||||
|         for (i = 0; frmt_msg[i]; ++i) { | ||||
|             if (frmt_msg[i] == '\n') | ||||
|                 ++new_line->newlines; | ||||
|         } | ||||
|         msg_len = strlen(new_line->msg); | ||||
|         len += msg_len; | ||||
|     } | ||||
|  | ||||
|     if (timestr) { | ||||
|         snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr); | ||||
|     if (show_timestamp) { | ||||
|         get_time_str(new_line->timestr, sizeof(new_line->timestr)); | ||||
|         len += strlen(new_line->timestr) + 1; | ||||
|     } | ||||
|  | ||||
| @@ -222,14 +431,20 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons | ||||
|         len += strlen(new_line->name2); | ||||
|     } | ||||
|  | ||||
|     new_line->id = (hst->line_end->id + 1 + hst->queue_size) % INT_MAX; | ||||
|     new_line->len = len; | ||||
|     new_line->msg_len = msg_len; | ||||
|     new_line->type = type; | ||||
|     new_line->bold = bold; | ||||
|     new_line->colour = colour; | ||||
|     new_line->noread_flag = false; | ||||
|     new_line->timestamp = get_unix_time(); | ||||
|  | ||||
|     hst->queue[hst->queue_sz++] = new_line; | ||||
|     line_info_init_line(self, new_line); | ||||
|  | ||||
|     hst->queue[hst->queue_size++] = new_line; | ||||
|  | ||||
|     return new_line->id; | ||||
| } | ||||
|  | ||||
| /* adds a single queue item to hst if possible. only called once per call to line_info_print() */ | ||||
| @@ -238,36 +453,21 @@ static void line_info_check_queue(ToxWindow *self) | ||||
|     struct history *hst = self->chatwin->hst; | ||||
|     struct line_info *line = line_info_ret_queue(hst); | ||||
|  | ||||
|     if (line == NULL) | ||||
|     if (line == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (hst->start_id > user_settings->history_size) | ||||
|     if (hst->start_id > user_settings->history_size) { | ||||
|         line_info_root_fwd(hst); | ||||
|     } | ||||
|  | ||||
|     line->id = hst->line_end->id + 1; | ||||
|     line->prev = hst->line_end; | ||||
|     hst->line_end->next = line; | ||||
|     hst->line_end = line; | ||||
|     hst->line_end->id = line->id; | ||||
|  | ||||
|     int y, y2, x, x2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|     getyx(self->chatwin->history, y, x); | ||||
|     (void) x; | ||||
|  | ||||
|     if (x2 <= SIDEBAR_WIDTH) | ||||
|         return; | ||||
|  | ||||
|     int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;   /* offset width of groupchat sidebar */ | ||||
|     int lines = 1 + line->newlines + (line->len / (x2 - offst)); | ||||
|     int max_y = y2 - CHATBOX_HEIGHT; | ||||
|  | ||||
|     /* move line_start forward proportionate to the number of new lines */ | ||||
|     if (y + lines - 1 >= max_y) { | ||||
|         while (lines > 0 && hst->line_start->next) { | ||||
|             lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst)); | ||||
|             hst->line_start = hst->line_start->next; | ||||
|             ++hst->start_id; | ||||
|         } | ||||
|     if (!self->scroll_pause) { | ||||
|         line_info_reset_start(self, hst); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -277,8 +477,9 @@ void line_info_print(ToxWindow *self) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx == NULL) | ||||
|     if (ctx == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     struct history *hst = ctx->hst; | ||||
|  | ||||
| @@ -286,22 +487,47 @@ void line_info_print(ToxWindow *self) | ||||
|     line_info_check_queue(self); | ||||
|  | ||||
|     WINDOW *win = ctx->history; | ||||
|  | ||||
|     wclear(win); | ||||
|     int y2, x2; | ||||
|  | ||||
|     int y2; | ||||
|  | ||||
|     int x2; | ||||
|  | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (x2 <= SIDEBAR_WIDTH) | ||||
|     if (x2 - 1 <= SIDEBAR_WIDTH) {  // leave room on x axis for sidebar padding | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (self->is_groupchat) | ||||
|     if (self->type == WINDOW_TYPE_CONFERENCE) { | ||||
|         wmove(win, 0, 0); | ||||
|     else | ||||
|         wmove(win, 2, 0); | ||||
|     } else { | ||||
|         wmove(win, TOP_BAR_HEIGHT, 0); | ||||
|     } | ||||
|  | ||||
|     struct line_info *line = hst->line_start->next; | ||||
|     int numlines = 0; | ||||
|  | ||||
|     while (line && numlines++ <= y2) { | ||||
|     if (!line) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT; | ||||
|     const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2; | ||||
|     uint16_t numlines = line->format_lines; | ||||
|     int print_ret = 0; | ||||
|  | ||||
|     while (line && numlines++ <= max_y && print_ret == 0) { | ||||
|         int y; | ||||
|         int x; | ||||
|         UNUSED_VAR(y); | ||||
|  | ||||
|         getyx(win, y, x); | ||||
|  | ||||
|         if (x > 0) { // Prevents us from printing off the screen | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         uint8_t type = line->type; | ||||
|  | ||||
|         switch (type) { | ||||
| @@ -318,49 +544,42 @@ void line_info_print(ToxWindow *self) | ||||
|  | ||||
|                 int nameclr = GREEN; | ||||
|  | ||||
|                 if (line->colour) | ||||
|                 if (line->colour) { | ||||
|                     nameclr = line->colour; | ||||
|                 else if (type == IN_MSG) | ||||
|                 } else if (type == IN_MSG) { | ||||
|                     nameclr = CYAN; | ||||
|                 } | ||||
|  | ||||
|                 wattron(win, COLOR_PAIR(nameclr)); | ||||
|                 wprintw(win, "%s %s: ", user_settings->line_normal, line->name1); | ||||
|                 wattroff(win, COLOR_PAIR(nameclr)); | ||||
|  | ||||
|                 char *msg = line->msg; | ||||
|  | ||||
|                 while (msg) { | ||||
|                     char *line = strsep(&msg, "\n"); | ||||
|  | ||||
|                     if (line[0] == '>') | ||||
|                         wattron(win, COLOR_PAIR(GREEN)); | ||||
|                     else if (line[0] == '<') | ||||
|                         wattron(win, COLOR_PAIR(RED)); | ||||
|  | ||||
|                     wprintw(win, "%s%c", line, msg ? '\n' : '\0'); | ||||
|  | ||||
|                     if (line[0] == '>') | ||||
|                         wattroff(win, COLOR_PAIR(GREEN)); | ||||
|                     else if (line[0] == '<') | ||||
|                         wattroff(win, COLOR_PAIR(RED)); | ||||
|  | ||||
|                     // change the \0 set by strsep back to \n | ||||
|                     if (msg) | ||||
|                         msg[-1] = '\n'; | ||||
|                 if (line->msg[0] == 0) { | ||||
|                     waddch(win, '\n'); | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|                 if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||
|                 if (line->msg[0] == '>') { | ||||
|                     wattron(win, COLOR_PAIR(GREEN)); | ||||
|                 } else if (line->msg[0] == '<') { | ||||
|                     wattron(win, COLOR_PAIR(RED)); | ||||
|                     wprintw(win, " x", line->msg); | ||||
|                     wattroff(win, COLOR_PAIR(RED)); | ||||
|                 } | ||||
|  | ||||
|                     if (line->noread_flag == false) { | ||||
|                 print_ret = print_wrap(win, line, max_x, max_y); | ||||
|  | ||||
|                 if (line->msg[0] == '>') { | ||||
|                     wattroff(win, COLOR_PAIR(GREEN)); | ||||
|                 } else if (line->msg[0] == '<') { | ||||
|                     wattroff(win, COLOR_PAIR(RED)); | ||||
|                 } | ||||
|  | ||||
|                 if (type == OUT_MSG && !line->read_flag) { | ||||
|                     if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||
|                         line->noread_flag = true; | ||||
|                         line->len += 2; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 wprintw(win, "\n", line->msg); | ||||
|                 waddch(win, '\n'); | ||||
|                 break; | ||||
|  | ||||
|             case OUT_ACTION_READ: | ||||
| @@ -375,21 +594,17 @@ void line_info_print(ToxWindow *self) | ||||
|                 wattroff(win, COLOR_PAIR(BLUE)); | ||||
|  | ||||
|                 wattron(win, COLOR_PAIR(YELLOW)); | ||||
|                 wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg); | ||||
|                 wprintw(win, "%s %s ", user_settings->line_normal, line->name1); | ||||
|                 print_ret = print_wrap(win, line, max_x, max_y); | ||||
|                 wattroff(win, COLOR_PAIR(YELLOW)); | ||||
|  | ||||
|                 if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||
|                     wattron(win, COLOR_PAIR(RED)); | ||||
|                     wprintw(win, " x", line->msg); | ||||
|                     wattroff(win, COLOR_PAIR(RED)); | ||||
|  | ||||
|                     if (line->noread_flag == false) { | ||||
|                 if (type == OUT_ACTION && !line->read_flag) { | ||||
|                     if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { | ||||
|                         line->noread_flag = true; | ||||
|                         line->len += 2; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 wprintw(win, "\n", line->msg); | ||||
|                 waddch(win, '\n'); | ||||
|                 break; | ||||
|  | ||||
|             case SYS_MSG: | ||||
| @@ -399,19 +614,24 @@ void line_info_print(ToxWindow *self) | ||||
|                     wattroff(win, COLOR_PAIR(BLUE)); | ||||
|                 } | ||||
|  | ||||
|                 if (line->bold) | ||||
|                 if (line->bold) { | ||||
|                     wattron(win, A_BOLD); | ||||
|                 } | ||||
|  | ||||
|                 if (line->colour) | ||||
|                 if (line->colour) { | ||||
|                     wattron(win, COLOR_PAIR(line->colour)); | ||||
|                 } | ||||
|  | ||||
|                 wprintw(win, "%s\n", line->msg); | ||||
|                 print_ret = print_wrap(win, line, max_x, max_y); | ||||
|                 waddch(win, '\n'); | ||||
|  | ||||
|                 if (line->bold) | ||||
|                 if (line->bold) { | ||||
|                     wattroff(win, A_BOLD); | ||||
|                 } | ||||
|  | ||||
|                 if (line->colour) | ||||
|                 if (line->colour) { | ||||
|                     wattroff(win, COLOR_PAIR(line->colour)); | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|  | ||||
| @@ -420,10 +640,11 @@ void line_info_print(ToxWindow *self) | ||||
|                 wprintw(win, "$ "); | ||||
|                 wattroff(win, COLOR_PAIR(GREEN)); | ||||
|  | ||||
|                 if (line->msg[0]) | ||||
|                     wprintw(win, "%s", line->msg); | ||||
|                 if (line->msg[0]) { | ||||
|                     print_ret = print_wrap(win, line, max_x, max_y); | ||||
|                 } | ||||
|  | ||||
|                 wprintw(win, "\n"); | ||||
|                 waddch(win, '\n'); | ||||
|                 break; | ||||
|  | ||||
|             case CONNECTION: | ||||
| @@ -438,7 +659,9 @@ void line_info_print(ToxWindow *self) | ||||
|                 wprintw(win, "%s ", line->name1); | ||||
|                 wattroff(win, A_BOLD); | ||||
|  | ||||
|                 wprintw(win, "%s\n", line->msg); | ||||
|                 print_ret = print_wrap(win, line, max_x, max_y); | ||||
|                 waddch(win, '\n'); | ||||
|  | ||||
|                 wattroff(win, COLOR_PAIR(line->colour)); | ||||
|  | ||||
|                 break; | ||||
| @@ -455,7 +678,9 @@ void line_info_print(ToxWindow *self) | ||||
|                 wprintw(win, "%s ", line->name1); | ||||
|                 wattroff(win, A_BOLD); | ||||
|  | ||||
|                 wprintw(win, "%s\n", line->msg); | ||||
|                 print_ret = print_wrap(win, line, max_x, max_y); | ||||
|                 waddch(win, '\n'); | ||||
|  | ||||
|                 wattroff(win, COLOR_PAIR(line->colour)); | ||||
|  | ||||
|                 break; | ||||
| @@ -471,7 +696,7 @@ void line_info_print(ToxWindow *self) | ||||
|                 wprintw(win, "%s", line->name1); | ||||
|                 wattroff(win, A_BOLD); | ||||
|  | ||||
|                 wprintw(win, "%s", line->msg); | ||||
|                 print_ret = print_wrap(win, line, max_x, max_y); | ||||
|  | ||||
|                 wattron(win, A_BOLD); | ||||
|                 wprintw(win, "%s\n", line->name2); | ||||
| @@ -485,8 +710,41 @@ void line_info_print(ToxWindow *self) | ||||
|     } | ||||
|  | ||||
|     /* keep calling until queue is empty */ | ||||
|     if (hst->queue_sz > 0) | ||||
|     if (hst->queue_size > 0) { | ||||
|         line_info_print(self); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return true if all lines starting from `line` can fit on the screen. | ||||
|  */ | ||||
| static bool line_info_screen_fit(ToxWindow *self, struct line_info *line) | ||||
| { | ||||
|     if (!line) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     int x2; | ||||
|     int y2; | ||||
|     getmaxyx(self->chatwin->history, y2, x2); | ||||
|  | ||||
|     UNUSED_VAR(x2); | ||||
|  | ||||
|     const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||
|     const int max_y = y2 - top_offset; | ||||
|  | ||||
|     uint16_t lines = line->format_lines; | ||||
|  | ||||
|     while (line) { | ||||
|         if (lines > max_y) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         lines += line->format_lines; | ||||
|         line = line->next; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| /* puts msg in specified line_info msg buffer */ | ||||
| @@ -496,6 +754,9 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg) | ||||
|  | ||||
|     while (line) { | ||||
|         if (line->id == id) { | ||||
|             size_t new_len = strlen(msg); | ||||
|             line->len = line->len - line->msg_len + new_len; | ||||
|             line->msg_len = new_len; | ||||
|             snprintf(line->msg, sizeof(line->msg), "%s", msg); | ||||
|             return; | ||||
|         } | ||||
| @@ -504,47 +765,75 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* static void line_info_goto_root(struct history *hst) | ||||
| static void line_info_scroll_up(ToxWindow *self, struct history *hst) | ||||
| { | ||||
|     hst->line_start = hst->line_root; | ||||
| } */ | ||||
|  | ||||
| static void line_info_scroll_up(struct history *hst) | ||||
| { | ||||
|     if (hst->line_start->prev) | ||||
|     if (hst->line_start->prev) { | ||||
|         hst->line_start = hst->line_start->prev; | ||||
|     else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||
|         self->scroll_pause = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void line_info_scroll_down(struct history *hst) | ||||
| static void line_info_scroll_down(ToxWindow *self, struct history *hst) | ||||
| { | ||||
|     if (hst->line_start->next) | ||||
|         hst->line_start = hst->line_start->next; | ||||
|     else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); | ||||
|     struct line_info *next = hst->line_start->next; | ||||
|  | ||||
|     if (next && self->scroll_pause) { | ||||
|         if (line_info_screen_fit(self, next->next)) { | ||||
|             line_info_reset_start(self, hst); | ||||
|         } else { | ||||
|             hst->line_start = next; | ||||
|         } | ||||
|     } else { | ||||
|         line_info_reset_start(self, hst); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void line_info_page_up(ToxWindow *self, struct history *hst) | ||||
| { | ||||
|     int x2, y2; | ||||
|     int x2; | ||||
|     int y2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|     (void) x2; | ||||
|     int jump_dist = y2 / 2; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < jump_dist && hst->line_start->prev; ++i) | ||||
|     UNUSED_VAR(x2); | ||||
|  | ||||
|     const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||
|     const int max_y = y2 - top_offset; | ||||
|     size_t jump_dist = max_y / 2; | ||||
|  | ||||
|     for (size_t i = 0; i < jump_dist && hst->line_start->prev; ++i) { | ||||
|         hst->line_start = hst->line_start->prev; | ||||
|     } | ||||
|  | ||||
|     self->scroll_pause = true; | ||||
| } | ||||
|  | ||||
| static void line_info_page_down(ToxWindow *self, struct history *hst) | ||||
| { | ||||
|     int x2, y2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|     (void) x2; | ||||
|     int jump_dist = y2 / 2; | ||||
|     int i; | ||||
|     if (!self->scroll_pause) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < jump_dist && hst->line_start->next; ++i) | ||||
|         hst->line_start = hst->line_start->next; | ||||
|     int x2; | ||||
|     int y2; | ||||
|     getmaxyx(self->chatwin->history, y2, x2); | ||||
|  | ||||
|     UNUSED_VAR(x2); | ||||
|  | ||||
|     const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; | ||||
|     const int max_y = y2 - top_offset; | ||||
|     size_t jump_dist = max_y / 2; | ||||
|  | ||||
|     struct line_info *next = hst->line_start->next; | ||||
|  | ||||
|     for (size_t i = 0; i < jump_dist && next; ++i) { | ||||
|         if (line_info_screen_fit(self, next->next)) { | ||||
|             line_info_reset_start(self, hst); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         hst->line_start = next; | ||||
|         next = hst->line_start->next; | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool line_info_onKey(ToxWindow *self, wint_t key) | ||||
| @@ -557,9 +846,9 @@ bool line_info_onKey(ToxWindow *self, wint_t key) | ||||
|     } else if (key == user_settings->key_half_page_down) { | ||||
|         line_info_page_down(self, hst); | ||||
|     } else if (key == user_settings->key_scroll_line_up) { | ||||
|         line_info_scroll_up(hst); | ||||
|         line_info_scroll_up(self, hst); | ||||
|     } else if (key == user_settings->key_scroll_line_down) { | ||||
|         line_info_scroll_down(hst); | ||||
|         line_info_scroll_down(self, hst); | ||||
|     } else if (key == user_settings->key_page_bottom) { | ||||
|         line_info_reset_start(self, hst); | ||||
|     } else { | ||||
|   | ||||
| @@ -23,15 +23,15 @@ | ||||
| #ifndef LINE_INFO_H | ||||
| #define LINE_INFO_H | ||||
|  | ||||
| #include "windows.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define MAX_HISTORY 100000 | ||||
| #define MIN_HISTORY 40 | ||||
| #define MAX_LINE_INFO_QUEUE 1024 | ||||
| #define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32    /* needs extra room for log loading */ | ||||
| #define MAX_LINE_INFO_MSG_SIZE (MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32) /* needs extra room for log loading */ | ||||
|  | ||||
| enum { | ||||
| typedef enum LINE_TYPE { | ||||
|     SYS_MSG, | ||||
|     IN_MSG, | ||||
|     OUT_MSG, | ||||
| @@ -54,10 +54,12 @@ struct line_info { | ||||
|     uint8_t type; | ||||
|     uint8_t bold; | ||||
|     uint8_t colour; | ||||
|     uint8_t noread_flag;   /* true if a line should be flagged as unread */ | ||||
|     bool    noread_flag;   /* true if a line should be flagged as unread */ | ||||
|     bool    read_flag;     /* true if a message has been flagged as read */ | ||||
|     uint32_t id; | ||||
|     uint16_t len;   /* combined len of entire line */ | ||||
|     uint8_t newlines; | ||||
|     uint16_t len;        /* combined length of entire line */ | ||||
|     uint16_t msg_len;    /* length of the message */ | ||||
|     uint16_t format_lines;  /* number of lines the combined string takes up (dynamically set) */ | ||||
|  | ||||
|     struct line_info *prev; | ||||
|     struct line_info *next; | ||||
| @@ -71,12 +73,16 @@ struct history { | ||||
|     uint32_t start_id;    /* keeps track of where line_start should be when at bottom of history */ | ||||
|  | ||||
|     struct line_info *queue[MAX_LINE_INFO_QUEUE]; | ||||
|     int queue_sz; | ||||
|     size_t queue_size; | ||||
| }; | ||||
|  | ||||
| /* creates new line_info line and puts it in the queue. */ | ||||
| void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, | ||||
|                    uint8_t bold, uint8_t colour, const char *msg, ...); | ||||
| /* creates new line_info line and puts it in the queue. | ||||
|  * | ||||
|  * Returns the id of the new line. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type, | ||||
|                   uint8_t bold, uint8_t colour, const char *msg, ...); | ||||
|  | ||||
| /* Prints a section of history starting at line_start */ | ||||
| void line_info_print(ToxWindow *self); | ||||
| @@ -96,4 +102,4 @@ void line_info_reset_start(ToxWindow *self, struct history *hst); | ||||
| void line_info_init(struct history *hst); | ||||
| bool line_info_onKey(ToxWindow *self, wint_t key);    /* returns true if key is a match */ | ||||
|  | ||||
| #endif /* #define LINE_INFO_H */ | ||||
| #endif /* LINE_INFO_H */ | ||||
|   | ||||
							
								
								
									
										314
									
								
								src/log.c
									
									
									
									
									
								
							
							
						
						
									
										314
									
								
								src/log.c
									
									
									
									
									
								
							| @@ -22,41 +22,46 @@ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <time.h> | ||||
| #include <sys/stat.h> | ||||
| #include <time.h> | ||||
|  | ||||
| #include "configdir.h" | ||||
| #include "line_info.h" | ||||
| #include "log.h" | ||||
| #include "misc_tools.h" | ||||
| #include "settings.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "misc_tools.h" | ||||
| #include "log.h" | ||||
| #include "settings.h" | ||||
| #include "line_info.h" | ||||
|  | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| /* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h) | ||||
|    A prompt log is in the format: LOGDIR/selfkey-home.log | ||||
|    A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log | ||||
|    A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log | ||||
|  | ||||
|    Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used. | ||||
|  | ||||
|    Returns 0 on success, -1 if the path is too long */ | ||||
| static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype) | ||||
| /* Creates a log path and puts it in `dest. | ||||
|  * | ||||
|  * There are two types of logs: chat logs and prompt logs (see LOG_TYPE in log.h) | ||||
|  * A prompt log is in the format: LOGDIR/selfkey-home.log | ||||
|  * A chat log is in the format: LOGDIR/selfkey-name-otherkey.log | ||||
|  * | ||||
|  * For friend chats `otherkey` is the first 6 bytes of the friend's Tox ID. | ||||
|  * For Conferences/groups `otherkey` is the first 6 bytes of the group's unique ID. | ||||
|  * | ||||
|  * Return path length on success. | ||||
|  * Return -1 if the path is too long. | ||||
|  */ | ||||
| static int get_log_path(char *dest, int destsize, const char *name, const char *selfkey, const char *otherkey) | ||||
| { | ||||
|     if (!valid_nick(name)) | ||||
|     if (!valid_nick(name)) { | ||||
|         name = UNKNOWN_NAME; | ||||
|     } | ||||
|  | ||||
|     const char *namedash = logtype == LOG_PROMPT ? "" : "-"; | ||||
|     const char *namedash = otherkey ? "-" : ""; | ||||
|     const char *set_path = user_settings->chatlogs_path; | ||||
|  | ||||
|     char *user_config_dir = get_user_config_dir(); | ||||
|     int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash); | ||||
|     path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR); | ||||
|  | ||||
|     /* first 6 digits of selfkey */ | ||||
|     char self_id[32]; | ||||
|     /* first 6 bytes of selfkey */ | ||||
|     char self_id[32] = {0}; | ||||
|     path_len += KEY_IDENT_DIGITS * 2; | ||||
|     sprintf(&self_id[0], "%02X", selfkey[0] & 0xff); | ||||
|     sprintf(&self_id[2], "%02X", selfkey[1] & 0xff); | ||||
| @@ -65,19 +70,13 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke | ||||
|  | ||||
|     char other_id[32] = {0}; | ||||
|  | ||||
|     switch (logtype) { | ||||
|         case LOG_CHAT: | ||||
|             path_len += KEY_IDENT_DIGITS * 2; | ||||
|             sprintf(&other_id[0], "%02X", otherkey[0] & 0xff); | ||||
|             sprintf(&other_id[2], "%02X", otherkey[1] & 0xff); | ||||
|             sprintf(&other_id[4], "%02X", otherkey[2] & 0xff); | ||||
|             other_id[KEY_IDENT_DIGITS * 2] = '\0'; | ||||
|             break; | ||||
|  | ||||
|         case LOG_GROUP: | ||||
|             strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time()); | ||||
|             path_len += strlen(other_id); | ||||
|             break; | ||||
|     if (otherkey) { | ||||
|         /* first 6 bytes of otherkey */ | ||||
|         path_len += KEY_IDENT_DIGITS * 2; | ||||
|         sprintf(&other_id[0], "%02X", otherkey[0] & 0xff); | ||||
|         sprintf(&other_id[2], "%02X", otherkey[1] & 0xff); | ||||
|         sprintf(&other_id[4], "%02X", otherkey[2] & 0xff); | ||||
|         other_id[KEY_IDENT_DIGITS * 2] = '\0'; | ||||
|     } | ||||
|  | ||||
|     if (path_len >= destsize) { | ||||
| @@ -85,32 +84,43 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (!string_is_empty(set_path)) | ||||
|     if (!string_is_empty(set_path)) { | ||||
|         snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id); | ||||
|     else | ||||
|     } else { | ||||
|         snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id); | ||||
|     } | ||||
|  | ||||
|     free(user_config_dir); | ||||
|  | ||||
|     return 0; | ||||
|     return path_len; | ||||
| } | ||||
|  | ||||
| /* Opens log file or creates a new one */ | ||||
| static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) | ||||
| /* Initializes log path for `log`. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| static int init_logging_session(const char *name, const char *selfkey, const char *otherkey, struct chatlog *log, | ||||
|                                 LOG_TYPE type) | ||||
| { | ||||
|     if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL)) | ||||
|     if (log == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (selfkey == NULL || (type == LOG_TYPE_CHAT && otherkey == NULL)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char log_path[MAX_STR_SIZE]; | ||||
|  | ||||
|     if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) | ||||
|         return -1; | ||||
|     int path_len = get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey); | ||||
|  | ||||
|     log->file = fopen(log_path, "a+"); | ||||
|     snprintf(log->path, sizeof(log->path), "%s", log_path); | ||||
|  | ||||
|     if (log->file == NULL) | ||||
|     if (path_len == -1 || path_len >= sizeof(log->path)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     memcpy(log->path, log_path, path_len); | ||||
|     log->path[path_len] = 0; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -119,8 +129,13 @@ static int init_logging_session(char *name, const char *selfkey, const char *oth | ||||
|  | ||||
| void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event) | ||||
| { | ||||
|     if (!log->log_on) | ||||
|     if (log == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!log->log_on) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (log->file == NULL) { | ||||
|         log->log_on = false; | ||||
| @@ -129,10 +144,11 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e | ||||
|  | ||||
|     char name_frmt[TOXIC_MAX_NAME_LENGTH + 3]; | ||||
|  | ||||
|     if (event) | ||||
|     if (event) { | ||||
|         snprintf(name_frmt, sizeof(name_frmt), "* %s", name); | ||||
|     else | ||||
|     } else { | ||||
|         snprintf(name_frmt, sizeof(name_frmt), "%s:", name); | ||||
|     } | ||||
|  | ||||
|     const char *t = user_settings->log_timestamp_format; | ||||
|     char s[MAX_STR_SIZE]; | ||||
| @@ -147,86 +163,162 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e | ||||
|  | ||||
| void log_disable(struct chatlog *log) | ||||
| { | ||||
|     if (log->file != NULL) | ||||
|         fclose(log->file); | ||||
|     if (log == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     memset(log, 0, sizeof(struct chatlog)); | ||||
|     if (log->file != NULL) { | ||||
|         fclose(log->file); | ||||
|         log->file = NULL; | ||||
|     } | ||||
|  | ||||
|     log->lastwrite = 0; | ||||
|     log->log_on = false; | ||||
| } | ||||
|  | ||||
| int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) | ||||
| int log_enable(struct chatlog *log) | ||||
| { | ||||
|     log->log_on = true; | ||||
|  | ||||
|     if (log->file != NULL) | ||||
|         return 0; | ||||
|  | ||||
|     if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) { | ||||
|         log_disable(log); | ||||
|     if (log == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (log->log_on) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (*log->path == 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (log->file != NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     log->file = fopen(log->path, "a+"); | ||||
|  | ||||
|     if (log->file == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     log->log_on = true; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Loads previous history from chat log */ | ||||
| void load_chat_history(ToxWindow *self, struct chatlog *log) | ||||
| /* Initializes a log. This function must be called before any other logging operations. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type) | ||||
| { | ||||
|     if (log->file == NULL) | ||||
|         return; | ||||
|     if (log == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (log->file != NULL || log->log_on) { | ||||
|         fprintf(stderr, "Warning: Called log_init() on an already initialized log\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (init_logging_session(name, selfkey, otherkey, log, type) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     log_disable(log); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Loads chat log history and prints it to `self` window. | ||||
|  * | ||||
|  * Return 0 on success or if log file doesn't exist. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| int load_chat_history(ToxWindow *self, struct chatlog *log) | ||||
| { | ||||
|     if (log == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (*log->path == 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     off_t sz = file_size(log->path); | ||||
|  | ||||
|     if (sz <= 0) | ||||
|         return; | ||||
|  | ||||
|     char *hstbuf = malloc(sz + 1); | ||||
|  | ||||
|     if (hstbuf == NULL) | ||||
|         exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY); | ||||
|  | ||||
|     if (fseek(log->file, 0L, SEEK_SET) == -1) { | ||||
|         free(hstbuf); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file"); | ||||
|         return; | ||||
|     if (sz <= 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (fread(hstbuf, sz, 1, log->file) != 1) { | ||||
|         free(hstbuf); | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file"); | ||||
|         return; | ||||
|     FILE *fp = fopen(log->path, "r"); | ||||
|  | ||||
|     if (fp == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     hstbuf[sz] = '\0'; | ||||
|     char *buf = malloc(sz + 1); | ||||
|  | ||||
|     if (buf == NULL) { | ||||
|         fclose(fp); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (fseek(fp, 0L, SEEK_SET) == -1) { | ||||
|         free(buf); | ||||
|         fclose(fp); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (fread(buf, sz, 1, fp) != 1) { | ||||
|         free(buf); | ||||
|         fclose(fp); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     fclose(fp); | ||||
|  | ||||
|     buf[sz] = 0; | ||||
|  | ||||
|     /* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */ | ||||
|     int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size); | ||||
|     int start, count = 0; | ||||
|  | ||||
|     int start = 0; | ||||
|     int count = 0; | ||||
|  | ||||
|     /* start at end and backtrace L lines or to the beginning of buffer */ | ||||
|     for (start = sz - 1; start >= 0 && count < L; --start) { | ||||
|         if (hstbuf[start] == '\n') | ||||
|         if (buf[start] == '\n') { | ||||
|             ++count; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     const char *line = strtok(&hstbuf[start + 1], "\n"); | ||||
|     char *tmp = NULL; | ||||
|     const char *line = strtok_r(&buf[start + 1], "\n", &tmp); | ||||
|  | ||||
|     if (line == NULL) { | ||||
|         free(hstbuf); | ||||
|         return; | ||||
|         free(buf); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     while (line != NULL && count--) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line); | ||||
|         line = strtok(NULL, "\n"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", line); | ||||
|         line = strtok_r(NULL, "\n", &tmp); | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|     free(hstbuf); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, YELLOW, "---"); | ||||
|  | ||||
|     free(buf); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* renames chatlog file replacing src with dest. | ||||
|    Returns 0 on success or if no log exists, -1 on failure. */ | ||||
| int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum) | ||||
| /* Renames chatlog file `src` to `dest`. | ||||
|  * | ||||
|  * Return 0 on success or if no log exists. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum) | ||||
| { | ||||
|     ToxWindow *toxwin = get_window_ptr(winnum); | ||||
|     struct chatlog *log = NULL; | ||||
| @@ -235,36 +327,60 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other | ||||
|     /* disable log if necessary and save its state */ | ||||
|     if (toxwin != NULL) { | ||||
|         log = toxwin->chatwin->log; | ||||
|  | ||||
|         if (log == NULL) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         log_on = log->log_on; | ||||
|     } | ||||
|  | ||||
|     if (log_on) | ||||
|     if (log_on) { | ||||
|         log_disable(log); | ||||
|     } | ||||
|  | ||||
|     char newpath[MAX_STR_SIZE]; | ||||
|     char oldpath[MAX_STR_SIZE]; | ||||
|  | ||||
|     if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) | ||||
|     if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey) == -1) { | ||||
|         goto on_error; | ||||
|     } | ||||
|  | ||||
|     if (!file_exists(oldpath)) | ||||
|     if (!file_exists(oldpath)) { | ||||
|         init_logging_session(dest, selfkey, otherkey, log, LOG_TYPE_CHAT);  // still need to rename path | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1) | ||||
|     int new_path_len = get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey); | ||||
|  | ||||
|     if (new_path_len == -1 || new_path_len >= MAX_STR_SIZE) { | ||||
|         goto on_error; | ||||
|     } | ||||
|  | ||||
|     if (rename(oldpath, newpath) != 0) | ||||
|     if (file_exists(newpath)) { | ||||
|         if (remove(oldpath) != 0) { | ||||
|             fprintf(stderr, "Warning: remove() failed to remove log path `%s`\n", oldpath); | ||||
|         } | ||||
|     } else if (rename(oldpath, newpath) != 0) { | ||||
|         goto on_error; | ||||
|     } | ||||
|  | ||||
|     if (log_on) | ||||
|         log_enable(dest, selfkey, otherkey, log, LOG_CHAT); | ||||
|     if (log != NULL) { | ||||
|         memcpy(log->path, newpath, new_path_len); | ||||
|         log->path[new_path_len] = 0; | ||||
|  | ||||
|         if (log_on) { | ||||
|             log_enable(log); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| on_error: | ||||
|  | ||||
|     if (log_on) | ||||
|         log_enable(src, selfkey, otherkey, log, LOG_CHAT); | ||||
|     if (log_on) { | ||||
|         log_enable(log); | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|   | ||||
							
								
								
									
										37
									
								
								src/log.h
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								src/log.h
									
									
									
									
									
								
							| @@ -30,30 +30,43 @@ struct chatlog { | ||||
|     bool log_on;    /* specific to current chat window */ | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|     LOG_GROUP, | ||||
|     LOG_PROMPT, | ||||
|     LOG_CHAT, | ||||
| typedef enum LOG_TYPE { | ||||
|     LOG_TYPE_PROMPT, | ||||
|     LOG_TYPE_CHAT, | ||||
| } LOG_TYPE; | ||||
|  | ||||
| /* Initializes a log. This function must be called before any other logging operations. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type); | ||||
|  | ||||
| /* formats/writes line to log file */ | ||||
| void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event); | ||||
|  | ||||
| /* enables logging for specified log and creates/fetches file if necessary. | ||||
| /* enables logging for specified log. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype); | ||||
| int log_enable(struct chatlog *log); | ||||
|  | ||||
| /* disables logging for specified log and closes file */ | ||||
| void log_disable(struct chatlog *log); | ||||
|  | ||||
| /* Loads previous history from chat log */ | ||||
| void load_chat_history(ToxWindow *self, struct chatlog *log); | ||||
| /* Loads chat log history and prints it to `self` window. | ||||
|  * | ||||
|  * Return 0 on success or if log file doesn't exist. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| int load_chat_history(ToxWindow *self, struct chatlog *log); | ||||
|  | ||||
| /* renames chatlog file replacing src with dest. | ||||
|    Returns 0 on success or if no log exists, -1 on failure. */ | ||||
| int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum); | ||||
| /* Renames chatlog file `src` to `dest`. | ||||
|  * | ||||
|  * Return 0 on success or if no log exists. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum); | ||||
|  | ||||
| #endif /* #define LOG_H */ | ||||
| #endif /* LOG_H */ | ||||
|   | ||||
| @@ -22,12 +22,12 @@ | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "message_queue.h" | ||||
| #include "misc_tools.h" | ||||
| #include "line_info.h" | ||||
| #include "log.h" | ||||
| #include "message_queue.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| void cqueue_cleanup(struct chat_queue *q) | ||||
| { | ||||
| @@ -42,19 +42,24 @@ void cqueue_cleanup(struct chat_queue *q) | ||||
|     free(q); | ||||
| } | ||||
|  | ||||
| void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id) | ||||
| void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id) | ||||
| { | ||||
|     if (line_id < 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg)); | ||||
|  | ||||
|     if (new_m == NULL) | ||||
|     if (new_m == NULL) { | ||||
|         exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     snprintf(new_m->message, sizeof(new_m->message), "%s", msg); | ||||
|     new_m->len = len; | ||||
|     new_m->type = type; | ||||
|     new_m->line_id = line_id; | ||||
|     new_m->last_send_try = 0; | ||||
|     new_m->receipt = 0; | ||||
|     new_m->receipt = -1; | ||||
|     new_m->next = NULL; | ||||
|  | ||||
|     if (q->root == NULL) { | ||||
| @@ -81,9 +86,9 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg) | ||||
|  | ||||
|         line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ; | ||||
|  | ||||
|         if (line->noread_flag == true) { | ||||
|             line->len -= 2; | ||||
|         if (line->noread_flag) { | ||||
|             line->noread_flag = false; | ||||
|             line->read_flag = true; | ||||
|         } | ||||
|  | ||||
|         return; | ||||
| @@ -93,6 +98,7 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg) | ||||
| /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ | ||||
| void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | ||||
| { | ||||
|     struct chatlog *log = self->chatwin->log; | ||||
|     struct chat_queue *q = self->chatwin->cqueue; | ||||
|     struct cqueue_msg *msg = q->root; | ||||
|  | ||||
| @@ -102,53 +108,84 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt) | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         char selfname[TOX_MAX_NAME_LENGTH]; | ||||
|         tox_self_get_name(m, (uint8_t *) selfname); | ||||
|         if (log->log_on) { | ||||
|             char selfname[TOX_MAX_NAME_LENGTH]; | ||||
|             tox_self_get_name(m, (uint8_t *) selfname); | ||||
|  | ||||
|         size_t len = tox_self_get_name_size(m); | ||||
|         selfname[len] = '\0'; | ||||
|             size_t len = tox_self_get_name_size(m); | ||||
|             selfname[len] = 0; | ||||
|  | ||||
|             write_to_log(msg->message, selfname, log, msg->type == OUT_ACTION); | ||||
|         } | ||||
|  | ||||
|         write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION); | ||||
|         cqueue_mark_read(self, msg); | ||||
|  | ||||
|         struct cqueue_msg *next = msg->next; | ||||
|  | ||||
|         if (msg->prev == NULL) {    /* root */ | ||||
|             if (next) | ||||
|             if (next) { | ||||
|                 next->prev = NULL; | ||||
|             } | ||||
|  | ||||
|             free(msg); | ||||
|             q->root = next; | ||||
|         } else { | ||||
|             struct cqueue_msg *prev = msg->prev; | ||||
|             free(msg); | ||||
|             prev->next = next; | ||||
|             next->prev = prev; | ||||
|         } | ||||
|  | ||||
|         free(msg); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #define CQUEUE_TRY_SEND_INTERVAL 60 | ||||
| // We use knowledge of toxcore internals (bad!) to determine that if we haven't received a read receipt for a | ||||
| // sent packet after this amount of time, the connection has been severed and the packet needs to be re-sent. | ||||
| #define TRY_SEND_TIMEOUT 32 | ||||
|  | ||||
| /* Tries to send the oldest unsent message in queue. */ | ||||
| /* | ||||
|  * Marks all timed out messages in queue as unsent. | ||||
|  */ | ||||
| static void cqueue_check_timeouts(struct cqueue_msg *msg) | ||||
| { | ||||
|     while (msg) { | ||||
|         if (timed_out(msg->last_send_try, TRY_SEND_TIMEOUT)) { | ||||
|             msg->receipt = -1; | ||||
|         } | ||||
|  | ||||
|         msg = msg->next; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Tries to send all messages in the send queue in sequential order. | ||||
|  * If a message fails to send the function will immediately return. | ||||
|  */ | ||||
| void cqueue_try_send(ToxWindow *self, Tox *m) | ||||
| { | ||||
|     struct chat_queue *q = self->chatwin->cqueue; | ||||
|     struct cqueue_msg *msg = q->root; | ||||
|  | ||||
|     if (!msg) | ||||
|         return; | ||||
|     while (msg) { | ||||
|         if (msg->receipt != -1) { | ||||
|             // we can no longer try to send unsent messages until we get receipts for our previous sent | ||||
|             // messages, but we continue to iterate the list, checking timestamps for any further | ||||
|             // successfully sent messages that have not yet gotten a receipt. | ||||
|             cqueue_check_timeouts(msg); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|     if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) | ||||
|         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); | ||||
|  | ||||
|     uint32_t receipt = 0; | ||||
|         if (err != TOX_ERR_FRIEND_SEND_MESSAGE_OK) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|     TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION; | ||||
|     receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL); | ||||
|  | ||||
|     msg->last_send_try = get_unix_time(); | ||||
|     msg->receipt = receipt; | ||||
|     return; | ||||
|         msg->receipt = receipt; | ||||
|         msg->last_send_try = get_unix_time(); | ||||
|         msg = msg->next; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -27,9 +27,9 @@ struct cqueue_msg { | ||||
|     char message[MAX_STR_SIZE]; | ||||
|     size_t len; | ||||
|     int line_id; | ||||
|     uint8_t type; | ||||
|     uint32_t receipt; | ||||
|     time_t last_send_try; | ||||
|     uint8_t type; | ||||
|     int64_t receipt; | ||||
|     struct cqueue_msg *next; | ||||
|     struct cqueue_msg *prev; | ||||
| }; | ||||
| @@ -40,12 +40,15 @@ struct chat_queue { | ||||
| }; | ||||
|  | ||||
| void cqueue_cleanup(struct chat_queue *q); | ||||
| void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id); | ||||
| void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id); | ||||
|  | ||||
| /* Tries to send the oldest unsent message in queue. */ | ||||
| /* | ||||
|  * Tries to send all messages in the send queue in sequential order. | ||||
|  * If a message fails to send the function will immediately return. | ||||
|  */ | ||||
| void cqueue_try_send(ToxWindow *self, Tox *m); | ||||
|  | ||||
| /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ | ||||
| void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt); | ||||
|  | ||||
| #endif  /* #define MESSAGE_QUEUE_H */ | ||||
| #endif /* MESSAGE_QUEUE_H */ | ||||
|   | ||||
							
								
								
									
										411
									
								
								src/misc_tools.c
									
									
									
									
									
								
							
							
						
						
									
										411
									
								
								src/misc_tools.c
									
									
									
									
									
								
							| @@ -20,42 +20,44 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <ctype.h> | ||||
| #include <string.h> | ||||
| #include <time.h> | ||||
| #include <limits.h> | ||||
| #include <dirent.h> | ||||
| #if defined(__FreeBSD__) | ||||
| #include <netinet/in.h> | ||||
| #include <sys/socket.h> | ||||
| #else | ||||
| #include <arpa/inet.h> | ||||
| #endif | ||||
| #include <ctype.h> | ||||
| #include <limits.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/stat.h> | ||||
| #include <time.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "file_transfers.h" | ||||
| #include "misc_tools.h" | ||||
| #include "settings.h" | ||||
| #include "file_transfers.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern ToxWindow *prompt; | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| void clear_screen(void) | ||||
| { | ||||
|     printf("\033[2J\033[1;1H"); | ||||
| } | ||||
|  | ||||
| void hst_to_net(uint8_t *num, uint16_t numbytes) | ||||
| { | ||||
| #ifndef WORDS_BIGENDIAN | ||||
|     uint32_t i; | ||||
|     uint8_t buff[numbytes]; | ||||
|     uint8_t *buff = malloc(numbytes); | ||||
|  | ||||
|     for (i = 0; i < numbytes; ++i) { | ||||
|     if (buff == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (uint32_t i = 0; i < numbytes; ++i) { | ||||
|         buff[i] = num[numbytes - i - 1]; | ||||
|     } | ||||
|  | ||||
|     memcpy(num, buff, numbytes); | ||||
|     free(buff); | ||||
| #endif | ||||
|     return; | ||||
| } | ||||
|  | ||||
| time_t get_unix_time(void) | ||||
| @@ -69,6 +71,22 @@ int timed_out(time_t timestamp, time_t timeout) | ||||
|     return timestamp + timeout <= get_unix_time(); | ||||
| } | ||||
|  | ||||
| /* Attempts to sleep the caller's thread for `usec` microseconds */ | ||||
| void sleep_thread(long int usec) | ||||
| { | ||||
|     struct timespec req; | ||||
|     struct timespec rem; | ||||
|  | ||||
|     req.tv_sec = 0; | ||||
|     req.tv_nsec = usec * 1000L; | ||||
|  | ||||
|     if (nanosleep(&req, &rem) == -1) { | ||||
|         if (nanosleep(&rem, NULL) == -1) { | ||||
|             fprintf(stderr, "nanosleep() returned -1\n"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Get the current local time */ | ||||
| struct tm *get_time(void) | ||||
| { | ||||
| @@ -78,34 +96,44 @@ struct tm *get_time(void) | ||||
|     return timeinfo; | ||||
| } | ||||
|  | ||||
| /*Puts the current time in buf in the format of [HH:mm:ss] */ | ||||
| void get_time_str(char *buf, int bufsize) | ||||
| /* Puts the current time in buf in the format of specified by the config */ | ||||
| void get_time_str(char *buf, size_t bufsize) | ||||
| { | ||||
|     if (buf == NULL || bufsize == 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     *buf = 0; | ||||
|  | ||||
|     if (user_settings->timestamps == TIMESTAMPS_OFF) { | ||||
|         buf[0] = '\0'; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *t = user_settings->timestamp_format; | ||||
|     strftime(buf, bufsize, t, get_time()); | ||||
|  | ||||
|     if (strftime(buf, bufsize, t, get_time()) == 0) { | ||||
|         strftime(buf, bufsize, TIMESTAMP_DEFAULT, get_time()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||
| void get_elapsed_time_str(char *buf, int bufsize, time_t secs) | ||||
| { | ||||
|     if (!secs) | ||||
|     if (!secs) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     long int seconds = secs % 60; | ||||
|     long int minutes = (secs % 3600) / 60; | ||||
|     long int hours = secs / 3600; | ||||
|  | ||||
|     if (!minutes && !hours) | ||||
|     if (!minutes && !hours) { | ||||
|         snprintf(buf, bufsize, "%.2ld", seconds); | ||||
|     else if (!hours) | ||||
|     } else if (!hours) { | ||||
|         snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds); | ||||
|     else | ||||
|     } else { | ||||
|         snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -117,11 +145,12 @@ void get_elapsed_time_str(char *buf, int bufsize, time_t secs) | ||||
|  */ | ||||
| int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size) | ||||
| { | ||||
|     if (output_size == 0 || hex_len != output_size * 2) | ||||
|     if (output_size == 0 || hex_len != output_size * 2) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     for (size_t i = 0; i < output_size; ++i) { | ||||
|         sscanf(hex_string, "%2hhx", &output[i]); | ||||
|         sscanf(hex_string, "%2hhx", (unsigned char *)&output[i]); | ||||
|         hex_string += 2; | ||||
|     } | ||||
|  | ||||
| @@ -130,18 +159,19 @@ int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size | ||||
|  | ||||
| int hex_string_to_bytes(char *buf, int size, const char *keystr) | ||||
| { | ||||
|     if (size % 2 != 0) | ||||
|     if (size % 2 != 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int i, res; | ||||
|     const char *pos = keystr; | ||||
|  | ||||
|     for (i = 0; i < size; ++i) { | ||||
|         res = sscanf(pos, "%2hhx", &buf[i]); | ||||
|     for (size_t i = 0; i < size; ++i) { | ||||
|         int res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]); | ||||
|         pos += 2; | ||||
|  | ||||
|         if (res == EOF || res < 1) | ||||
|         if (res == EOF || res < 1) { | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -154,13 +184,31 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr) | ||||
|  */ | ||||
| int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size) | ||||
| { | ||||
|     if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) | ||||
|     if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < TOX_ADDRESS_SIZE; ++i) | ||||
|     for (size_t i = 0; i < TOX_ADDRESS_SIZE; ++i) { | ||||
|         snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Converts a binary representation of a Tox public key into a string. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size) | ||||
| { | ||||
|     if (bin_pubkey_size != TOX_PUBLIC_KEY_SIZE || output_size < (TOX_PUBLIC_KEY_SIZE * 2 + 1)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     for (size_t i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) { | ||||
|         snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_pubkey[i] & 0xff); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -168,8 +216,9 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_ | ||||
| /* Returns 1 if the string is empty, 0 otherwise */ | ||||
| int string_is_empty(const char *string) | ||||
| { | ||||
|     if (!string) | ||||
|     if (!string) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return string[0] == '\0'; | ||||
| } | ||||
| @@ -177,8 +226,9 @@ int string_is_empty(const char *string) | ||||
| /* Returns 1 if the string is empty, 0 otherwise */ | ||||
| int wstring_is_empty(const wchar_t *string) | ||||
| { | ||||
|     if (!string) | ||||
|     if (!string) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return string[0] == L'\0'; | ||||
| } | ||||
| @@ -188,11 +238,13 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n) | ||||
| { | ||||
|     size_t len = mbstowcs(NULL, string, 0) + 1; | ||||
|  | ||||
|     if (n < len) | ||||
|     if (n < len) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if ((len = mbstowcs(buf, string, n)) == (size_t) - 1) | ||||
|     if ((len = mbstowcs(buf, string, n)) == (size_t) - 1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return len; | ||||
| } | ||||
| @@ -202,11 +254,13 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n) | ||||
| { | ||||
|     size_t len = wcstombs(NULL, string, 0) + 1; | ||||
|  | ||||
|     if (n < len) | ||||
|     if (n < len) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if ((len = wcstombs(buf, string, n)) == (size_t) - 1) | ||||
|     if ((len = wcstombs(buf, string, n)) == (size_t) - 1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return len; | ||||
| } | ||||
| @@ -217,41 +271,67 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2) | ||||
|     return strcasecmp((const char *) str1, (const char *) str2); | ||||
| } | ||||
|  | ||||
| /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | ||||
|       - cannot be empty | ||||
|       - cannot start with a space | ||||
|       - must not contain a forward slash (for logfile naming purposes) | ||||
|       - must not contain contiguous spaces | ||||
|       - must not contain a newline or tab seqeunce */ | ||||
| int valid_nick(const char *nick) | ||||
| /* case-insensitive string compare function for use with qsort */ | ||||
| int qsort_ptr_char_array_helper(const void *str1, const void *str2) | ||||
| { | ||||
|     if (!nick[0] || nick[0] == ' ') | ||||
|         return 0; | ||||
|     return strcasecmp(*(char **)str1, *(char **)str2); | ||||
| } | ||||
|  | ||||
|     int i; | ||||
| static const char invalid_chars[] = {'/', '\n', '\t', '\v', '\r', '\0'}; | ||||
|  | ||||
|     for (i = 0; nick[i]; ++i) { | ||||
|         if ((nick[i] == ' ' && nick[i + 1] == ' ') | ||||
|                 || nick[i] == '/' | ||||
|                 || nick[i] == '\n' | ||||
|                 || nick[i] == '\t' | ||||
|                 || nick[i] == '\v' | ||||
|                 || nick[i] == '\r') | ||||
| /* | ||||
|  * Helper function for `valid_nick()`. | ||||
|  * | ||||
|  * Returns true if `ch` is not in the `invalid_chars` array. | ||||
|  */ | ||||
| static bool is_valid_char(char ch) | ||||
| { | ||||
|     char tmp; | ||||
|  | ||||
|             return 0; | ||||
|     for (size_t i = 0; (tmp = invalid_chars[i]); ++i) { | ||||
|         if (tmp == ch) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 1; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| /* Returns true if nick is valid. | ||||
|  * | ||||
|  * A valid toxic nick: | ||||
|  * - cannot be empty | ||||
|  * - cannot start with a space | ||||
|  * - must not contain a forward slash (for logfile naming purposes) | ||||
|  * - must not contain contiguous spaces | ||||
|  * - must not contain a newline or tab seqeunce | ||||
|  */ | ||||
| bool valid_nick(const char *nick) | ||||
| { | ||||
|     if (!nick[0] || nick[0] == ' ') { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     for (size_t i = 0; nick[i]; ++i) { | ||||
|         char ch = nick[i]; | ||||
|  | ||||
|         if ((ch == ' ' && nick[i + 1] == ' ') || !is_valid_char(ch)) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | ||||
| void filter_str(char *str, size_t len) | ||||
| { | ||||
|     size_t i; | ||||
|     for (size_t i = 0; i < len; ++i) { | ||||
|         char ch = str[i]; | ||||
|  | ||||
|     for (i = 0; i < len; ++i) { | ||||
|         if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') | ||||
|         if (!is_valid_char(ch) || str[i] == '\0') { | ||||
|             str[i] = ' '; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -263,22 +343,26 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname) | ||||
|     int len = strlen(pathname) - 1; | ||||
|     char *path = strdup(pathname); | ||||
|  | ||||
|     if (path == NULL) | ||||
|     if (path == NULL) { | ||||
|         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     while (len >= 0 && pathname[len] == '/') | ||||
|     while (len >= 0 && pathname[len] == '/') { | ||||
|         path[len--] = '\0'; | ||||
|     } | ||||
|  | ||||
|     char *finalname = strdup(path); | ||||
|  | ||||
|     if (finalname == NULL) | ||||
|     if (finalname == NULL) { | ||||
|         exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     const char *basenm = strrchr(path, '/'); | ||||
|  | ||||
|     if (basenm != NULL) { | ||||
|         if (basenm[1]) | ||||
|         if (basenm[1]) { | ||||
|             strcpy(finalname, &basenm[1]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     snprintf(namebuf, bufsize, "%s", finalname); | ||||
| @@ -295,13 +379,15 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname) | ||||
|  */ | ||||
| size_t get_base_dir(const char *path, size_t path_len, char *dir) | ||||
| { | ||||
|     if (path_len == 0 || path == NULL) | ||||
|     if (path_len == 0 || path == NULL) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     size_t dir_len = char_rfind(path, '/', path_len); | ||||
|  | ||||
|     if (dir_len != 0 && dir_len < path_len) | ||||
|         ++dir_len;  /* Leave trailing slash */ | ||||
|     if (dir_len != 0 && dir_len < path_len) { | ||||
|         ++dir_len;    /* Leave trailing slash */ | ||||
|     } | ||||
|  | ||||
|     memcpy(dir, path, dir_len); | ||||
|     dir[dir_len] = '\0'; | ||||
| @@ -314,8 +400,9 @@ void str_to_lower(char *str) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; str[i]; ++i) | ||||
|     for (i = 0; str[i]; ++i) { | ||||
|         str[i] = tolower(str[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary. | ||||
| @@ -323,45 +410,71 @@ void str_to_lower(char *str) | ||||
|    Returns nick len */ | ||||
| size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum) | ||||
| { | ||||
|     size_t len = tox_friend_get_name_size(m, friendnum, NULL); | ||||
|     Tox_Err_Friend_Query err; | ||||
|     size_t len = tox_friend_get_name_size(m, friendnum, &err); | ||||
|  | ||||
|     if (len == 0) { | ||||
|         strcpy(buf, UNKNOWN_NAME); | ||||
|         len = strlen(UNKNOWN_NAME); | ||||
|     if (err != TOX_ERR_FRIEND_QUERY_OK) { | ||||
|         goto on_error; | ||||
|     } else { | ||||
|         tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL); | ||||
|         if (!tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL)) { | ||||
|             goto on_error; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||
|     buf[len] = '\0'; | ||||
|     filter_str(buf, len); | ||||
|     return len; | ||||
|  | ||||
| on_error: | ||||
|     strcpy(buf, UNKNOWN_NAME); | ||||
|     len = strlen(UNKNOWN_NAME); | ||||
|     buf[len] = '\0'; | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| /* same as get_nick_truncate but for groupchats */ | ||||
| int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum) | ||||
| /* same as get_nick_truncate but for conferences */ | ||||
| int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum) | ||||
| { | ||||
|     int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf); | ||||
|     Tox_Err_Conference_Peer_Query err; | ||||
|     size_t len = tox_conference_peer_get_name_size(m, conferencenum, peernum, &err); | ||||
|  | ||||
|     if (len == -1) { | ||||
|         strcpy(buf, UNKNOWN_NAME); | ||||
|         len = strlen(UNKNOWN_NAME); | ||||
|     if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { | ||||
|         goto on_error; | ||||
|     } else { | ||||
|         if (!tox_conference_peer_get_name(m, conferencenum, peernum, (uint8_t *) buf, NULL)) { | ||||
|             goto on_error; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||
|     buf[len] = '\0'; | ||||
|     filter_str(buf, len); | ||||
|     return len; | ||||
|  | ||||
| on_error: | ||||
|     strcpy(buf, UNKNOWN_NAME); | ||||
|     len = strlen(UNKNOWN_NAME); | ||||
|     buf[len] = '\0'; | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| /* copies data to msg buffer. | ||||
| /* copies data to msg buffer, removing return characters. | ||||
|    returns length of msg, which will be no larger than size-1 */ | ||||
| size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length) | ||||
| { | ||||
|     size_t len = MIN(length, size - 1); | ||||
|     memcpy(msg, data, len); | ||||
|     msg[len] = '\0'; | ||||
|     return len; | ||||
|     size_t i; | ||||
|     size_t j = 0; | ||||
|  | ||||
|     for (i = 0; (i < length) && (j < size - 1); ++i) { | ||||
|         if (data[i] != '\r') { | ||||
|             msg[j++] = data[i]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     msg[j] = '\0'; | ||||
|  | ||||
|     return j; | ||||
| } | ||||
|  | ||||
| /* returns index of the first instance of ch in s starting at idx. | ||||
| @@ -375,8 +488,9 @@ int char_find(int idx, const char *s, char ch) | ||||
|     int i = idx; | ||||
|  | ||||
|     for (i = idx; s[i]; ++i) { | ||||
|         if (s[i] == ch) | ||||
|         if (s[i] == ch) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return i; | ||||
| @@ -393,8 +507,9 @@ int char_rfind(const char *s, char ch, int len) | ||||
|     int i = 0; | ||||
|  | ||||
|     for (i = len; i > 0; --i) { | ||||
|         if (s[i] == ch) | ||||
|         if (s[i] == ch) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return i; | ||||
| @@ -429,45 +544,59 @@ bool file_exists(const char *path) | ||||
|     return stat(path, &s) == 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Checks the file type path points to and returns a File_Type enum value. | ||||
|  * | ||||
|  * Returns FILE_TYPE_DIRECTORY if path points to a directory. | ||||
|  * Returns FILE_TYPE_REGULAR if path points to a regular file. | ||||
|  * Returns FILE_TYPE_OTHER on any other result, including an invalid path. | ||||
|  */ | ||||
| File_Type file_type(const char *path) | ||||
| { | ||||
|     struct stat s; | ||||
|  | ||||
|     if (stat(path, &s) == -1) { | ||||
|         return FILE_TYPE_OTHER; | ||||
|     } | ||||
|  | ||||
|     switch (s.st_mode & S_IFMT) { | ||||
|         case S_IFDIR: | ||||
|             return FILE_TYPE_DIRECTORY; | ||||
|  | ||||
|         case S_IFREG: | ||||
|             return FILE_TYPE_REGULAR; | ||||
|  | ||||
|         default: | ||||
|             return FILE_TYPE_OTHER; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* returns file size. If file doesn't exist returns 0. */ | ||||
| off_t file_size(const char *path) | ||||
| { | ||||
|     struct stat st; | ||||
|  | ||||
|     if (stat(path, &st) == -1) | ||||
|     if (stat(path, &st) == -1) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return st.st_size; | ||||
| } | ||||
|  | ||||
| /* compares the first size bytes of fp to signature. | ||||
|    Returns 0 if they are the same, 1 if they differ, and -1 on error. | ||||
|  | ||||
|    On success this function will seek back to the beginning of fp */ | ||||
| int check_file_signature(const char *signature, size_t size, FILE *fp) | ||||
| { | ||||
|     char buf[size]; | ||||
|  | ||||
|     if (fread(buf, size, 1, fp) != 1) | ||||
|         return -1; | ||||
|  | ||||
|     int ret = memcmp(signature, buf, size); | ||||
|  | ||||
|     if (fseek(fp, 0L, SEEK_SET) == -1) | ||||
|         return -1; | ||||
|  | ||||
|     return ret == 0 ? 0 : 1; | ||||
| } | ||||
|  | ||||
| /* sets window title in tab bar. */ | ||||
| void set_window_title(ToxWindow *self, const char *title, int len) | ||||
| { | ||||
|     if (len <= 0 || !title) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char cpy[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|  | ||||
|     if (self->is_groupchat)   /* keep groupnumber in title */ | ||||
|         snprintf(cpy, sizeof(cpy), "%d %s", self->num, title); | ||||
|     else | ||||
|     if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title for invites */ | ||||
|         snprintf(cpy, sizeof(cpy), "%u %s", self->num, title); | ||||
|     } else { | ||||
|         snprintf(cpy, sizeof(cpy), "%s", title); | ||||
|     } | ||||
|  | ||||
|     if (len > MAX_WINDOW_NAME_LENGTH) { | ||||
|         strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "..."); | ||||
| @@ -477,35 +606,49 @@ void set_window_title(ToxWindow *self, const char *title, int len) | ||||
|     snprintf(self->name, sizeof(self->name), "%s", cpy); | ||||
| } | ||||
|  | ||||
| /* Return true if address appears to be a valid ipv4 address. */ | ||||
| bool is_ip4_address(const char *address) | ||||
| /* | ||||
|  * Frees all members of a pointer array plus `arr`. | ||||
|  */ | ||||
| void free_ptr_array(void **arr) | ||||
| { | ||||
|     struct sockaddr_in s_addr; | ||||
|     return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0; | ||||
|     if (arr == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     void **tmp = arr; | ||||
|  | ||||
|     while (*arr) { | ||||
|         free(*arr); | ||||
|         ++arr; | ||||
|     } | ||||
|  | ||||
|     free(tmp); | ||||
| } | ||||
|  | ||||
| /* Return true if address roughly appears to be a valid ipv6 address. | ||||
| /* | ||||
|  * Returns a null terminated array of `length` pointers. Each pointer is allocated `bytes` bytes. | ||||
|  * Returns NULL on failure. | ||||
|  * | ||||
|  * 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). | ||||
|  * The caller is responsible for freeing the array with `free_ptr_array`. | ||||
|  */ | ||||
| bool is_ip6_address(const char *address) | ||||
| void **malloc_ptr_array(size_t length, size_t bytes) | ||||
| { | ||||
|     size_t i; | ||||
|     size_t num_colons = 0; | ||||
|     char ch = 0; | ||||
|     void **arr = malloc((length + 1) * sizeof(void *)); | ||||
|  | ||||
|     for (i = 0; (ch = address[i]); ++i) { | ||||
|         if (ch == '.') { | ||||
|             return false; | ||||
|         } | ||||
|     if (arr == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|         if (ch == ':') { | ||||
|             ++num_colons; | ||||
|     for (size_t i = 0; i < length; ++i) { | ||||
|         arr[i] = malloc(bytes); | ||||
|  | ||||
|         if (arr[i] == NULL) { | ||||
|             free_ptr_array(arr); | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return num_colons > 1 && num_colons < 8; | ||||
|     arr[length] = NULL; | ||||
|  | ||||
|     return arr; | ||||
| } | ||||
|   | ||||
| @@ -24,8 +24,8 @@ | ||||
|  | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #include "windows.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #ifndef MIN | ||||
| #define MIN(x, y) (((x) < (y)) ? (x) : (y)) | ||||
| @@ -39,6 +39,17 @@ | ||||
| #define net_to_host(x, y) hst_to_net(x, y) | ||||
| #endif | ||||
|  | ||||
| #define UNUSED_VAR(x) ((void) x) | ||||
|  | ||||
| typedef enum File_Type { | ||||
|     FILE_TYPE_REGULAR, | ||||
|     FILE_TYPE_DIRECTORY, | ||||
|     FILE_TYPE_OTHER, | ||||
| } File_Type; | ||||
|  | ||||
|  | ||||
| void clear_screen(void); | ||||
|  | ||||
| void hst_to_net(uint8_t *num, uint16_t numbytes); | ||||
|  | ||||
| /* | ||||
| @@ -60,11 +71,18 @@ 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); | ||||
|  | ||||
| /* 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); | ||||
|  | ||||
| /* get the current unix time (not thread safe) */ | ||||
| time_t get_unix_time(void); | ||||
|  | ||||
| /* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */ | ||||
| void get_time_str(char *buf, int bufsize); | ||||
| /* Puts the current time in buf in the format of specified by the config */ | ||||
| void get_time_str(char *buf, size_t bufsize); | ||||
|  | ||||
| /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ | ||||
| void get_elapsed_time_str(char *buf, int bufsize, time_t secs); | ||||
| @@ -78,31 +96,40 @@ int string_is_empty(const char *string); | ||||
| /* Same as above but for wide character strings */ | ||||
| int wstring_is_empty(const wchar_t *string); | ||||
|  | ||||
| /* convert a multibyte string to a wide character string (must provide buffer) */ | ||||
| /* converts a multibyte string to a wide character string (must provide buffer) */ | ||||
| int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||
|  | ||||
| /* converts wide character string into a multibyte string and puts in buf. */ | ||||
| int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n); | ||||
|  | ||||
| /* convert a multibyte string to a wide character string and puts in buf) */ | ||||
| /* converts a multibyte string to a wide character string and puts in buf) */ | ||||
| int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); | ||||
|  | ||||
| /* Returns 1 if connection has timed out, 0 otherwise */ | ||||
| int timed_out(time_t timestamp, time_t timeout); | ||||
|  | ||||
| /* Attempts to sleep the caller's thread for `usec` microseconds */ | ||||
| void sleep_thread(long int usec); | ||||
|  | ||||
| /* Colours the window tab according to type. Beeps if is_beep is true */ | ||||
| void alert_window(ToxWindow *self, int type, bool is_beep); | ||||
|  | ||||
| /* case-insensitive string compare function for use with qsort */ | ||||
| int qsort_strcasecmp_hlpr(const void *str1, const void *str2); | ||||
|  | ||||
| /* Returns 1 if nick is valid, 0 if not. A valid toxic nick: | ||||
|       - cannot be empty | ||||
|       - cannot start with a space | ||||
|       - must not contain a forward slash (for logfile naming purposes) | ||||
|       - must not contain contiguous spaces | ||||
|       - must not contain a newline or tab seqeunce */ | ||||
| int valid_nick(const char *nick); | ||||
| /* case-insensitive string compare function for use with qsort */ | ||||
| int qsort_ptr_char_array_helper(const void *str1, const void *str2); | ||||
|  | ||||
| /* Returns true if nick is valid. | ||||
|  * | ||||
|  * A valid toxic nick: | ||||
|  * - cannot be empty | ||||
|  * - cannot start with a space | ||||
|  * - must not contain a forward slash (for logfile naming purposes) | ||||
|  * - must not contain contiguous spaces | ||||
|  * - must not contain a newline or tab seqeunce | ||||
|  */ | ||||
| bool valid_nick(const char *nick); | ||||
|  | ||||
| /* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */ | ||||
| void filter_str(char *str, size_t len); | ||||
| @@ -125,8 +152,8 @@ void str_to_lower(char *str); | ||||
|    Returns nick len on success, -1 on failure */ | ||||
| size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); | ||||
|  | ||||
| /* same as get_nick_truncate but for groupchats */ | ||||
| int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum); | ||||
| /* same as get_nick_truncate but for conferences */ | ||||
| int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum); | ||||
|  | ||||
| /* copies data to msg buffer. | ||||
|    returns length of msg, which will be no larger than size-1 */ | ||||
| @@ -146,28 +173,32 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes); | ||||
| /* checks if a file exists. Returns true or false */ | ||||
| bool file_exists(const char *path); | ||||
|  | ||||
| /* | ||||
|  * Checks the file type path points to and returns a File_Type enum value. | ||||
|  * | ||||
|  * Returns FILE_TYPE_DIRECTORY if path points to a directory. | ||||
|  * Returns FILE_TYPE_REGULAR if path points to a regular file. | ||||
|  * Returns FILE_TYPE_OTHER on any other result, including an invalid path. | ||||
|  */ | ||||
| File_Type file_type(const char *path); | ||||
|  | ||||
| /* returns file size. If file doesn't exist returns 0. */ | ||||
| off_t file_size(const char *path); | ||||
|  | ||||
| /* compares the first size bytes of fp and signature. | ||||
|    Returns 0 if they are the same, 1 if they differ, and -1 on error. | ||||
|  | ||||
|    On success this function will seek back to the beginning of fp */ | ||||
| int check_file_signature(const char *signature, size_t size, FILE *fp); | ||||
|  | ||||
| /* sets window title in tab bar. */ | ||||
| void set_window_title(ToxWindow *self, const char *title, int len); | ||||
|  | ||||
| /* Return true if address appears to be a valid ipv4 address. */ | ||||
| bool is_ip4_address(const char *address); | ||||
|  | ||||
| /* Return true if address roughly appears to be a valid ipv6 address. | ||||
|  * | ||||
|  * TODO: Improve this function (inet_pton behaves strangely with ipv6). | ||||
|  * for now the only guarantee is that it won't return true if the | ||||
|  * address is a domain or ipv4 address, and should only be used if you're | ||||
|  * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). | ||||
| /* | ||||
|  * Frees all members of a pointer array plus `arr`. | ||||
|  */ | ||||
| bool is_ip6_address(const char *address); | ||||
| void free_ptr_array(void **arr); | ||||
|  | ||||
| #endif /* #define MISC_TOOLS_H */ | ||||
| /* | ||||
|  * 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,17 +20,18 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <curl/curl.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <curl/curl.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "line_info.h" | ||||
| #include "global_commands.h" | ||||
| #include "misc_tools.h" | ||||
| #include "configdir.h" | ||||
| #include "curl_util.h" | ||||
| #include "global_commands.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern struct arg_opts arg_opts; | ||||
| extern struct Winthread Winthread; | ||||
| @@ -41,7 +42,7 @@ extern struct Winthread Winthread; | ||||
| #define MAX_DOMAIN_SIZE 32 | ||||
| #define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3 | ||||
|  | ||||
| struct Nameservers { | ||||
| static struct Nameservers { | ||||
|     int     lines; | ||||
|     char    names[MAX_SERVERS][MAX_DOMAIN_SIZE]; | ||||
|     char    keys[MAX_SERVERS][SERVER_KEY_SIZE]; | ||||
| @@ -62,6 +63,13 @@ static struct lookup_thread { | ||||
|     pthread_attr_t attr; | ||||
| } lookup_thread; | ||||
|  | ||||
| static void clear_thread_data(void) | ||||
| { | ||||
|     t_data = (struct thread_data) { | ||||
|         0 | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static int lookup_error(ToxWindow *self, const char *errmsg, ...) | ||||
| { | ||||
|     char frmt_msg[MAX_STR_SIZE]; | ||||
| @@ -72,7 +80,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...) | ||||
|     va_end(args); | ||||
|  | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg); | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     return -1; | ||||
| @@ -80,7 +88,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...) | ||||
|  | ||||
| static void kill_lookup_thread(void) | ||||
| { | ||||
|     memset(&t_data, 0, sizeof(struct thread_data)); | ||||
|     clear_thread_data(); | ||||
|     pthread_attr_destroy(&lookup_thread.attr); | ||||
|     pthread_exit(NULL); | ||||
| } | ||||
| @@ -96,42 +104,49 @@ static int load_nameserver_list(const char *path) | ||||
| { | ||||
|     FILE *fp = fopen(path, "r"); | ||||
|  | ||||
|     if (fp == NULL) | ||||
|     if (fp == NULL) { | ||||
|         return -2; | ||||
|     } | ||||
|  | ||||
|     char line[MAX_SERVER_LINE]; | ||||
|  | ||||
|     while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) { | ||||
|         int linelen = strlen(line); | ||||
|  | ||||
|         if (linelen < SERVER_KEY_SIZE * 2 + 5) | ||||
|         if (linelen < SERVER_KEY_SIZE * 2 + 5) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (line[linelen - 1] == '\n') | ||||
|         if (line[linelen - 1] == '\n') { | ||||
|             line[--linelen] = '\0'; | ||||
|         } | ||||
|  | ||||
|         const char *name = strtok(line, " "); | ||||
|         const char *keystr = strtok(NULL, " "); | ||||
|  | ||||
|         if (name == NULL || keystr == NULL) | ||||
|         if (name == NULL || keystr == NULL) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (strlen(keystr) != SERVER_KEY_SIZE * 2) | ||||
|         if (strlen(keystr) != SERVER_KEY_SIZE * 2) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name); | ||||
|         int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr); | ||||
|  | ||||
|         if (res == -1) | ||||
|         if (res == -1) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         ++Nameservers.lines; | ||||
|     } | ||||
|  | ||||
|     fclose(fp); | ||||
|  | ||||
|     if (Nameservers.lines < 1) | ||||
|     if (Nameservers.lines < 1) { | ||||
|         return -3; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -144,8 +159,9 @@ static int load_nameserver_list(const char *path) | ||||
|  */ | ||||
| static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz) | ||||
| { | ||||
|     if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH))) | ||||
|     if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH))) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char tmpaddr[MAX_STR_SIZE]; | ||||
|     char *tmpname = NULL; | ||||
| @@ -155,8 +171,9 @@ static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char * | ||||
|     tmpname = strtok(tmpaddr, "@"); | ||||
|     tmpdom = strtok(NULL, ""); | ||||
|  | ||||
|     if (tmpname == NULL || tmpdom == NULL) | ||||
|     if (tmpname == NULL || tmpdom == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     str_to_lower(tmpdom); | ||||
|     snprintf(namebuf, namebuf_sz, "%s", tmpname); | ||||
| @@ -196,29 +213,35 @@ static int process_response(struct Recv_Curl_Data *recv_data) | ||||
| { | ||||
|     size_t prefix_size = strlen(ID_PREFIX); | ||||
|  | ||||
|     if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size) | ||||
|     if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     const char *IDstart = strstr(recv_data->data, ID_PREFIX); | ||||
|  | ||||
|     if (IDstart == NULL) | ||||
|     if (IDstart == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) | ||||
|     if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char ID_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||
|     memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2); | ||||
|     ID_string[TOX_ADDRESS_SIZE * 2] = 0; | ||||
|  | ||||
|     if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1) | ||||
|     if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void *lookup_thread_func(void *data) | ||||
| { | ||||
|     UNUSED_VAR(data); | ||||
|  | ||||
|     ToxWindow *self = t_data.self; | ||||
|  | ||||
|     char input_domain[MAX_STR_SIZE]; | ||||
| @@ -233,10 +256,11 @@ void *lookup_thread_func(void *data) | ||||
|     char real_domain[MAX_DOMAIN_SIZE]; | ||||
|  | ||||
|     if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) { | ||||
|         if (!strcasecmp(input_domain, "utox.org")) | ||||
|         if (!strcasecmp(input_domain, "utox.org")) { | ||||
|             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."); | ||||
|         } | ||||
|  | ||||
|         kill_lookup_thread(); | ||||
|     } | ||||
| @@ -248,11 +272,14 @@ void *lookup_thread_func(void *data) | ||||
|         kill_lookup_thread(); | ||||
|     } | ||||
|  | ||||
|     struct Recv_Curl_Data recv_data; | ||||
|     struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data)); | ||||
|  | ||||
|     memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); | ||||
|     if (recv_data == NULL) { | ||||
|         lookup_error(self, "memory allocation error"); | ||||
|         kill_lookup_thread(); | ||||
|     } | ||||
|  | ||||
|     char post_data[MAX_STR_SIZE]; | ||||
|     char post_data[MAX_STR_SIZE + 30]; | ||||
|  | ||||
|     snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name); | ||||
|  | ||||
| @@ -268,7 +295,7 @@ void *lookup_thread_func(void *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_WRITEDATA, recv_data); | ||||
|  | ||||
|     curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); | ||||
|  | ||||
| @@ -277,7 +304,7 @@ void *lookup_thread_func(void *data) | ||||
|     int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type); | ||||
|  | ||||
|     if (proxy_ret != 0) { | ||||
|         lookup_error(self, "Failed to set proxy (error %d)\n"); | ||||
|         lookup_error(self, "Failed to set proxy (error %d)\n", proxy_ret); | ||||
|         goto on_exit; | ||||
|     } | ||||
|  | ||||
| @@ -317,7 +344,7 @@ void *lookup_thread_func(void *data) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (process_response(&recv_data) == -1) { | ||||
|     if (process_response(recv_data) == -1) { | ||||
|         lookup_error(self, "Bad response."); | ||||
|         goto on_exit; | ||||
|     } | ||||
| @@ -327,6 +354,7 @@ void *lookup_thread_func(void *data) | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
| on_exit: | ||||
|     free(recv_data); | ||||
|     curl_slist_free_all(headers); | ||||
|     curl_easy_cleanup(c_handle); | ||||
|     kill_lookup_thread(); | ||||
| @@ -337,12 +365,12 @@ on_exit: | ||||
| void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message) | ||||
| { | ||||
|     if (t_data.disabled) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (t_data.busy) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish."); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -354,22 +382,22 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, | ||||
|     t_data.busy = true; | ||||
|  | ||||
|     if (pthread_attr_init(&lookup_thread.attr) != 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init"); | ||||
|         memset(&t_data, 0, sizeof(struct thread_data)); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init"); | ||||
|         clear_thread_data(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set"); | ||||
|         pthread_attr_destroy(&lookup_thread.attr); | ||||
|         memset(&t_data, 0, sizeof(struct thread_data)); | ||||
|         clear_thread_data(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) { | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init"); | ||||
|         pthread_attr_destroy(&lookup_thread.attr); | ||||
|         memset(&t_data, 0, sizeof(struct thread_data)); | ||||
|         clear_thread_data(); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										365
									
								
								src/notify.c
									
									
									
									
									
								
							
							
						
						
									
										365
									
								
								src/notify.c
									
									
									
									
									
								
							| @@ -20,22 +20,22 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <stdbool.h> | ||||
| #include <unistd.h> | ||||
| #include <stdarg.h> | ||||
| #include <time.h> | ||||
| #include <assert.h> | ||||
| #include <sys/stat.h> | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "notify.h" | ||||
| #include "audio_device.h" | ||||
| #include "settings.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "xtra.h" | ||||
| #include "notify.h" | ||||
| #include "settings.h" | ||||
| #include "x11focus.h" | ||||
|  | ||||
| #if defined(AUDIO) || defined(SOUND_NOTIFY) | ||||
| #ifdef __APPLE__ | ||||
| @@ -47,12 +47,12 @@ | ||||
| /* compatibility with older versions of OpenAL */ | ||||
| #ifndef ALC_ALL_DEVICES_SPECIFIER | ||||
| #include <AL/alext.h> | ||||
| #endif | ||||
| #endif | ||||
| #endif /* ALC_ALL_DEVICES_SPECIFIER */ | ||||
| #endif /* __APPLE__ */ | ||||
| #ifdef SOUND_NOTIFY | ||||
| #include <AL/alut.h> /* freealut packet */ | ||||
| #endif | ||||
| #endif /* AUDIO */ | ||||
| #endif /* SOUND_NOTIFY */ | ||||
| #endif /* defined(AUDIO) || defined(SOUND_NOTIFY) */ | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
| #include <libnotify/notify.h> | ||||
| @@ -60,11 +60,11 @@ | ||||
|  | ||||
| #define MAX_BOX_MSG_LEN 127 | ||||
| #define SOUNDS_SIZE 10 | ||||
| #define ACTIVE_NOTIFS_MAX 50 | ||||
| #define ACTIVE_NOTIFS_MAX 10 | ||||
|  | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| struct Control { | ||||
| static struct Control { | ||||
|     time_t cooldown; | ||||
|     time_t notif_timeout; | ||||
|  | ||||
| @@ -79,12 +79,12 @@ struct Control { | ||||
| #endif /* SOUND_NOTIFY */ | ||||
| } Control = {0}; | ||||
|  | ||||
| struct _ActiveNotifications { | ||||
| static struct _ActiveNotifications { | ||||
| #ifdef SOUND_NOTIFY | ||||
|     uint32_t source; | ||||
|     uint32_t buffer; | ||||
|     bool looping; | ||||
| #endif | ||||
| #endif /* SOUND_NOTIFY */ | ||||
|     bool active; | ||||
|     int *id_indicator; | ||||
| #ifdef BOX_NOTIFY | ||||
| @@ -93,7 +93,7 @@ struct _ActiveNotifications { | ||||
|     char title[64]; | ||||
|     size_t size; | ||||
|     time_t n_timeout; | ||||
| #endif | ||||
| #endif /* BOX_NOTIFY */ | ||||
| } actives[ACTIVE_NOTIFS_MAX]; | ||||
| /**********************************************************************************/ | ||||
| /**********************************************************************************/ | ||||
| @@ -101,24 +101,40 @@ struct _ActiveNotifications { | ||||
| /**********************************************************************************/ | ||||
| /**********************************************************************************/ | ||||
|  | ||||
| static void clear_actives_index(size_t idx) | ||||
| { | ||||
|     if (actives[idx].id_indicator) { | ||||
|         *actives[idx].id_indicator = -1; | ||||
|     } | ||||
|  | ||||
|     actives[idx] = (struct _ActiveNotifications) { | ||||
|         0 | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /* coloured tab notifications: primary notification type */ | ||||
| static void tab_notify(ToxWindow *self, uint64_t flags) | ||||
| { | ||||
|     if (self == NULL) | ||||
|     if (self == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (flags & NT_WNDALERT_0) | ||||
|     if (flags & NT_WNDALERT_0) { | ||||
|         self->alert = WINDOW_ALERT_0; | ||||
|     else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) ) | ||||
|     } else if ((flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0)) { | ||||
|         self->alert = WINDOW_ALERT_1; | ||||
|     else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) ) | ||||
|     } else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) { | ||||
|         self->alert = WINDOW_ALERT_2; | ||||
|     } | ||||
|  | ||||
|     ++self->pending_messages; | ||||
| } | ||||
|  | ||||
| static bool notifications_are_disabled(uint64_t flags) | ||||
| { | ||||
|     if (user_settings->alerts != ALERTS_ENABLED) | ||||
|     if (user_settings->alerts != ALERTS_ENABLED) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time()); | ||||
| #ifdef X11 | ||||
| @@ -128,14 +144,14 @@ static bool notifications_are_disabled(uint64_t flags) | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static void control_lock() | ||||
| static void control_lock(void) | ||||
| { | ||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||
|     pthread_mutex_lock(Control.poll_mutex); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static void control_unlock() | ||||
| static void control_unlock(void) | ||||
| { | ||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||
|     pthread_mutex_unlock(Control.poll_mutex); | ||||
| @@ -157,21 +173,25 @@ static bool device_opened = false; | ||||
| time_t last_opened_update = 0; | ||||
|  | ||||
| /* Opens primary device. Returns true on succe*/ | ||||
| void m_open_device() | ||||
| void m_open_device(void) | ||||
| { | ||||
|     last_opened_update = get_unix_time(); | ||||
|  | ||||
|     if (device_opened) return; | ||||
|     if (device_opened) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Blah error check */ | ||||
|     open_primary_device(output, &Control.device_idx, 48000, 20, 1); | ||||
|     open_output_device(&Control.device_idx, 48000, 20, 1); | ||||
|  | ||||
|     device_opened = true; | ||||
| } | ||||
|  | ||||
| void m_close_device() | ||||
| void m_close_device(void) | ||||
| { | ||||
|     if (!device_opened) return; | ||||
|     if (!device_opened) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     close_device(output, Control.device_idx); | ||||
|  | ||||
| @@ -179,14 +199,14 @@ void m_close_device() | ||||
| } | ||||
|  | ||||
| /* Terminate all sounds but wait for them to finish first */ | ||||
| void graceful_clear() | ||||
| void graceful_clear(void) | ||||
| { | ||||
|     control_lock(); | ||||
|  | ||||
|     while (1) { | ||||
|         int i; | ||||
|  | ||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||
|             if (actives[i].active) { | ||||
| #ifdef BOX_NOTIFY | ||||
|  | ||||
| @@ -196,17 +216,20 @@ void graceful_clear() | ||||
|                     actives[i].box = NULL; | ||||
|                 } | ||||
|  | ||||
| #endif | ||||
| #endif /* BOX_NOTIFY */ | ||||
|  | ||||
|                 if (actives[i].id_indicator) | ||||
|                 if (actives[i].id_indicator) { | ||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|                 } | ||||
|  | ||||
|                 if ( actives[i].looping ) { | ||||
|                 if (actives[i].looping) { | ||||
|                     stop_sound(i); | ||||
|                 } else { | ||||
|                     if (!is_playing(actives[i].source)) | ||||
|                         memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|                     else break; | ||||
|                     if (!is_playing(actives[i].source)) { | ||||
|                         clear_actives_index(i); | ||||
|                     } else { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -217,7 +240,7 @@ void graceful_clear() | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         usleep(1000); | ||||
|         sleep_thread(1000L); | ||||
|     } | ||||
|  | ||||
|     control_unlock(); | ||||
| @@ -225,7 +248,7 @@ void graceful_clear() | ||||
|  | ||||
| void *do_playing(void *_p) | ||||
| { | ||||
|     (void)_p; | ||||
|     UNUSED_VAR(_p); | ||||
|  | ||||
|     while (true) { | ||||
|         control_lock(); | ||||
| @@ -239,9 +262,11 @@ void *do_playing(void *_p) | ||||
|         bool test_active_notify = false; | ||||
|         int i; | ||||
|  | ||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||
|  | ||||
|             if (actives[i].looping) has_looping = true; | ||||
|             if (actives[i].looping) { | ||||
|                 has_looping = true; | ||||
|             } | ||||
|  | ||||
|             test_active_notify = actives[i].active && !actives[i].looping; | ||||
| #ifdef BOX_NOTIFY | ||||
| @@ -249,15 +274,16 @@ void *do_playing(void *_p) | ||||
| #endif | ||||
|  | ||||
|             if (test_active_notify) { | ||||
|                 if (actives[i].id_indicator) | ||||
|                 if (actives[i].id_indicator) { | ||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|                 } | ||||
|  | ||||
|                 if (!is_playing(actives[i].source)) { | ||||
|                     /* Close */ | ||||
|                     alSourceStop(actives[i].source); | ||||
|                     alDeleteSources(1, &actives[i].source); | ||||
|                     alDeleteBuffers(1, &actives[i].buffer); | ||||
|                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|                     clear_actives_index(i); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -267,19 +293,20 @@ void *do_playing(void *_p) | ||||
|                 notify_notification_close(actives[i].box, &ignore); | ||||
|                 actives[i].box = NULL; | ||||
|  | ||||
|                 if (actives[i].id_indicator) | ||||
|                 if (actives[i].id_indicator) { | ||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|                 } | ||||
|  | ||||
|                 if (!actives[i].looping && !is_playing(actives[i].source)) { | ||||
|                     /* stop source if not looping or playing, just terminate box */ | ||||
|                     alSourceStop(actives[i].source); | ||||
|                     alDeleteSources(1, &actives[i].source); | ||||
|                     alDeleteBuffers(1, &actives[i].buffer); | ||||
|                     memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|                     clear_actives_index(i); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| #endif | ||||
| #endif /* BOX_NOTIFY */ | ||||
|         } | ||||
|  | ||||
|         /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/ | ||||
| @@ -291,7 +318,7 @@ void *do_playing(void *_p) | ||||
|         has_looping = false; | ||||
|  | ||||
|         control_unlock(); | ||||
|         usleep(10000); | ||||
|         sleep_thread(10000L); | ||||
|     } | ||||
|  | ||||
|     pthread_exit(NULL); | ||||
| @@ -301,9 +328,9 @@ int play_source(uint32_t source, uint32_t buffer, bool looping) | ||||
| { | ||||
|     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 */ | ||||
|     } | ||||
|  | ||||
| @@ -320,7 +347,7 @@ int play_source(uint32_t source, uint32_t buffer, bool looping) | ||||
| #elif BOX_NOTIFY | ||||
| void *do_playing(void *_p) | ||||
| { | ||||
|     (void)_p; | ||||
|     UNUSED_VAR(_p); | ||||
|  | ||||
|     while (true) { | ||||
|         control_lock(); | ||||
| @@ -330,49 +357,64 @@ void *do_playing(void *_p) | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         int i; | ||||
|  | ||||
|         for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||
|         for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||
|             if (actives[i].box && time(NULL) >= actives[i].n_timeout) { | ||||
|                 GError *ignore; | ||||
|                 notify_notification_close(actives[i].box, &ignore); | ||||
|                 actives[i].box = NULL; | ||||
|  | ||||
|                 if (actives[i].id_indicator) | ||||
|                     *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|  | ||||
|                 memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|                 clear_actives_index(i); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         control_unlock(); | ||||
|         usleep(10000); | ||||
|         sleep_thread(10000L); | ||||
|     } | ||||
|  | ||||
|     pthread_exit(NULL); | ||||
| } | ||||
|  | ||||
| void graceful_clear() | ||||
| void graceful_clear(void) | ||||
| { | ||||
|     int i; | ||||
|     control_lock(); | ||||
|  | ||||
|     for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { | ||||
|     for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||
|         if (actives[i].box) { | ||||
|             GError *ignore; | ||||
|             notify_notification_close(actives[i].box, &ignore); | ||||
|             actives[i].box = NULL; | ||||
|         } | ||||
|  | ||||
|         if (actives[i].id_indicator) | ||||
|             *actives[i].id_indicator = -1;    /* reset indicator value */ | ||||
|  | ||||
|         memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); | ||||
|         clear_actives_index(i); | ||||
|     } | ||||
|  | ||||
|     control_unlock(); | ||||
| } | ||||
|  | ||||
| #endif /* SOUND_NOTIFY */ | ||||
|  | ||||
| /* Kills all notifications for `id`. This must be called before freeing a ToxWindow. */ | ||||
| void kill_notifs(int id) | ||||
| { | ||||
|     control_lock(); | ||||
|  | ||||
|     for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) { | ||||
|         if (!actives[i].id_indicator) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (*actives[i].id_indicator == id) { | ||||
| #ifdef BOX_NOTIFY | ||||
|  | ||||
|             if (actives[i].box) { | ||||
|                 GError *ignore; | ||||
|                 notify_notification_close(actives[i].box, &ignore); | ||||
|             } | ||||
|  | ||||
| #endif // BOX_NOTIFY | ||||
|             clear_actives_index(i); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     control_unlock(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /**********************************************************************************/ | ||||
| /**********************************************************************************/ | ||||
| @@ -391,19 +433,20 @@ int init_notify(int login_cooldown, int notification_timeout) | ||||
|  | ||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||
|  | ||||
|     if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) | ||||
|     if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     Control.poll_active = 1; | ||||
|     pthread_t thread; | ||||
|  | ||||
|     if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) { | ||||
|     if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0) { | ||||
|         pthread_mutex_destroy(Control.poll_mutex); | ||||
|         Control.poll_active = 0; | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| #endif | ||||
| #endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */ | ||||
|     Control.cooldown = time(NULL) + login_cooldown; | ||||
|  | ||||
|  | ||||
| @@ -414,12 +457,12 @@ int init_notify(int login_cooldown, int notification_timeout) | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| void terminate_notify() | ||||
| void terminate_notify(void) | ||||
| { | ||||
| #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) | ||||
|     control_lock(); | ||||
|  | ||||
|     if ( !Control.poll_active ) { | ||||
|     if (!Control.poll_active) { | ||||
|         control_unlock(); | ||||
|         return; | ||||
|     } | ||||
| @@ -428,12 +471,14 @@ void terminate_notify() | ||||
|     control_unlock(); | ||||
|  | ||||
|     graceful_clear(); | ||||
| #endif | ||||
| #endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */ | ||||
|  | ||||
| #ifdef SOUND_NOTIFY | ||||
|     int i = 0; | ||||
|  | ||||
|     for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]); | ||||
|     for (; i < SOUNDS_SIZE; ++i) { | ||||
|         free(Control.sounds[i]); | ||||
|     } | ||||
|  | ||||
|     alutExit(); | ||||
| #endif /* SOUND_NOTIFY */ | ||||
| @@ -446,7 +491,9 @@ void terminate_notify() | ||||
| #ifdef SOUND_NOTIFY | ||||
| int set_sound(Notification sound, const char *value) | ||||
| { | ||||
|     if (sound == silent) return 0; | ||||
|     if (sound == silent) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     free(Control.sounds[sound]); | ||||
|  | ||||
| @@ -487,11 +534,14 @@ int play_notify_sound(Notification notif, uint64_t flags) | ||||
| { | ||||
|     int rc = -1; | ||||
|  | ||||
|     if (flags & NT_BEEP) beep(); | ||||
|     if (flags & NT_BEEP) { | ||||
|         beep(); | ||||
|     } | ||||
|  | ||||
|     if (notif != silent) { | ||||
|         if ( !Control.poll_active || !Control.sounds[notif] ) | ||||
|         if (!Control.poll_active || !Control.sounds[notif]) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0); | ||||
|     } | ||||
| @@ -499,10 +549,9 @@ int play_notify_sound(Notification notif, uint64_t flags) | ||||
|     return rc; | ||||
| } | ||||
|  | ||||
|  | ||||
| void stop_sound(int id) | ||||
| { | ||||
|     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) { | ||||
|     if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active) { | ||||
| #ifdef BOX_NOTIFY | ||||
|  | ||||
|         if (actives[id].box) { | ||||
| @@ -510,19 +559,16 @@ void stop_sound(int id) | ||||
|             notify_notification_close(actives[id].box, &ignore); | ||||
|         } | ||||
|  | ||||
| #endif | ||||
| #endif /* BOX_NOTIFY */ | ||||
|  | ||||
|         if (actives[id].id_indicator) | ||||
|             *actives[id].id_indicator = -1; | ||||
|  | ||||
| //         alSourcei(actives[id].source, AL_LOOPING, false); | ||||
|         // alSourcei(actives[id].source, AL_LOOPING, false); | ||||
|         alSourceStop(actives[id].source); | ||||
|         alDeleteSources(1, &actives[id].source); | ||||
|         alDeleteBuffers(1, &actives[id].buffer); | ||||
|         memset(&actives[id], 0, sizeof(struct _ActiveNotifications)); | ||||
|         clear_actives_index(id); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
| #endif /* SOUND_NOTIFY */ | ||||
|  | ||||
| static int m_play_sound(Notification notif, uint64_t flags) | ||||
| { | ||||
| @@ -530,48 +576,45 @@ static int m_play_sound(Notification notif, uint64_t flags) | ||||
|     return play_notify_sound(notif, flags); | ||||
| #else | ||||
|  | ||||
|     if (notif != silent) | ||||
|     if (notif != silent) { | ||||
|         beep(); | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| #endif /* SOUND_NOTIFY */ | ||||
| } | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
| void m_notify_action(NotifyNotification *box, char *action, void *data) | ||||
| { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator) | ||||
| { | ||||
|     tab_notify(self, flags); | ||||
|  | ||||
|     if (notifications_are_disabled(flags)) | ||||
|     if (notifications_are_disabled(flags)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int id = -1; | ||||
|     control_lock(); | ||||
|  | ||||
|     if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY)) | ||||
|     if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY)) { | ||||
|         id = m_play_sound(notif, flags); | ||||
|     else if (flags & NT_ALWAYS) | ||||
|     } else if (flags & NT_ALWAYS) { | ||||
|         id = m_play_sound(notif, flags); | ||||
|     } | ||||
|  | ||||
| #if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) | ||||
|  | ||||
|     if (id == -1) { | ||||
|         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++); | ||||
|  | ||||
|         if ( id == ACTIVE_NOTIFS_MAX ) { | ||||
|         if (id == ACTIVE_NOTIFS_MAX) { | ||||
|             control_unlock(); | ||||
|             return -1; /* Full */ | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #endif | ||||
| #endif /* defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) */ | ||||
|  | ||||
|     if ( id_indicator && id != -1 ) { | ||||
|     if (id_indicator && id != -1) { | ||||
|         actives[id].id_indicator = id_indicator; | ||||
|         *id_indicator = id; | ||||
|     } | ||||
| @@ -585,10 +628,13 @@ int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id) | ||||
| { | ||||
|     tab_notify(self, flags); | ||||
|  | ||||
|     if (notifications_are_disabled(flags)) | ||||
|     if (notifications_are_disabled(flags)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1; | ||||
|     if (id < 0 || id >= ACTIVE_NOTIFS_MAX) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| #ifdef SOUND_NOTIFY | ||||
|     control_lock(); | ||||
| @@ -617,8 +663,9 @@ int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id) | ||||
|     return id; | ||||
| #else | ||||
|  | ||||
|     if (notif != silent) | ||||
|     if (notif != silent) { | ||||
|         beep(); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| #endif /* SOUND_NOTIFY */ | ||||
| @@ -644,7 +691,7 @@ int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indi | ||||
|  | ||||
|         for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); | ||||
|  | ||||
|         if ( id == ACTIVE_NOTIFS_MAX ) { | ||||
|         if (id == ACTIVE_NOTIFS_MAX) { | ||||
|             control_unlock(); | ||||
|             return -1; /* Full */ | ||||
|         } | ||||
| @@ -652,27 +699,33 @@ int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indi | ||||
|         actives[id].active = 1; | ||||
|         actives[id].id_indicator = id_indicator; | ||||
|  | ||||
|         if (id_indicator) *id_indicator = id; | ||||
|         if (id_indicator) { | ||||
|             *id_indicator = id; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #else | ||||
|  | ||||
|     if (id == -1) | ||||
|     if (id == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| #endif    /* SOUND_NOTIFY */ | ||||
| #endif /* SOUND_NOTIFY */ | ||||
|  | ||||
|     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); | ||||
|  | ||||
|     if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); | ||||
|     if (strlen(title) > 23) { | ||||
|         strcpy(actives[id].title + 20, "..."); | ||||
|     } | ||||
|  | ||||
|     va_list __ARGS__; | ||||
|     va_start (__ARGS__, format); | ||||
|     vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end (__ARGS__); | ||||
|     va_start(__ARGS__, format); | ||||
|     vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end(__ARGS__); | ||||
|  | ||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) | ||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) { | ||||
|         strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); | ||||
|     } | ||||
|  | ||||
|     actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); | ||||
|     actives[id].size++; | ||||
| @@ -687,7 +740,7 @@ int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indi | ||||
|     return id; | ||||
| #else | ||||
|     return sound_notify(self, notif, flags, id_indicator); | ||||
| #endif   /* BOX_NOTIFY */ | ||||
| #endif /* BOX_NOTIFY */ | ||||
| } | ||||
|  | ||||
| int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...) | ||||
| @@ -699,8 +752,9 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
|  | ||||
|     if (sound_notify2(self, notif, flags, id) == -1) | ||||
|     if (sound_notify2(self, notif, flags, id) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     control_lock(); | ||||
|  | ||||
| @@ -710,44 +764,44 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con | ||||
|     } | ||||
|  | ||||
|     va_list __ARGS__; | ||||
|     va_start (__ARGS__, format); | ||||
|     vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end (__ARGS__); | ||||
|     va_start(__ARGS__, format); | ||||
|     vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end(__ARGS__); | ||||
|  | ||||
|     if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) | ||||
|     if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) { | ||||
|         strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); | ||||
|     } | ||||
|  | ||||
|     actives[id].size++; | ||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; | ||||
|  | ||||
|     char formated[128 * 129] = {'\0'}; | ||||
|     char *formatted = calloc(1, sizeof(char) * ((MAX_BOX_MSG_LEN + 1) * (MAX_BOX_MSG_LEN + 2))); | ||||
|  | ||||
|     int i = 0; | ||||
|  | ||||
|     for (; i < actives[id].size; i ++) { | ||||
|         strcat(formated, actives[id].messages[i]); | ||||
|         strcat(formated, "\n"); | ||||
|     for (size_t i = 0; i < actives[id].size; ++i) { | ||||
|         strcat(formatted, actives[id].messages[i]); | ||||
|         strcat(formatted, "\n"); | ||||
|     } | ||||
|  | ||||
|     formated[strlen(formated) - 1] = '\0'; | ||||
|  | ||||
|     notify_notification_update(actives[id].box, actives[id].title, formated, NULL); | ||||
|     notify_notification_update(actives[id].box, actives[id].title, formatted, NULL); | ||||
|     notify_notification_show(actives[id].box, NULL); | ||||
|  | ||||
|     free(formatted); | ||||
|  | ||||
|     control_unlock(); | ||||
|  | ||||
|     return id; | ||||
| #else | ||||
|     return sound_notify2(self, notif, flags, id); | ||||
| #endif | ||||
| #endif /* BOX_NOTIFY */ | ||||
| } | ||||
|  | ||||
| int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...) | ||||
| { | ||||
|     tab_notify(self, flags); | ||||
|  | ||||
|     if (notifications_are_disabled(flags)) | ||||
|     if (notifications_are_disabled(flags)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
|  | ||||
| @@ -757,7 +811,7 @@ int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const | ||||
|  | ||||
|     for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); | ||||
|  | ||||
|     if ( id == ACTIVE_NOTIFS_MAX ) { | ||||
|     if (id == ACTIVE_NOTIFS_MAX) { | ||||
|         control_unlock(); | ||||
|         return -1; /* Full */ | ||||
|     } | ||||
| @@ -769,15 +823,18 @@ int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const | ||||
|  | ||||
|     snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); | ||||
|  | ||||
|     if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); | ||||
|     if (strlen(title) > 23) { | ||||
|         strcpy(actives[id].title + 20, "..."); | ||||
|     } | ||||
|  | ||||
|     va_list __ARGS__; | ||||
|     va_start (__ARGS__, format); | ||||
|     vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end (__ARGS__); | ||||
|     va_start(__ARGS__, format); | ||||
|     vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end(__ARGS__); | ||||
|  | ||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) | ||||
|     if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) { | ||||
|         strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); | ||||
|     } | ||||
|  | ||||
|     actives[id].active = 1; | ||||
|     actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); | ||||
| @@ -793,54 +850,54 @@ int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const | ||||
|     return id; | ||||
| #else | ||||
|     return -1; | ||||
| #endif | ||||
| #endif /* BOX_NOTIFY */ | ||||
| } | ||||
|  | ||||
| int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...) | ||||
| { | ||||
|     tab_notify(self, flags); | ||||
|  | ||||
|     if (notifications_are_disabled(flags)) | ||||
|     if (notifications_are_disabled(flags)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| #ifdef BOX_NOTIFY | ||||
|     control_lock(); | ||||
|  | ||||
|     if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) { | ||||
|     if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) { | ||||
|         control_unlock(); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     va_list __ARGS__; | ||||
|     va_start (__ARGS__, format); | ||||
|     vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end (__ARGS__); | ||||
|     va_start(__ARGS__, format); | ||||
|     vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); | ||||
|     va_end(__ARGS__); | ||||
|  | ||||
|     if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) | ||||
|     if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) { | ||||
|         strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); | ||||
|     } | ||||
|  | ||||
|     actives[id].size ++; | ||||
|     actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; | ||||
|  | ||||
|     char formated[128 * 129] = {'\0'}; | ||||
|     char *formatted = calloc(1, sizeof(char) * ((MAX_BOX_MSG_LEN + 1) * (MAX_BOX_MSG_LEN + 2))); | ||||
|  | ||||
|     int i = 0; | ||||
|  | ||||
|     for (; i < actives[id].size; i ++) { | ||||
|         strcat(formated, actives[id].messages[i]); | ||||
|         strcat(formated, "\n"); | ||||
|     for (size_t i = 0; i < actives[id].size; ++i) { | ||||
|         strcat(formatted, actives[id].messages[i]); | ||||
|         strcat(formatted, "\n"); | ||||
|     } | ||||
|  | ||||
|     formated[strlen(formated) - 1] = '\0'; | ||||
|  | ||||
|     notify_notification_update(actives[id].box, actives[id].title, formated, NULL); | ||||
|     notify_notification_update(actives[id].box, actives[id].title, formatted, NULL); | ||||
|     notify_notification_show(actives[id].box, NULL); | ||||
|  | ||||
|     free(formatted); | ||||
|  | ||||
|     control_unlock(); | ||||
|  | ||||
|     return id; | ||||
| #else | ||||
|     return -1; | ||||
| #endif | ||||
| #endif /* BOX_NOTIFY */ | ||||
| } | ||||
|   | ||||
| @@ -23,7 +23,7 @@ | ||||
| #ifndef NOTIFY_H | ||||
| #define NOTIFY_H | ||||
|  | ||||
| #include <inttypes.h> | ||||
| #include <stdint.h> | ||||
| #include "windows.h" | ||||
|  | ||||
| typedef enum _Notification { | ||||
| @@ -60,7 +60,10 @@ typedef enum _Flags { | ||||
| } Flags; | ||||
|  | ||||
| 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_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id); | ||||
|   | ||||
| @@ -45,7 +45,7 @@ void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t * | ||||
| #endif /* __OBJC__ */ | ||||
|  | ||||
| int osx_video_init(char **device_names, int *size); | ||||
| void osx_video_release(); | ||||
| void osx_video_release(void); | ||||
| /* Start device */ | ||||
| int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height); | ||||
| /* Stop device */ | ||||
|   | ||||
| @@ -144,8 +144,9 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | ||||
|         device_names[i] = video_input_name; | ||||
|     } | ||||
|  | ||||
|     if ( i <= 0 ) | ||||
|     if (i <= 0) { | ||||
|         return nil; | ||||
|     } | ||||
|  | ||||
|     *size = i; | ||||
|  | ||||
| @@ -174,7 +175,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | ||||
|     NSError *error = NULL; | ||||
|     AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error]; | ||||
|  | ||||
|     if ( error != NULL ) { | ||||
|     if (error != NULL) { | ||||
|         [input release]; | ||||
|         return -1; | ||||
|     } | ||||
| @@ -193,7 +194,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | ||||
|     AVCaptureInputPort *port = [input.ports objectAtIndex: 0]; | ||||
|     CMFormatDescriptionRef format_description = port.formatDescription; | ||||
|  | ||||
|     if ( format_description ) { | ||||
|     if (format_description) { | ||||
|         CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description); | ||||
|         *width = dimensions.width; | ||||
|         *height = dimensions.height; | ||||
| @@ -208,10 +209,10 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t | ||||
|     // TODO possibly get a better pixel format | ||||
|     if (_shouldMangleDimensions) { | ||||
|         [_linkerVideo setVideoSettings: @ { | ||||
| (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), | ||||
| (id)kCVPixelBufferWidthKey: @640, | ||||
| (id)kCVPixelBufferHeightKey: @480 | ||||
|         }]; | ||||
|              (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), | ||||
|              (id)kCVPixelBufferWidthKey: @640, | ||||
|              (id)kCVPixelBufferHeightKey: @480 | ||||
|                      }]; | ||||
|     } else { | ||||
|         [_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}]; | ||||
|     } | ||||
| @@ -303,8 +304,9 @@ int osx_video_init(char **device_names, int *size) | ||||
| { | ||||
|     _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size]; | ||||
|  | ||||
|     if ( _OSXVideo == nil ) | ||||
|     if (_OSXVideo == nil) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -317,8 +319,9 @@ void osx_video_release() | ||||
|  | ||||
| int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height) | ||||
| { | ||||
|     if ( _OSXVideo == nil ) | ||||
|     if (_OSXVideo == nil) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height]; | ||||
| } | ||||
| @@ -330,8 +333,9 @@ void osx_video_close_device(uint32_t device_idx) | ||||
|  | ||||
| int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height) | ||||
| { | ||||
|     if ( _OSXVideo == nil ) | ||||
|     if (_OSXVideo == nil) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height]; | ||||
| } | ||||
|   | ||||
							
								
								
									
										450
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										450
									
								
								src/prompt.c
									
									
									
									
									
								
							| @@ -28,20 +28,20 @@ | ||||
| #include <string.h> | ||||
| #include <wchar.h> | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "prompt.h" | ||||
| #include "friendlist.h" | ||||
| #include "execute.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "log.h" | ||||
| #include "line_info.h" | ||||
| #include "settings.h" | ||||
| #include "input.h" | ||||
| #include "help.h" | ||||
| #include "notify.h" | ||||
| #include "autocomplete.h" | ||||
| #include "execute.h" | ||||
| #include "friendlist.h" | ||||
| #include "help.h" | ||||
| #include "input.h" | ||||
| #include "line_info.h" | ||||
| #include "log.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "prompt.h" | ||||
| #include "settings.h" | ||||
| #include "toxic.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern ToxWindow *prompt; | ||||
| extern struct user_settings *user_settings; | ||||
| @@ -49,49 +49,50 @@ extern struct Winthread Winthread; | ||||
|  | ||||
| extern FriendsList Friends; | ||||
| FriendRequests FrndRequests; | ||||
| #ifdef VIDEO | ||||
| #define AC_NUM_GLOB_COMMANDS 22 | ||||
| #elif AUDIO | ||||
| #define AC_NUM_GLOB_COMMANDS 20 | ||||
| #else | ||||
| #define AC_NUM_GLOB_COMMANDS 18 | ||||
| #endif | ||||
|  | ||||
| /* Array of global command names used for tab completion. */ | ||||
| static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { | ||||
|     { "/accept"     }, | ||||
|     { "/add"        }, | ||||
|     { "/avatar"     }, | ||||
|     { "/clear"      }, | ||||
|     { "/connect"    }, | ||||
|     { "/decline"    }, | ||||
|     { "/exit"       }, | ||||
|     { "/group"      }, | ||||
|     { "/help"       }, | ||||
|     { "/log"        }, | ||||
|     { "/myid"       }, | ||||
|     { "/myqr"       }, | ||||
|     { "/nick"       }, | ||||
|     { "/note"       }, | ||||
|     { "/nospam"     }, | ||||
|     { "/quit"       }, | ||||
|     { "/requests"   }, | ||||
|     { "/status"     }, | ||||
| static const char *glob_cmd_list[] = { | ||||
|     "/accept", | ||||
|     "/add", | ||||
|     "/avatar", | ||||
|     "/clear", | ||||
|     "/connect", | ||||
|     "/decline", | ||||
|     "/exit", | ||||
|     "/conference", | ||||
|     "/help", | ||||
|     "/log", | ||||
|     "/myid", | ||||
| #ifdef QRCODE | ||||
|     "/myqr", | ||||
| #endif /* QRCODE */ | ||||
|     "/nick", | ||||
|     "/note", | ||||
|     "/nospam", | ||||
|     "/quit", | ||||
|     "/requests", | ||||
|     "/status", | ||||
|  | ||||
| #ifdef AUDIO | ||||
|  | ||||
|     { "/lsdev"       }, | ||||
|     { "/sdev"        }, | ||||
|     "/lsdev", | ||||
|     "/sdev", | ||||
|  | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #ifdef VIDEO | ||||
|  | ||||
|     { "/lsvdev"      }, | ||||
|     { "/svdev"       }, | ||||
|     "/lsvdev", | ||||
|     "/svdev", | ||||
|  | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| #ifdef PYTHON | ||||
|  | ||||
|     "/run", | ||||
|  | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| }; | ||||
|  | ||||
| void kill_prompt_window(ToxWindow *self) | ||||
| @@ -115,8 +116,10 @@ void kill_prompt_window(ToxWindow *self) | ||||
| } | ||||
|  | ||||
| /* callback: Updates own connection status in prompt statusbar */ | ||||
| void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata) | ||||
| void on_self_connection_status(Tox *m, Tox_Connection connection_status, void *userdata) | ||||
| { | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(userdata); | ||||
|     StatusBar *statusbar = prompt->stb; | ||||
|     statusbar->connection = connection_status; | ||||
| } | ||||
| @@ -137,26 +140,35 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms | ||||
|     size_t len = strlen(statusbar->statusmsg); | ||||
|     statusbar->statusmsg_len = len; | ||||
|  | ||||
|     TOX_ERR_SET_INFO err; | ||||
|     tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err); | ||||
|     Tox_Err_Set_Info err; | ||||
|     tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_SET_INFO_OK) | ||||
|         line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err); | ||||
|     if (err != TOX_ERR_SET_INFO_OK) { | ||||
|         line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Updates own status in prompt statusbar */ | ||||
| void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status) | ||||
| void prompt_update_status(ToxWindow *prompt, Tox_User_Status status) | ||||
| { | ||||
|     StatusBar *statusbar = prompt->stb; | ||||
|     statusbar->status = status; | ||||
| } | ||||
|  | ||||
| /* Returns our own connection status */ | ||||
| Tox_Connection prompt_selfConnectionStatus(void) | ||||
| { | ||||
|     StatusBar *statusbar = prompt->stb; | ||||
|     return statusbar->connection; | ||||
| } | ||||
|  | ||||
| /* Adds friend request to pending friend requests. | ||||
|    Returns request number on success, -1 if queue is full. */ | ||||
| static int add_friend_request(const char *public_key, const char *data) | ||||
| { | ||||
|     if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS) | ||||
|     if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int i; | ||||
|  | ||||
| @@ -166,8 +178,9 @@ static int add_friend_request(const char *public_key, const char *data) | ||||
|             memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE); | ||||
|             snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data); | ||||
|  | ||||
|             if (i == FrndRequests.max_idx) | ||||
|             if (i == FrndRequests.max_idx) { | ||||
|                 ++FrndRequests.max_idx; | ||||
|             } | ||||
|  | ||||
|             ++FrndRequests.num_requests; | ||||
|  | ||||
| @@ -178,7 +191,10 @@ static int add_friend_request(const char *public_key, const char *data) | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | ||||
| /* | ||||
|  * Return true if input is recognized by handler | ||||
|  */ | ||||
| static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
| @@ -186,43 +202,64 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | ||||
|     getyx(self->window, y, x); | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (x2 <= 0 || y2 <= 0) | ||||
|         return; | ||||
|     UNUSED_VAR(y); | ||||
|  | ||||
|     if (ctx->pastemode && key == '\r') | ||||
|     if (x2 <= 0 || y2 <= 0) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (ctx->pastemode && key == '\r') { | ||||
|         key = '\n'; | ||||
|     } | ||||
|  | ||||
|     /* ignore non-menu related input if active */ | ||||
|     if (self->help->active) { | ||||
|         help_onKey(self, key); | ||||
|         return; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (ltr || key == '\n') {    /* char is printable */ | ||||
|         input_new_char(self, key, x, y, x2, y2); | ||||
|         return; | ||||
|         input_new_char(self, key, x, x2); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (line_info_onKey(self, key)) | ||||
|         return; | ||||
|     if (line_info_onKey(self, key)) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     input_handle(self, key, x, y, x2, y2); | ||||
|     if (input_handle(self, key, x, x2)) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     int input_ret = false; | ||||
|  | ||||
|     if (key == '\t') {    /* TAB key: auto-completes command */ | ||||
|         input_ret = true; | ||||
|  | ||||
|         if (ctx->len > 1 && ctx->line[0] == '/') { | ||||
|             int diff = -1; | ||||
|  | ||||
|             if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) | ||||
|             if (wcsncmp(ctx->line, L"/avatar ", wcslen(L"/avatar ")) == 0) { | ||||
|                 diff = dir_match(self, m, ctx->line, L"/avatar"); | ||||
|             } | ||||
|  | ||||
| #ifdef PYTHON | ||||
|             else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) { | ||||
|                 diff = dir_match(self, m, ctx->line, L"/run"); | ||||
|             } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|             else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { | ||||
|                 const char status_cmd_list[3][8] = { | ||||
|                     {"online"}, | ||||
|                     {"away"}, | ||||
|                     {"busy"}, | ||||
|                 const char *status_cmd_list[] = { | ||||
|                     "online", | ||||
|                     "away", | ||||
|                     "busy", | ||||
|                 }; | ||||
|                 diff = complete_line(self, status_cmd_list, 3, 8); | ||||
|             } else | ||||
|                 diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); | ||||
|                 diff = complete_line(self, status_cmd_list, sizeof(status_cmd_list) / sizeof(char *)); | ||||
|             } else { | ||||
|                 diff = complete_line(self, glob_cmd_list, sizeof(glob_cmd_list) / sizeof(char *)); | ||||
|             } | ||||
|  | ||||
|             if (diff != -1) { | ||||
|                 if (x + diff > x2 - 1) { | ||||
| @@ -236,34 +273,41 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) | ||||
|             sound_notify(self, notif_error, 0, NULL); | ||||
|         } | ||||
|     } else if (key == '\r') { | ||||
|         input_ret = true; | ||||
|  | ||||
|         rm_trailing_spaces_buf(ctx); | ||||
|  | ||||
|         if (!wstring_is_empty(ctx->line)) { | ||||
|             add_line_to_hist(ctx); | ||||
|             wstrsubst(ctx->line, L'¶', L'\n'); | ||||
|  | ||||
|             char line[MAX_STR_SIZE] = {0}; | ||||
|             char line[MAX_STR_SIZE]; | ||||
|  | ||||
|             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) | ||||
|                 memset(&line, 0, sizeof(line)); | ||||
|  | ||||
|             line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); | ||||
|             execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); | ||||
|             if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { | ||||
|                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message."); | ||||
|             } else { | ||||
|                 line_info_add(self, false, NULL, NULL, PROMPT, 0, 0, "%s", line); | ||||
|                 execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         wclear(ctx->linewin); | ||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||
|         wmove(self->window, y2, 0); | ||||
|         reset_buf(ctx); | ||||
|     } | ||||
|  | ||||
|     return input_ret; | ||||
| } | ||||
|  | ||||
| static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||
| { | ||||
|     int x2, y2; | ||||
|     int x2; | ||||
|     int y2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) | ||||
|     if (y2 <= 0 || x2 <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
| @@ -273,70 +317,95 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||
|  | ||||
|     wclear(ctx->linewin); | ||||
|  | ||||
|     curs_set(1); | ||||
|     if (ctx->len > 0) { | ||||
|         mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]); | ||||
|     } | ||||
|  | ||||
|     if (ctx->len > 0) | ||||
|         mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); | ||||
|     mvwhline(ctx->linewin, 0, ctx->len, ' ', x2 - ctx->len); | ||||
|  | ||||
|     curs_set(1); | ||||
|  | ||||
|     StatusBar *statusbar = self->stb; | ||||
|  | ||||
|     mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2); | ||||
|     wmove(statusbar->topline, 0, 0); | ||||
|  | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     TOX_CONNECTION connection = statusbar->connection; | ||||
|     Tox_Connection connection = statusbar->connection; | ||||
|     Tox_User_Status status = statusbar->status; | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     if (connection != TOX_CONNECTION_NONE) { | ||||
|         int colour = MAGENTA; | ||||
|         const char *status_text = "ERROR"; | ||||
|  | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         TOX_USER_STATUS status = statusbar->status; | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|         switch (status) { | ||||
|             case TOX_USER_STATUS_NONE: | ||||
|                 status_text = "Online"; | ||||
|                 colour = GREEN; | ||||
|                 colour = STATUS_ONLINE; | ||||
|                 break; | ||||
|  | ||||
|             case TOX_USER_STATUS_AWAY: | ||||
|                 status_text = "Away"; | ||||
|                 colour = YELLOW; | ||||
|                 colour = STATUS_AWAY; | ||||
|                 break; | ||||
|  | ||||
|             case TOX_USER_STATUS_BUSY: | ||||
|                 status_text = "Busy"; | ||||
|                 colour = RED; | ||||
|                 colour = STATUS_BUSY; | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); | ||||
|         wprintw(statusbar->topline, " [%s]", status_text); | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|         wprintw(statusbar->topline, " ["); | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|  | ||||
|         wattron(statusbar->topline, A_BOLD | COLOR_PAIR(colour)); | ||||
|         wprintw(statusbar->topline, "%s", status_text); | ||||
|         wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(colour)); | ||||
|  | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|         wprintw(statusbar->topline, "]"); | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|  | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||
|  | ||||
|         wattron(statusbar->topline, A_BOLD); | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         wprintw(statusbar->topline, " %s", statusbar->nick); | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|         wattroff(statusbar->topline, A_BOLD); | ||||
|     } else { | ||||
|         wprintw(statusbar->topline, " [Offline]"); | ||||
|         wattron(statusbar->topline, A_BOLD); | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|         wprintw(statusbar->topline, " ["); | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|  | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||
|         wprintw(statusbar->topline, "Offline"); | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||
|  | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|         wprintw(statusbar->topline, "]"); | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|  | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||
|  | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         wprintw(statusbar->topline, " %s", statusbar->nick); | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|         wattroff(statusbar->topline, A_BOLD); | ||||
|     } | ||||
|  | ||||
|     int s_y; | ||||
|     int s_x; | ||||
|     getyx(statusbar->topline, s_y, s_x); | ||||
|  | ||||
|     mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x); | ||||
|     wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||
|  | ||||
|     /* Reset statusbar->statusmsg on window resize */ | ||||
|     if (x2 != self->x) { | ||||
|         char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; | ||||
|  | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         size_t slen = tox_self_get_status_message_size(m); | ||||
|         tox_self_get_status_message (m, (uint8_t *) statusmsg); | ||||
|         tox_self_get_status_message(m, (uint8_t *) statusmsg); | ||||
|         statusmsg[slen] = '\0'; | ||||
|         snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); | ||||
|         statusbar->statusmsg_len = strlen(statusbar->statusmsg); | ||||
| @@ -356,38 +425,47 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) | ||||
|         statusbar->statusmsg_len = maxlen; | ||||
|     } | ||||
|  | ||||
|     if (statusbar->statusmsg[0]) | ||||
|         wprintw(statusbar->topline, " : %s", statusbar->statusmsg); | ||||
|     if (statusbar->statusmsg[0]) { | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|         wprintw(statusbar->topline, " | "); | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT)); | ||||
|  | ||||
|         wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||
|         wprintw(statusbar->topline, "%s", statusbar->statusmsg); | ||||
|         wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT)); | ||||
|     } | ||||
|  | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); | ||||
|  | ||||
|     int y, x; | ||||
|     int y; | ||||
|     int x; | ||||
|     getyx(self->window, y, x); | ||||
|     (void) x; | ||||
|  | ||||
|     UNUSED_VAR(x); | ||||
|  | ||||
|     int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); | ||||
|     wmove(self->window, y + 1, new_x); | ||||
|     wmove(self->window, y, new_x); | ||||
|  | ||||
|     draw_window_bar(self); | ||||
|  | ||||
|     wnoutrefresh(self->window); | ||||
|  | ||||
|     if (self->help->active) | ||||
|     if (self->help->active) { | ||||
|         help_onDraw(self); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status) | ||||
| static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum, Tox_Connection connection_status) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     char nick[TOX_MAX_NAME_LENGTH] = {0};    /* stop removing this initiation */ | ||||
|     get_nick_truncate(m, nick, friendnum); | ||||
|  | ||||
|     if (!nick[0]) | ||||
|     if (!nick[0]) { | ||||
|         snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); | ||||
|     } | ||||
|  | ||||
|     char timefrmt[TIME_STR_SIZE]; | ||||
|     get_time_str(timefrmt, sizeof(timefrmt)); | ||||
|     const char *msg; | ||||
|  | ||||
|     if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) { | ||||
| @@ -396,60 +474,63 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu | ||||
|  | ||||
|     if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) { | ||||
|         msg = "has come online"; | ||||
|         line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); | ||||
|         line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg); | ||||
|         write_to_log(msg, nick, ctx->log, true); | ||||
|  | ||||
|         if (self->active_box != -1) | ||||
|         if (self->active_box != -1) { | ||||
|             box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box, | ||||
|                         "%s has come online", nick ); | ||||
|         else | ||||
|                         "%s has come online", nick); | ||||
|         } else { | ||||
|             box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, | ||||
|                        "Toxic", "%s has come online", nick ); | ||||
|                        "Toxic", "%s has come online", nick); | ||||
|         } | ||||
|     } else if (connection_status == TOX_CONNECTION_NONE) { | ||||
|         msg = "has gone offline"; | ||||
|         line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); | ||||
|         line_info_add(self, true, nick, NULL, DISCONNECTION, 0, RED, msg); | ||||
|         write_to_log(msg, nick, ctx->log, true); | ||||
|  | ||||
|         if (self->active_box != -1) | ||||
|         if (self->active_box != -1) { | ||||
|             box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box, | ||||
|                         "%s has gone offline", nick ); | ||||
|         else | ||||
|                         "%s has gone offline", nick); | ||||
|         } else { | ||||
|             box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, | ||||
|                        "Toxic", "%s has gone offline", nick ); | ||||
|                        "Toxic", "%s has gone offline", nick); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length) | ||||
| { | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(length); | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     char timefrmt[TIME_STR_SIZE]; | ||||
|     get_time_str(timefrmt, sizeof(timefrmt)); | ||||
|  | ||||
|     line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data); | ||||
|     line_info_add(self, true, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data); | ||||
|     write_to_log("Friend request with the message '%s'", "", ctx->log, true); | ||||
|  | ||||
|     int n = add_friend_request(key, data); | ||||
|  | ||||
|     if (n == -1) { | ||||
|         const char *errmsg = "Friend request queue is full. Discarding request."; | ||||
|         line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n); | ||||
|     sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL); | ||||
| } | ||||
|  | ||||
| void prompt_init_statusbar(ToxWindow *self, Tox *m) | ||||
| void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run) | ||||
| { | ||||
|     int x2, y2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) | ||||
|     if (y2 <= 0 || x2 <= 0) { | ||||
|         exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES); | ||||
|     } | ||||
|  | ||||
|     (void) y2; | ||||
|     UNUSED_VAR(y2); | ||||
|  | ||||
|     /* Init statusbar info */ | ||||
|     StatusBar *statusbar = self->stb; | ||||
| @@ -465,12 +546,12 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m) | ||||
|     size_t s_len = tox_self_get_status_message_size(m); | ||||
|     tox_self_get_status_message(m, (uint8_t *) statusmsg); | ||||
|  | ||||
|     TOX_USER_STATUS status = tox_self_get_status(m); | ||||
|     Tox_User_Status status = tox_self_get_status(m); | ||||
|  | ||||
|     nick[n_len] = '\0'; | ||||
|     statusmsg[s_len] = '\0'; | ||||
|  | ||||
|     if (s_len == 0 || !strncmp(statusmsg, "Toxing on Toxic", strlen("Toxing on Toxic"))) { | ||||
|     if (first_time_run) { | ||||
|         snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic"); | ||||
|         s_len = strlen(statusmsg); | ||||
|         statusmsg[s_len] = '\0'; | ||||
| @@ -481,90 +562,113 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m) | ||||
|     prompt_update_nick(prompt, nick); | ||||
|  | ||||
|     /* Init statusbar subwindow */ | ||||
|     statusbar->topline = subwin(self->window, 2, x2, 0, 0); | ||||
|     statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0); | ||||
| } | ||||
|  | ||||
| static void print_welcome_msg(ToxWindow *self) | ||||
| { | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "    _____ _____  _____ ____ "); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|"); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    "); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ "); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____| v." TOXICVER); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "    _____ _____  _____ ____ "); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "   |_   _/ _ \\ \\/ /_ _/ ___|"); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     | || | | \\  / | | |    "); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     | || |_| /  \\ | | |___ "); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, "     |_| \\___/_/\\_\\___\\____| v." TOXICVER); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|  | ||||
|     const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client."; | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg); | ||||
|     const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messaging client."; | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, CYAN, msg); | ||||
|     msg = "Type \"/help\" for assistance. Further help may be found via the man page."; | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg); | ||||
|     line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 1, CYAN, msg); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
| } | ||||
|  | ||||
| static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     char myid[TOX_ADDRESS_SIZE]; | ||||
|     tox_self_get_address(m, (uint8_t *) myid); | ||||
|  | ||||
|     if (log_init(ctx->log, self->name, myid, NULL, LOG_TYPE_PROMPT) != 0) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (user_settings->autolog == AUTOLOG_ON) { | ||||
|         if (log_enable(ctx->log) == -1) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Failed to enable log."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void prompt_onInit(ToxWindow *self, Tox *m) | ||||
| { | ||||
|     curs_set(1); | ||||
|     int y2, x2; | ||||
|  | ||||
|     int y2; | ||||
|     int x2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) | ||||
|     if (y2 <= 0 || x2 <= 0) { | ||||
|         exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES); | ||||
|     } | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); | ||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); | ||||
|  | ||||
|     ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); | ||||
|     self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); | ||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0); | ||||
|  | ||||
|     ctx->log = calloc(1, sizeof(struct chatlog)); | ||||
|     ctx->hst = calloc(1, sizeof(struct history)); | ||||
|  | ||||
|     if (ctx->log == NULL || ctx->hst == NULL) | ||||
|     if (ctx->log == NULL || ctx->hst == NULL) { | ||||
|         exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     line_info_init(ctx->hst); | ||||
|  | ||||
|     if (user_settings->autolog == AUTOLOG_ON) { | ||||
|         char myid[TOX_ADDRESS_SIZE]; | ||||
|         tox_self_get_address(m, (uint8_t *) myid); | ||||
|  | ||||
|         if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) | ||||
|             line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); | ||||
|     } | ||||
|     prompt_init_log(self, m, self->name); | ||||
|  | ||||
|     scrollok(ctx->history, 0); | ||||
|     wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||
|  | ||||
|     if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON) | ||||
|     if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON) { | ||||
|         print_welcome_msg(self); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ToxWindow new_prompt(void) | ||||
| ToxWindow *new_prompt(void) | ||||
| { | ||||
|     ToxWindow ret; | ||||
|     memset(&ret, 0, sizeof(ret)); | ||||
|     ToxWindow *ret = calloc(1, sizeof(ToxWindow)); | ||||
|  | ||||
|     ret.num = -1; | ||||
|     ret.active = true; | ||||
|     ret.is_prompt = true; | ||||
|     if (ret == NULL) { | ||||
|         exit_toxic_err("failed in new_prompt", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     ret.onKey = &prompt_onKey; | ||||
|     ret.onDraw = &prompt_onDraw; | ||||
|     ret.onInit = &prompt_onInit; | ||||
|     ret.onConnectionChange = &prompt_onConnectionChange; | ||||
|     ret.onFriendRequest = &prompt_onFriendRequest; | ||||
|     ret->num = -1; | ||||
|     ret->type = WINDOW_TYPE_PROMPT; | ||||
|  | ||||
|     strcpy(ret.name, "home"); | ||||
|     ret->onKey = &prompt_onKey; | ||||
|     ret->onDraw = &prompt_onDraw; | ||||
|     ret->onInit = &prompt_onInit; | ||||
|     ret->onConnectionChange = &prompt_onConnectionChange; | ||||
|     ret->onFriendRequest = &prompt_onFriendRequest; | ||||
|  | ||||
|     strcpy(ret->name, "Home"); | ||||
|  | ||||
|     ChatContext *chatwin = calloc(1, sizeof(ChatContext)); | ||||
|     StatusBar *stb = calloc(1, sizeof(StatusBar)); | ||||
|     Help *help = calloc(1, sizeof(Help)); | ||||
|  | ||||
|     if (stb == NULL || chatwin == NULL || help == NULL) | ||||
|     if (stb == NULL || chatwin == NULL || help == NULL) { | ||||
|         exit_toxic_err("failed in new_prompt", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     ret.chatwin = chatwin; | ||||
|     ret.stb = stb; | ||||
|     ret.help = help; | ||||
|     ret->chatwin = chatwin; | ||||
|     ret->stb = stb; | ||||
|     ret->help = help; | ||||
|  | ||||
|     ret.active_box = -1; | ||||
|     ret->active_box = -1; | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|   | ||||
							
								
								
									
										17
									
								
								src/prompt.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/prompt.h
									
									
									
									
									
								
							| @@ -26,31 +26,34 @@ | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define MAX_FRIEND_REQUESTS 32 | ||||
| #define MAX_FRIEND_REQUESTS 20 | ||||
|  | ||||
| struct friend_request { | ||||
|     bool active; | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|     char msg[TOX_MAX_FRIEND_REQUEST_LENGTH + 1]; | ||||
|     uint8_t key[TOX_PUBLIC_KEY_SIZE]; | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
| typedef struct FriendRequests { | ||||
|     int max_idx; | ||||
|     int num_requests; | ||||
|     struct friend_request request[MAX_FRIEND_REQUESTS]; | ||||
| } FriendRequests; | ||||
|  | ||||
| ToxWindow new_prompt(void); | ||||
| ToxWindow *new_prompt(void); | ||||
|  | ||||
| void prep_prompt_win(void); | ||||
| void prompt_init_statusbar(ToxWindow *self, Tox *m); | ||||
| void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run); | ||||
| void prompt_update_nick(ToxWindow *prompt, const char *nick); | ||||
| void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg); | ||||
| void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status); | ||||
| void prompt_update_status(ToxWindow *prompt, Tox_User_Status status); | ||||
| void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected); | ||||
| void kill_prompt_window(ToxWindow *self); | ||||
|  | ||||
| /* callback: Updates own connection status in prompt statusbar */ | ||||
| void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata); | ||||
| void on_self_connection_status(Tox *m, Tox_Connection connection_status, void *userdata); | ||||
|  | ||||
| /* Returns our own connection status */ | ||||
| Tox_Connection prompt_selfConnectionStatus(void); | ||||
|  | ||||
| #endif /* end of include guard: PROMPT_H */ | ||||
|   | ||||
							
								
								
									
										365
									
								
								src/python_api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								src/python_api.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,365 @@ | ||||
| /*  python_api.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net> | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #include <Python.h> | ||||
| #include "api.h" | ||||
|  | ||||
| #include "execute.h" | ||||
|  | ||||
| extern Tox       *user_tox; | ||||
|  | ||||
| static struct python_registered_func { | ||||
|     char     *name; | ||||
|     char     *help; | ||||
|     PyObject *callback; | ||||
|     struct python_registered_func *next; | ||||
| } python_commands = {0}; | ||||
|  | ||||
| static PyObject *python_api_display(PyObject *self, PyObject *args) | ||||
| { | ||||
|     const char *msg; | ||||
|  | ||||
|     if (!PyArg_ParseTuple(args, "s", &msg)) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     api_display(msg); | ||||
|     return Py_None; | ||||
| } | ||||
|  | ||||
| static PyObject *python_api_get_nick(PyObject *self, PyObject *args) | ||||
| { | ||||
|     char     *name; | ||||
|     PyObject *ret; | ||||
|  | ||||
|     if (!PyArg_ParseTuple(args, "")) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     name = api_get_nick(); | ||||
|  | ||||
|     if (name == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     ret  = Py_BuildValue("s", name); | ||||
|     free(name); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static PyObject *python_api_get_status(PyObject *self, PyObject *args) | ||||
| { | ||||
|     PyObject        *ret = NULL; | ||||
|  | ||||
|     if (!PyArg_ParseTuple(args, "")) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     switch (api_get_status()) { | ||||
|         case TOX_USER_STATUS_NONE: | ||||
|             ret = Py_BuildValue("s", "online"); | ||||
|             break; | ||||
|  | ||||
|         case TOX_USER_STATUS_AWAY: | ||||
|             ret = Py_BuildValue("s", "away"); | ||||
|             break; | ||||
|  | ||||
|         case TOX_USER_STATUS_BUSY: | ||||
|             ret = Py_BuildValue("s", "busy"); | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static PyObject *python_api_get_status_message(PyObject *self, PyObject *args) | ||||
| { | ||||
|     char     *status; | ||||
|     PyObject *ret; | ||||
|  | ||||
|     if (!PyArg_ParseTuple(args, "")) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     status = api_get_status_message(); | ||||
|  | ||||
|     if (status == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     ret    = Py_BuildValue("s", status); | ||||
|     free(status); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args) | ||||
| { | ||||
|     FriendsList  friends; | ||||
|     char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1]; | ||||
|  | ||||
|     if (!PyArg_ParseTuple(args, "")) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     friends = api_get_friendslist(); | ||||
|     PyObject *ret = PyList_New(0); | ||||
|  | ||||
|     for (size_t i = 0; i < friends.num_friends; i++) { | ||||
|         for (size_t ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) { | ||||
|             snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff); | ||||
|         } | ||||
|  | ||||
|         pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0'; | ||||
|         PyObject *cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf); | ||||
|         PyList_Append(ret, cur); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static PyObject *python_api_send(PyObject *self, PyObject *args) | ||||
| { | ||||
|     const char *msg; | ||||
|  | ||||
|     if (!PyArg_ParseTuple(args, "s", &msg)) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     api_send(msg); | ||||
|     return Py_None; | ||||
| } | ||||
|  | ||||
| static PyObject *python_api_execute(PyObject *self, PyObject *args) | ||||
| { | ||||
|     int         mode; | ||||
|     const char *command; | ||||
|  | ||||
|     if (!PyArg_ParseTuple(args, "si", &command, &mode)) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     api_execute(command, mode); | ||||
|     return Py_None; | ||||
| } | ||||
|  | ||||
| static PyObject *python_api_register(PyObject *self, PyObject *args) | ||||
| { | ||||
|     struct python_registered_func *cur; | ||||
|     size_t      command_len, help_len; | ||||
|     const char *command, *help; | ||||
|     PyObject   *callback; | ||||
|  | ||||
|     if (!PyArg_ParseTuple(args, "ssO:register_command", &command, &help, &callback)) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     if (!PyCallable_Check(callback)) { | ||||
|         PyErr_SetString(PyExc_TypeError, "Calback parameter must be callable"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     if (command[0] != '/') { | ||||
|         PyErr_SetString(PyExc_TypeError, "Command must be prefixed with a '/'"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     for (cur = &python_commands; ; cur = cur->next) { | ||||
|         if (cur->name != NULL && !strcmp(command, cur->name)) { | ||||
|             Py_XDECREF(cur->callback); | ||||
|             Py_XINCREF(callback); | ||||
|             cur->callback = callback; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if (cur->next == NULL) { | ||||
|             Py_XINCREF(callback); | ||||
|             cur->next = malloc(sizeof(struct python_registered_func)); | ||||
|  | ||||
|             if (cur->next == NULL) { | ||||
|                 return PyErr_NoMemory(); | ||||
|             } | ||||
|  | ||||
|             command_len     = strlen(command); | ||||
|             cur->next->name = malloc(command_len + 1); | ||||
|  | ||||
|             if (cur->next->name == NULL) { | ||||
|                 return PyErr_NoMemory(); | ||||
|             } | ||||
|  | ||||
|             strncpy(cur->next->name, command, command_len + 1); | ||||
|             help_len        = strlen(help); | ||||
|             cur->next->help = malloc(help_len + 1); | ||||
|  | ||||
|             if (cur->next->help == NULL) { | ||||
|                 return PyErr_NoMemory(); | ||||
|             } | ||||
|  | ||||
|             strncpy(cur->next->help, help, help_len + 1); | ||||
|             cur->next->callback = callback; | ||||
|             cur->next->next     = NULL; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Py_INCREF(Py_None); | ||||
|     return Py_None; | ||||
| } | ||||
|  | ||||
| static PyMethodDef ToxicApiMethods[] = { | ||||
|     {"display",            python_api_display,            METH_VARARGS, "Display a message to the current prompt"}, | ||||
|     {"get_nick",           python_api_get_nick,           METH_VARARGS, "Return the user's current nickname"}, | ||||
|     {"get_status",         python_api_get_status,         METH_VARARGS, "Returns the user's current status"}, | ||||
|     {"get_status_message", python_api_get_status_message, METH_VARARGS, "Return the user's current status message"}, | ||||
|     {"get_all_friends",    python_api_get_all_friends,    METH_VARARGS, "Return all of the user's friends"}, | ||||
|     {"send",               python_api_send,               METH_VARARGS, "Send the message to the current user"}, | ||||
|     {"execute",            python_api_execute,            METH_VARARGS, "Execute a command like `/nick`"}, | ||||
|     {"register",           python_api_register,           METH_VARARGS, "Register a command like `/nick` to a Python function"}, | ||||
|     {NULL,                 NULL,                          0,            NULL}, | ||||
| }; | ||||
|  | ||||
| static struct PyModuleDef toxic_api_module = { | ||||
|     PyModuleDef_HEAD_INIT, | ||||
|     "toxic_api", | ||||
|     NULL, | ||||
|     -1, | ||||
|     ToxicApiMethods | ||||
| }; | ||||
|  | ||||
| PyMODINIT_FUNC PyInit_toxic_api(void) | ||||
| { | ||||
|     PyObject *m = PyModule_Create(&toxic_api_module); | ||||
|     PyObject *global_command_const    = Py_BuildValue("i", GLOBAL_COMMAND_MODE); | ||||
|     PyObject *chat_command_const      = Py_BuildValue("i", CHAT_COMMAND_MODE); | ||||
|     PyObject *conference_command_const = Py_BuildValue("i", CONFERENCE_COMMAND_MODE); | ||||
|     PyObject_SetAttrString(m, "GLOBAL_COMMAND",    global_command_const); | ||||
|     PyObject_SetAttrString(m, "CHAT_COMMAND",      chat_command_const); | ||||
|     PyObject_SetAttrString(m, "CONFERENCE_COMMAND", conference_command_const); | ||||
|     Py_DECREF(global_command_const); | ||||
|     Py_DECREF(chat_command_const); | ||||
|     Py_DECREF(conference_command_const); | ||||
|     return m; | ||||
| } | ||||
|  | ||||
| void terminate_python(void) | ||||
| { | ||||
|     if (python_commands.name != NULL) { | ||||
|         free(python_commands.name); | ||||
|     } | ||||
|  | ||||
|     struct python_registered_func *cur = NULL; | ||||
|  | ||||
|     for (cur = python_commands.next; cur != NULL;) { | ||||
|         struct python_registered_func *old = cur; | ||||
|         cur = cur->next; | ||||
|         free(old->name); | ||||
|         free(old); | ||||
|     } | ||||
|  | ||||
|     Py_Finalize(); | ||||
| } | ||||
|  | ||||
| void init_python(Tox *m) | ||||
| { | ||||
|     user_tox = m; | ||||
|     PyImport_AppendInittab("toxic_api", PyInit_toxic_api); | ||||
|     Py_Initialize(); | ||||
| } | ||||
|  | ||||
| void run_python(FILE *fp, char *path) | ||||
| { | ||||
|     PyRun_SimpleFile(fp, path); | ||||
| } | ||||
|  | ||||
| int do_python_command(int num_args, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     int i; | ||||
|     PyObject *callback_args, *args_strings; | ||||
|     struct python_registered_func *cur; | ||||
|  | ||||
|     for (cur = &python_commands; cur != NULL; cur = cur->next) { | ||||
|         if (cur->name == NULL) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (!strcmp(args[0], cur->name)) { | ||||
|             args_strings = PyList_New(0); | ||||
|  | ||||
|             for (i = 1; i < num_args; i++) { | ||||
|                 PyList_Append(args_strings, Py_BuildValue("s", args[i])); | ||||
|             } | ||||
|  | ||||
|             callback_args = PyTuple_Pack(1, args_strings); | ||||
|  | ||||
|             if (PyObject_CallObject(cur->callback, callback_args) == NULL) { | ||||
|                 api_display("Exception raised in callback function"); | ||||
|             } | ||||
|  | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| int python_num_registered_handlers(void) | ||||
| { | ||||
|     int n = 0; | ||||
|     struct python_registered_func *cur; | ||||
|  | ||||
|     for (cur = &python_commands; cur != NULL; cur = cur->next) { | ||||
|         if (cur->name != NULL) { | ||||
|             n++; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return n; | ||||
| } | ||||
|  | ||||
| int python_help_max_width(void) | ||||
| { | ||||
|     size_t tmp; | ||||
|     int    max = 0; | ||||
|     struct python_registered_func *cur; | ||||
|  | ||||
|     for (cur = &python_commands; cur != NULL; cur = cur->next) { | ||||
|         if (cur->name != NULL) { | ||||
|             tmp = strlen(cur->help); | ||||
|             max = tmp > max ? tmp : max; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     max = max > 50 ? 50 : max; | ||||
|     return 37 + max; | ||||
| } | ||||
|  | ||||
| void python_draw_handler_help(WINDOW *win) | ||||
| { | ||||
|     struct python_registered_func *cur; | ||||
|  | ||||
|     for (cur = &python_commands; cur != NULL; cur = cur->next) { | ||||
|         if (cur->name != NULL) { | ||||
|             wprintw(win, "  %-29s: %.50s\n", cur->name, cur->help); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif /* PYTHON */ | ||||
| @@ -1,7 +1,7 @@ | ||||
| /*  xtra.h
 | ||||
| /*  python_api.h
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net> | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
| @@ -20,22 +20,20 @@ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef XTRA_H | ||||
| #define XTRA_H | ||||
| #ifndef PYTHON_API_H | ||||
| #define PYTHON_API_H | ||||
| 
 | ||||
| /* NOTE: If no xlib present don't compile */ | ||||
| #ifdef PYTHON | ||||
| #include <Python.h> | ||||
| #endif /* PYTHON */ | ||||
| 
 | ||||
| typedef enum { | ||||
|     DT_plain, | ||||
|     DT_file_list | ||||
| } | ||||
| DropType; | ||||
| PyMODINIT_FUNC PyInit_toxic_api(void); | ||||
| void terminate_python(void); | ||||
| void init_python(Tox *m); | ||||
| void run_python(FILE *fp, char *path); | ||||
| int do_python_command(int num_args, char (*args)[MAX_STR_SIZE]); | ||||
| int python_num_registered_handlers(void); | ||||
| int python_help_max_width(void); | ||||
| void python_draw_handler_help(WINDOW *win); | ||||
| 
 | ||||
| typedef void (*drop_callback) (const char *, DropType); | ||||
| 
 | ||||
| int               init_xtra(drop_callback d); | ||||
| void              terminate_xtra(); | ||||
| long unsigned int focused_window_id(); | ||||
| int               is_focused(); /* returns bool */ | ||||
| 
 | ||||
| #endif /* XTRA_H */ | ||||
| #endif /* PYTHON_API_H */ | ||||
| @@ -20,13 +20,16 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #ifdef QRCODE | ||||
|  | ||||
| #include <qrencode.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "qr_code.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "qr_code.h" | ||||
|  | ||||
| #ifdef QRPNG | ||||
| #include <png.h> | ||||
| @@ -49,8 +52,9 @@ int ID_to_QRcode_txt(const char *tox_id, const char *outfile) | ||||
| { | ||||
|     FILE *fp = fopen(outfile, "wb"); | ||||
|  | ||||
|     if (fp == NULL) | ||||
|     if (fp == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0); | ||||
|  | ||||
| @@ -62,14 +66,16 @@ int ID_to_QRcode_txt(const char *tox_id, const char *outfile) | ||||
|     size_t width = qr_obj->width; | ||||
|     size_t i, j; | ||||
|  | ||||
|     for (i = 0; i < width + BORDER_LEN * 2; ++i) | ||||
|     for (i = 0; i < width + BORDER_LEN * 2; ++i) { | ||||
|         fprintf(fp, "%s", CHAR_1); | ||||
|     } | ||||
|  | ||||
|     fprintf(fp, "\n"); | ||||
|  | ||||
|     for (i = 0; i < width; i += 2) { | ||||
|         for (j = 0; j < BORDER_LEN; ++j) | ||||
|         for (j = 0; j < BORDER_LEN; ++j) { | ||||
|             fprintf(fp, "%s", CHAR_1); | ||||
|         } | ||||
|  | ||||
|         const unsigned char *row_1 = qr_obj->data + width * i; | ||||
|         const unsigned char *row_2 = row_1 + width; | ||||
| @@ -78,18 +84,20 @@ int ID_to_QRcode_txt(const char *tox_id, const char *outfile) | ||||
|             bool x = row_1[j] & 1; | ||||
|             bool y = (i + 1) < width ? (row_2[j] & 1) : false; | ||||
|  | ||||
|             if (x && y) | ||||
|             if (x && y) { | ||||
|                 fprintf(fp, " "); | ||||
|             else if (x) | ||||
|             } else if (x) { | ||||
|                 fprintf(fp, "%s", CHAR_2); | ||||
|             else if (y) | ||||
|             } else if (y) { | ||||
|                 fprintf(fp, "%s", CHAR_3); | ||||
|             else | ||||
|             } else { | ||||
|                 fprintf(fp, "%s", CHAR_1); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (j = 0; j < BORDER_LEN; ++j) | ||||
|         for (j = 0; j < BORDER_LEN; ++j) { | ||||
|             fprintf(fp, "%s", CHAR_1); | ||||
|         } | ||||
|  | ||||
|         fprintf(fp, "\n"); | ||||
|     } | ||||
| @@ -130,12 +138,19 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile) | ||||
|  | ||||
|     real_width = (qr_obj->width + BORDER_LEN * 2) * SQUARE_SIZE; | ||||
|     size_t row_size = real_width * 4; | ||||
|     unsigned char row[row_size]; | ||||
|     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; | ||||
|     } | ||||
| @@ -144,12 +159,14 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile) | ||||
|  | ||||
|     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; | ||||
| @@ -198,12 +215,16 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile) | ||||
|         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); | ||||
|  | ||||
|     fclose(fp); | ||||
|     QRcode_free(qr_obj); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| #endif /* QRPNG */ | ||||
|  | ||||
| #endif /* QRCODE */ | ||||
|   | ||||
| @@ -23,6 +23,8 @@ | ||||
| #ifndef QR_CODE | ||||
| #define QR_CODE | ||||
|  | ||||
| #ifdef QRCODE | ||||
|  | ||||
| #define QRCODE_FILENAME_EXT ".QRcode" | ||||
|  | ||||
| /* Converts a tox ID string into a QRcode and prints it into the given filename. | ||||
| @@ -42,4 +44,6 @@ int ID_to_QRcode_txt(const char *tox_id, const char *outfile); | ||||
| int ID_to_QRcode_png(const char *tox_id, const char *outfile); | ||||
| #endif /* QRPNG */ | ||||
|  | ||||
| #endif /* QRCODE */ | ||||
|  | ||||
| #endif /* QR_CODE */ | ||||
|   | ||||
							
								
								
									
										247
									
								
								src/settings.c
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								src/settings.c
									
									
									
									
									
								
							| @@ -20,23 +20,23 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <ctype.h> | ||||
| #include <libconfig.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <libconfig.h> | ||||
| #include <ctype.h> | ||||
|  | ||||
| #include "configdir.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
| #include "configdir.h" | ||||
| #include "notify.h" | ||||
| #include "misc_tools.h" | ||||
|  | ||||
| #ifdef AUDIO | ||||
| #include "audio_device.h" | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #include "settings.h" | ||||
| #include "line_info.h" | ||||
| #include "settings.h" | ||||
|  | ||||
| #ifndef PACKAGE_DATADIR | ||||
| #define PACKAGE_DATADIR "." | ||||
| @@ -58,11 +58,13 @@ static struct ui_strings { | ||||
|     const char *native_colors; | ||||
|     const char *autolog; | ||||
|     const char *history_size; | ||||
|     const char *notification_timeout; | ||||
|     const char *show_typing_self; | ||||
|     const char *show_typing_other; | ||||
|     const char *show_welcome_msg; | ||||
|     const char *show_connection_msg; | ||||
|     const char *nodeslist_update_freq; | ||||
|     const char *autosave_freq; | ||||
|  | ||||
|     const char *line_join; | ||||
|     const char *line_quit; | ||||
| @@ -71,6 +73,11 @@ static struct ui_strings { | ||||
|  | ||||
|     const char *mplex_away; | ||||
|     const char *mplex_away_note; | ||||
|  | ||||
|     const char *color_bar_bg; | ||||
|     const char *color_bar_fg; | ||||
|     const char *color_bar_accent; | ||||
|     const char *color_bar_notify; | ||||
| } ui_strings = { | ||||
|     "ui", | ||||
|     "timestamps", | ||||
| @@ -85,17 +92,23 @@ static struct ui_strings { | ||||
|     "native_colors", | ||||
|     "autolog", | ||||
|     "history_size", | ||||
|     "notification_timeout", | ||||
|     "show_typing_self", | ||||
|     "show_typing_other", | ||||
|     "show_welcome_msg", | ||||
|     "show_connection_msg", | ||||
|     "nodeslist_update_freq", | ||||
|     "autosave_freq", | ||||
|     "line_join", | ||||
|     "line_quit", | ||||
|     "line_alert", | ||||
|     "line_normal", | ||||
|     "mplex_away", | ||||
|     "mplex_away_note", | ||||
|     "color_bar_bg", | ||||
|     "color_bar_fg", | ||||
|     "color_bar_accent", | ||||
|     "color_bar_notify", | ||||
| }; | ||||
|  | ||||
| static void ui_defaults(struct user_settings *settings) | ||||
| @@ -112,11 +125,13 @@ static void ui_defaults(struct user_settings *settings) | ||||
|     settings->bell_on_invite = 0; | ||||
|     settings->colour_theme = DFLT_COLS; | ||||
|     settings->history_size = 700; | ||||
|     settings->notification_timeout = 6000; | ||||
|     settings->show_typing_self = SHOW_TYPING_ON; | ||||
|     settings->show_typing_other = SHOW_TYPING_ON; | ||||
|     settings->show_welcome_msg = SHOW_WELCOME_MSG_ON; | ||||
|     settings->show_connection_msg = SHOW_CONNECTION_MSG_ON; | ||||
|     settings->nodeslist_update_freq = 7; | ||||
|     settings->autosave_freq = 600; | ||||
|  | ||||
|     snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN); | ||||
|     snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); | ||||
| @@ -124,10 +139,7 @@ static void ui_defaults(struct user_settings *settings) | ||||
|     snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL); | ||||
|  | ||||
|     settings->mplex_away = MPLEX_ON; | ||||
|     snprintf (settings->mplex_away_note, | ||||
|               sizeof (settings->mplex_away_note), | ||||
|               "%s", | ||||
|               MPLEX_AWAY_NOTE); | ||||
|     snprintf(settings->mplex_away_note, sizeof(settings->mplex_away_note), "%s", MPLEX_AWAY_NOTE); | ||||
| } | ||||
|  | ||||
| static const struct keys_strings { | ||||
| @@ -139,8 +151,6 @@ static const struct keys_strings { | ||||
|     const char *half_page_up; | ||||
|     const char *half_page_down; | ||||
|     const char *page_bottom; | ||||
|     const char *peer_list_up; | ||||
|     const char *peer_list_down; | ||||
|     const char *toggle_peerlist; | ||||
|     const char *toggle_pastemode; | ||||
| } key_strings = { | ||||
| @@ -152,8 +162,6 @@ static const struct keys_strings { | ||||
|     "half_page_up", | ||||
|     "half_page_down", | ||||
|     "page_bottom", | ||||
|     "peer_list_up", | ||||
|     "peer_list_down", | ||||
|     "toggle_peerlist", | ||||
|     "toggle_paste_mode", | ||||
| }; | ||||
| @@ -168,8 +176,6 @@ static void key_defaults(struct user_settings *settings) | ||||
|     settings->key_half_page_up = T_KEY_C_F; | ||||
|     settings->key_half_page_down = T_KEY_C_V; | ||||
|     settings->key_page_bottom = T_KEY_C_H; | ||||
|     settings->key_peer_list_up = T_KEY_C_LB; | ||||
|     settings->key_peer_list_down = T_KEY_C_RB; | ||||
|     settings->key_toggle_peerlist = T_KEY_C_B; | ||||
|     settings->key_toggle_pastemode = T_KEY_C_T; | ||||
| } | ||||
| @@ -179,12 +185,14 @@ static const struct tox_strings { | ||||
|     const char *download_path; | ||||
|     const char *chatlogs_path; | ||||
|     const char *avatar_path; | ||||
|     const char *autorun_path; | ||||
|     const char *password_eval; | ||||
| } tox_strings = { | ||||
|     "tox", | ||||
|     "download_path", | ||||
|     "chatlogs_path", | ||||
|     "avatar_path", | ||||
|     "autorun_path", | ||||
|     "password_eval", | ||||
| }; | ||||
|  | ||||
| @@ -193,6 +201,7 @@ static void tox_defaults(struct user_settings *settings) | ||||
|     strcpy(settings->download_path, ""); | ||||
|     strcpy(settings->chatlogs_path, ""); | ||||
|     strcpy(settings->avatar_path, ""); | ||||
|     strcpy(settings->autorun_path, ""); | ||||
|     strcpy(settings->password_eval, ""); | ||||
| } | ||||
|  | ||||
| @@ -201,19 +210,28 @@ static const struct audio_strings { | ||||
|     const char *self; | ||||
|     const char *input_device; | ||||
|     const char *output_device; | ||||
|     const char *VAD_treshold; | ||||
|     const char *VAD_threshold; | ||||
|     const char *conference_audio_channels; | ||||
|     const char *chat_audio_channels; | ||||
|     const char *push_to_talk; | ||||
| } audio_strings = { | ||||
|     "audio", | ||||
|     "input_device", | ||||
|     "output_device", | ||||
|     "VAD_treshold", | ||||
|     "VAD_threshold", | ||||
|     "conference_audio_channels", | ||||
|     "chat_audio_channels", | ||||
|     "push_to_talk", | ||||
| }; | ||||
|  | ||||
| static void audio_defaults(struct user_settings *settings) | ||||
| { | ||||
|     settings->audio_in_dev = 0; | ||||
|     settings->audio_out_dev = 0; | ||||
|     settings->VAD_treshold = 40.0; | ||||
|     settings->VAD_threshold = 5.0; | ||||
|     settings->conference_audio_channels = 1; | ||||
|     settings->chat_audio_channels = 2; | ||||
|     settings->push_to_talk = 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -250,15 +268,18 @@ static int key_parse(const char **bind) | ||||
|     int len = strlen(*bind); | ||||
|  | ||||
|     if (len > 5) { | ||||
|         if (strncasecmp(*bind, "ctrl+", 5) == 0 && toupper(bind[0][5]) != 'M')  /* ctrl+m cannot be used */ | ||||
|         if (strncasecmp(*bind, "ctrl+", 5) == 0 && toupper(bind[0][5]) != 'M') { /* ctrl+m cannot be used */ | ||||
|             return toupper(bind[0][5]) - 'A' + 1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (strncasecmp(*bind, "tab", 3) == 0) | ||||
|     if (strncasecmp(*bind, "tab", 3) == 0) { | ||||
|         return T_KEY_TAB; | ||||
|     } | ||||
|  | ||||
|     if (strncasecmp(*bind, "page", 4) == 0) | ||||
|     if (strncasecmp(*bind, "page", 4) == 0) { | ||||
|         return len == 6 ? KEY_PPAGE : KEY_NPAGE; | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
| @@ -301,8 +322,9 @@ int settings_load(struct user_settings *s, const char *patharg) | ||||
|         if (!file_exists(path)) { | ||||
|             FILE *fp = fopen(path, "w"); | ||||
|  | ||||
|             if (fp == NULL) | ||||
|             if (fp == NULL) { | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             fclose(fp); | ||||
|         } | ||||
| @@ -321,18 +343,34 @@ int settings_load(struct user_settings *s, const char *patharg) | ||||
|  | ||||
|         int time = 24; | ||||
|  | ||||
|         if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) { | ||||
|         if (config_setting_lookup_int(setting, ui_strings.time_format, &time)) { | ||||
|             if (time == 12) { | ||||
|                 snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p"); | ||||
|                 snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M %p"); | ||||
|                 snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, ui_strings.timestamp_format, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, ui_strings.timestamp_format, &str)) { | ||||
|             snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str); | ||||
|         } | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, ui_strings.color_bar_bg, &str)) { | ||||
|             snprintf(s->color_bar_bg, sizeof(s->color_bar_bg), "%s", str); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, ui_strings.color_bar_fg, &str)) { | ||||
|             snprintf(s->color_bar_fg, sizeof(s->color_bar_fg), "%s", str); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, ui_strings.color_bar_accent, &str)) { | ||||
|             snprintf(s->color_bar_accent, sizeof(s->color_bar_accent), "%s", str); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, ui_strings.color_bar_notify, &str)) { | ||||
|             snprintf(s->color_bar_notify, sizeof(s->color_bar_notify), "%s", str); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str)) { | ||||
|             snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str); | ||||
|         } | ||||
|  | ||||
| @@ -362,68 +400,89 @@ int settings_load(struct user_settings *s, const char *patharg) | ||||
|         config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg); | ||||
|  | ||||
|         config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size); | ||||
|         config_setting_lookup_int(setting, ui_strings.notification_timeout, &s->notification_timeout); | ||||
|         config_setting_lookup_int(setting, ui_strings.nodeslist_update_freq, &s->nodeslist_update_freq); | ||||
|         config_setting_lookup_int(setting, ui_strings.autosave_freq, &s->autosave_freq); | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, ui_strings.line_join, &str)) { | ||||
|             snprintf(s->line_join, sizeof(s->line_join), "%s", str); | ||||
|         } | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, ui_strings.line_quit, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, ui_strings.line_quit, &str)) { | ||||
|             snprintf(s->line_quit, sizeof(s->line_quit), "%s", str); | ||||
|         } | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, ui_strings.line_alert, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, ui_strings.line_alert, &str)) { | ||||
|             snprintf(s->line_alert, sizeof(s->line_alert), "%s", str); | ||||
|         } | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, ui_strings.line_normal, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, ui_strings.line_normal, &str)) { | ||||
|             snprintf(s->line_normal, sizeof(s->line_normal), "%s", str); | ||||
|         } | ||||
|  | ||||
|         config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away); | ||||
|         config_setting_lookup_bool(setting, ui_strings.mplex_away, &s->mplex_away); | ||||
|  | ||||
|         if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) { | ||||
|             snprintf (s->mplex_away_note, sizeof (s->mplex_away_note), "%s", str); | ||||
|         if (config_setting_lookup_string(setting, ui_strings.mplex_away_note, &str)) { | ||||
|             snprintf(s->mplex_away_note, sizeof(s->mplex_away_note), "%s", str); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* paths */ | ||||
|     if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) { | ||||
|         if ( config_setting_lookup_string(setting, tox_strings.download_path, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, tox_strings.download_path, &str)) { | ||||
|             snprintf(s->download_path, sizeof(s->download_path), "%s", str); | ||||
|             int len = strlen(s->download_path); | ||||
|  | ||||
|             /* make sure path ends with a '/' */ | ||||
|             if (len >= sizeof(s->download_path) - 2) | ||||
|             if (len >= sizeof(s->download_path) - 2) { | ||||
|                 s->download_path[0] = '\0'; | ||||
|             else if (s->download_path[len - 1] != '/') | ||||
|             } else if (s->download_path[len - 1] != '/') { | ||||
|                 strcat(&s->download_path[len - 1], "/"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str)) { | ||||
|             snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str); | ||||
|             int len = strlen(s->chatlogs_path); | ||||
|  | ||||
|             if (len >= sizeof(s->chatlogs_path) - 2) | ||||
|             if (len >= sizeof(s->chatlogs_path) - 2) { | ||||
|                 s->chatlogs_path[0] = '\0'; | ||||
|             else if (s->chatlogs_path[len - 1] != '/') | ||||
|             } else if (s->chatlogs_path[len - 1] != '/') { | ||||
|                 strcat(&s->chatlogs_path[len - 1], "/"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, tox_strings.avatar_path, &str) ) { | ||||
|         if (config_setting_lookup_string(setting, tox_strings.avatar_path, &str)) { | ||||
|             snprintf(s->avatar_path, sizeof(s->avatar_path), "%s", str); | ||||
|             int len = strlen(str); | ||||
|  | ||||
|             if (len >= sizeof(s->avatar_path)) | ||||
|             if (len >= sizeof(s->avatar_path)) { | ||||
|                 s->avatar_path[0] = '\0'; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( config_setting_lookup_string(setting, tox_strings.password_eval, &str) ) { | ||||
| #ifdef PYTHON | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, tox_strings.autorun_path, &str)) { | ||||
|             snprintf(s->autorun_path, sizeof(s->autorun_path), "%s", str); | ||||
|             int len = strlen(str); | ||||
|  | ||||
|             if (len >= sizeof(s->autorun_path) - 2) { | ||||
|                 s->autorun_path[0] = '\0'; | ||||
|             } else if (s->autorun_path[len - 1] != '/') { | ||||
|                 strcat(&s->autorun_path[len - 1], "/"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, tox_strings.password_eval, &str)) { | ||||
|             snprintf(s->password_eval, sizeof(s->password_eval), "%s", str); | ||||
|             int len = strlen(str); | ||||
|  | ||||
|             if (len >= sizeof(s->password_eval)) | ||||
|             if (len >= sizeof(s->password_eval)) { | ||||
|                 s->password_eval[0] = '\0'; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -431,38 +490,41 @@ int settings_load(struct user_settings *s, const char *patharg) | ||||
|     if ((setting = config_lookup(cfg, key_strings.self)) != NULL) { | ||||
|         const char *tmp = NULL; | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) { | ||||
|             set_key_binding(&s->key_next_tab, &tmp); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) { | ||||
|             set_key_binding(&s->key_prev_tab, &tmp); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) { | ||||
|             set_key_binding(&s->key_scroll_line_up, &tmp); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) { | ||||
|             set_key_binding(&s->key_scroll_line_down, &tmp); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) { | ||||
|             set_key_binding(&s->key_half_page_up, &tmp); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) { | ||||
|             set_key_binding(&s->key_half_page_down, &tmp); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) { | ||||
|             set_key_binding(&s->key_page_bottom, &tmp); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) | ||||
|             set_key_binding(&s->key_peer_list_up, &tmp); | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) | ||||
|             set_key_binding(&s->key_peer_list_down, &tmp); | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp)) { | ||||
|             set_key_binding(&s->key_toggle_peerlist, &tmp); | ||||
|         } | ||||
|  | ||||
|         if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp)) | ||||
|         if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp)) { | ||||
|             set_key_binding(&s->key_toggle_pastemode, &tmp); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #ifdef AUDIO | ||||
| @@ -474,7 +536,16 @@ int settings_load(struct user_settings *s, const char *patharg) | ||||
|         config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev); | ||||
|         s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev; | ||||
|  | ||||
|         config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold); | ||||
|         config_setting_lookup_float(setting, audio_strings.VAD_threshold, &s->VAD_threshold); | ||||
|  | ||||
|         config_setting_lookup_int(setting, audio_strings.conference_audio_channels, &s->conference_audio_channels); | ||||
|         s->conference_audio_channels = s->conference_audio_channels <= 0 | ||||
|                                        || s->conference_audio_channels > 2 ? 1 : s->conference_audio_channels; | ||||
|  | ||||
|         config_setting_lookup_int(setting, audio_strings.chat_audio_channels, &s->chat_audio_channels); | ||||
|         s->chat_audio_channels = s->chat_audio_channels <= 0 || s->chat_audio_channels > 2 ? 2 : s->chat_audio_channels; | ||||
|  | ||||
|         config_setting_lookup_bool(setting, audio_strings.push_to_talk, &s->push_to_talk); | ||||
|     } | ||||
|  | ||||
| #endif | ||||
| @@ -482,52 +553,60 @@ int settings_load(struct user_settings *s, const char *patharg) | ||||
| #ifdef SOUND_NOTIFY | ||||
|  | ||||
|     if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) { | ||||
|         if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) || | ||||
|                 !set_sound(notif_error, str) ) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||
|         if ((config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) || | ||||
|                 !set_sound(notif_error, str)) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) { | ||||
|                 set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) || | ||||
|                 !set_sound(user_log_in, str) ) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||
|         if (!config_setting_lookup_string(setting, sound_strings.user_log_in, &str) || | ||||
|                 !set_sound(user_log_in, str)) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) { | ||||
|                 set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) || | ||||
|                 !set_sound(user_log_out, str) ) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||
|         if (!config_setting_lookup_string(setting, sound_strings.user_log_out, &str) || | ||||
|                 !set_sound(user_log_out, str)) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) { | ||||
|                 set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( !config_setting_lookup_string(setting, sound_strings.call_incoming, &str) || | ||||
|                 !set_sound(call_incoming, str) ) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||
|         if (!config_setting_lookup_string(setting, sound_strings.call_incoming, &str) || | ||||
|                 !set_sound(call_incoming, str)) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) { | ||||
|                 set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) || | ||||
|                 !set_sound(call_outgoing, str) ) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||
|         if (!config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) || | ||||
|                 !set_sound(call_outgoing, str)) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) { | ||||
|                 set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) || | ||||
|                 !set_sound(generic_message, str) ) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||
|         if (!config_setting_lookup_string(setting, sound_strings.generic_message, &str) || | ||||
|                 !set_sound(generic_message, str)) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) { | ||||
|                 set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) || | ||||
|                 !set_sound(transfer_pending, str) ) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||
|         if (!config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) || | ||||
|                 !set_sound(transfer_pending, str)) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) { | ||||
|                 set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) || | ||||
|                 !set_sound(transfer_completed, str) ) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) | ||||
|         if (!config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) || | ||||
|                 !set_sound(transfer_completed, str)) { | ||||
|             if (str && strcasecmp(str, NO_SOUND) != 0) { | ||||
|                 set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav"); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav"); | ||||
|   | ||||
| @@ -49,11 +49,13 @@ struct user_settings { | ||||
|  | ||||
|     int colour_theme;      /* boolean (0 for default toxic colours) */ | ||||
|     int history_size;      /* int between MIN_HISTORY and MAX_HISTORY */ | ||||
|     int notification_timeout; | ||||
|     int show_typing_self;  /* boolean */ | ||||
|     int show_typing_other; /* boolean */ | ||||
|     int show_welcome_msg;  /* boolean */ | ||||
|     int show_connection_msg;  /* boolean */ | ||||
|     int nodeslist_update_freq;  /* int (<= 0 to disable updates) */ | ||||
|     int autosave_freq; /* int (<= 0 to disable autosave) */ | ||||
|  | ||||
|     char line_join[LINE_HINT_MAX + 1]; | ||||
|     char line_quit[LINE_HINT_MAX + 1]; | ||||
| @@ -63,8 +65,14 @@ struct user_settings { | ||||
|     char download_path[PATH_MAX]; | ||||
|     char chatlogs_path[PATH_MAX]; | ||||
|     char avatar_path[PATH_MAX]; | ||||
|     char autorun_path[PATH_MAX]; | ||||
|     char password_eval[PASSWORD_EVAL_MAX]; | ||||
|  | ||||
|     char color_bar_bg[COLOR_STR_SIZE]; | ||||
|     char color_bar_fg[COLOR_STR_SIZE]; | ||||
|     char color_bar_accent[COLOR_STR_SIZE]; | ||||
|     char color_bar_notify[COLOR_STR_SIZE]; | ||||
|  | ||||
|     int key_next_tab; | ||||
|     int key_prev_tab; | ||||
|     int key_scroll_line_up; | ||||
| @@ -72,8 +80,6 @@ struct user_settings { | ||||
|     int key_half_page_up; | ||||
|     int key_half_page_down; | ||||
|     int key_page_bottom; | ||||
|     int key_peer_list_up; | ||||
|     int key_peer_list_down; | ||||
|     int key_toggle_peerlist; | ||||
|     int key_toggle_pastemode; | ||||
|  | ||||
| @@ -83,11 +89,14 @@ struct user_settings { | ||||
| #ifdef AUDIO | ||||
|     int audio_in_dev; | ||||
|     int audio_out_dev; | ||||
|     double VAD_treshold; | ||||
|     double VAD_threshold; | ||||
|     int conference_audio_channels; | ||||
|     int chat_audio_channels; | ||||
|     int push_to_talk;      /* boolean */ | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| enum { | ||||
| enum settings_values { | ||||
|     AUTOLOG_OFF = 0, | ||||
|     AUTOLOG_ON = 1, | ||||
|  | ||||
| @@ -113,15 +122,16 @@ enum { | ||||
|  | ||||
|     MPLEX_OFF = 0, | ||||
|     MPLEX_ON = 1, | ||||
| } settings_values; | ||||
| }; | ||||
|  | ||||
| #define LINE_JOIN    "-->" | ||||
| #define LINE_QUIT    "<--" | ||||
| #define LINE_ALERT   "-!-" | ||||
| #define LINE_NORMAL  "---" | ||||
| #define TIMESTAMP_DEFAULT      "%H:%M:%S" | ||||
| #define LINE_NORMAL  "-" | ||||
| #define TIMESTAMP_DEFAULT      "%H:%M" | ||||
| #define LOG_TIMESTAMP_DEFAULT  "%Y/%m/%d [%H:%M:%S]" | ||||
| #define MPLEX_AWAY_NOTE "Detached from screen" | ||||
| #define MPLEX_AWAY_NOTE "Away from keyboard, be back soon!" | ||||
|  | ||||
| int settings_load(struct user_settings *s, const char *patharg); | ||||
| #endif /* #define SETTINGS_H */ | ||||
|  | ||||
| #endif /* SETTINGS_H */ | ||||
|   | ||||
							
								
								
									
										300
									
								
								src/term_mplex.c
									
									
									
									
									
								
							
							
						
						
									
										300
									
								
								src/term_mplex.c
									
									
									
									
									
								
							| @@ -26,18 +26,18 @@ | ||||
| #include <string.h> /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp */ | ||||
|  | ||||
| #include <pthread.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include <tox/tox.h> | ||||
|  | ||||
| #include "global_commands.h" | ||||
| #include "windows.h" | ||||
| #include "execute.h" | ||||
| #include "settings.h" | ||||
| #include "term_mplex.h" | ||||
| #include "toxic.h" | ||||
| #include "settings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern struct ToxWindow *prompt; | ||||
| extern struct user_settings *user_settings; | ||||
| @@ -69,7 +69,7 @@ static char buffer [BUFFER_SIZE]; | ||||
| static bool auto_away_active = false; | ||||
|  | ||||
| static mplex_status mplex = MPLEX_NONE; | ||||
| static TOX_USER_STATUS prev_status = TOX_USER_STATUS_NONE; | ||||
| static Tox_User_Status prev_status = TOX_USER_STATUS_NONE; | ||||
| static char prev_note [TOX_MAX_STATUS_MESSAGE_LENGTH] = ""; | ||||
|  | ||||
| /* mutex for access to status data, for sync between: | ||||
| @@ -80,108 +80,133 @@ static char prev_note [TOX_MAX_STATUS_MESSAGE_LENGTH] = ""; | ||||
| static pthread_mutex_t status_lock; | ||||
| static pthread_t mplex_tid; | ||||
|  | ||||
| void lock_status () | ||||
| void lock_status(void) | ||||
| { | ||||
|     pthread_mutex_lock (&status_lock); | ||||
|     pthread_mutex_lock(&status_lock); | ||||
| } | ||||
|  | ||||
| void unlock_status () | ||||
| void unlock_status(void) | ||||
| { | ||||
|     pthread_mutex_unlock (&status_lock); | ||||
|     pthread_mutex_unlock(&status_lock); | ||||
| } | ||||
|  | ||||
| static char *read_into_dyn_buffer (FILE *stream) | ||||
| static char *read_into_dyn_buffer(FILE *stream) | ||||
| { | ||||
|     const char *input_ptr = NULL; | ||||
|     char *dyn_buffer = NULL; | ||||
|     int dyn_buffer_size = 1; /* account for the \0 */ | ||||
|  | ||||
|     while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) { | ||||
|         int length = dyn_buffer_size + strlen (input_ptr); | ||||
|     while ((input_ptr = fgets(buffer, BUFFER_SIZE, stream)) != NULL) { | ||||
|         int length = dyn_buffer_size + strlen(input_ptr); | ||||
|  | ||||
|         if (dyn_buffer) | ||||
|             dyn_buffer = (char *) realloc (dyn_buffer, length); | ||||
|         else | ||||
|             dyn_buffer = (char *) malloc (length); | ||||
|         if (dyn_buffer) { | ||||
|             char *tmp = realloc(dyn_buffer, length); | ||||
|  | ||||
|         strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr); | ||||
|             if (tmp == NULL) { | ||||
|                 return NULL; | ||||
|             } | ||||
|  | ||||
|             dyn_buffer = tmp; | ||||
|         } else { | ||||
|             dyn_buffer = malloc(length); | ||||
|  | ||||
|             if (dyn_buffer == NULL) { | ||||
|                 return NULL; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         strcpy(dyn_buffer + dyn_buffer_size - 1, input_ptr); | ||||
|         dyn_buffer_size = length; | ||||
|     } | ||||
|  | ||||
|     return dyn_buffer; | ||||
| } | ||||
|  | ||||
| static char *extract_socket_path (const char *info) | ||||
| static char *extract_socket_path(const char *info) | ||||
| { | ||||
|     const char *search_str = " Socket"; | ||||
|     const char *pos = strstr (info, search_str); | ||||
|     const char *pos = strstr(info, search_str); | ||||
|     char *end = NULL; | ||||
|     char *path = NULL; | ||||
|  | ||||
|     if (!pos) | ||||
|     if (!pos) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     pos += strlen (search_str); | ||||
|     pos = strchr (pos, PATH_SEP_C); | ||||
|     pos += strlen(search_str); | ||||
|     pos = strchr(pos, PATH_SEP_C); | ||||
|  | ||||
|     if (!pos) | ||||
|     if (!pos) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     end = strchr (pos, '\n'); | ||||
|     end = strchr(pos, '\n'); | ||||
|  | ||||
|     if (!end) | ||||
|     if (!end) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     *end = '\0'; | ||||
|     end = strrchr (pos, '.'); | ||||
|     end = strrchr(pos, '.'); | ||||
|  | ||||
|     if (!end) | ||||
|     if (!end) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     path = malloc(end - pos + 1); | ||||
|  | ||||
|     if (path == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     path = (char *) malloc (end - pos + 1); | ||||
|     *end = '\0'; | ||||
|     return strcpy (path, pos); | ||||
|     return strcpy(path, pos); | ||||
| } | ||||
|  | ||||
| static int detect_gnu_screen () | ||||
| static int detect_gnu_screen(void) | ||||
| { | ||||
|     FILE *session_info_stream = NULL; | ||||
|     char *socket_name = NULL, *socket_path = NULL; | ||||
|     char *dyn_buffer = NULL; | ||||
|  | ||||
|     socket_name = getenv ("STY"); | ||||
|     socket_name = getenv("STY"); | ||||
|  | ||||
|     if (!socket_name) | ||||
|     if (!socket_name) { | ||||
|         goto nomplex; | ||||
|     } | ||||
|  | ||||
|     session_info_stream = popen ("env LC_ALL=C screen -ls", "r"); | ||||
|     session_info_stream = popen("env LC_ALL=C screen -ls", "r"); | ||||
|  | ||||
|     if (!session_info_stream) | ||||
|     if (!session_info_stream) { | ||||
|         goto nomplex; | ||||
|     } | ||||
|  | ||||
|     dyn_buffer = read_into_dyn_buffer (session_info_stream); | ||||
|     dyn_buffer = read_into_dyn_buffer(session_info_stream); | ||||
|  | ||||
|     if (!dyn_buffer) | ||||
|     if (!dyn_buffer) { | ||||
|         goto nomplex; | ||||
|     } | ||||
|  | ||||
|     pclose (session_info_stream); | ||||
|     pclose(session_info_stream); | ||||
|     session_info_stream = NULL; | ||||
|  | ||||
|     socket_path = extract_socket_path (dyn_buffer); | ||||
|     socket_path = extract_socket_path(dyn_buffer); | ||||
|  | ||||
|     if (!socket_path) | ||||
|     if (!socket_path) { | ||||
|         goto nomplex; | ||||
|     } | ||||
|  | ||||
|     free (dyn_buffer); | ||||
|     free(dyn_buffer); | ||||
|     dyn_buffer = NULL; | ||||
|  | ||||
|     if (strlen(socket_path) + strlen(PATH_SEP_S) + strlen(socket_name) >= sizeof(mplex_data)) | ||||
|     if (strlen(socket_path) + strlen(PATH_SEP_S) + strlen(socket_name) >= sizeof(mplex_data)) { | ||||
|         goto nomplex; | ||||
|     } | ||||
|  | ||||
|     strcpy (mplex_data, socket_path); | ||||
|     strcat (mplex_data, PATH_SEP_S); | ||||
|     strcat (mplex_data, socket_name); | ||||
|     free (socket_path); | ||||
|     strcpy(mplex_data, socket_path); | ||||
|     strcat(mplex_data, PATH_SEP_S); | ||||
|     strcat(mplex_data, socket_name); | ||||
|     free(socket_path); | ||||
|     socket_path = NULL; | ||||
|  | ||||
|     mplex = MPLEX_SCREEN; | ||||
| @@ -189,33 +214,38 @@ static int detect_gnu_screen () | ||||
|  | ||||
| nomplex: | ||||
|  | ||||
|     if (session_info_stream) | ||||
|         pclose (session_info_stream); | ||||
|     if (session_info_stream) { | ||||
|         pclose(session_info_stream); | ||||
|     } | ||||
|  | ||||
|     if (dyn_buffer) | ||||
|         free (dyn_buffer); | ||||
|     if (dyn_buffer) { | ||||
|         free(dyn_buffer); | ||||
|     } | ||||
|  | ||||
|     if (socket_path) | ||||
|     if (socket_path) { | ||||
|         free(socket_path); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int detect_tmux () | ||||
| static int detect_tmux(void) | ||||
| { | ||||
|     char *tmux_env = getenv ("TMUX"), *pos; | ||||
|     char *tmux_env = getenv("TMUX"), *pos; | ||||
|  | ||||
|     if (!tmux_env) | ||||
|     if (!tmux_env) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /* find second separator */ | ||||
|     pos = strrchr (tmux_env, ','); | ||||
|     pos = strrchr(tmux_env, ','); | ||||
|  | ||||
|     if (!pos) | ||||
|     if (!pos) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /* store the session number string for later use */ | ||||
|     snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1); | ||||
|     /* store the session id for later use */ | ||||
|     snprintf(mplex_data, sizeof(mplex_data), "$%s", pos + 1); | ||||
|     mplex = MPLEX_TMUX; | ||||
|     return 1; | ||||
| } | ||||
| @@ -228,98 +258,107 @@ static int detect_tmux () | ||||
|    Returns 1 if present, 0 otherwise. This value can be used to determine | ||||
|    whether an auto-away detection timer is needed. | ||||
|  */ | ||||
| static int detect_mplex () | ||||
| static int detect_mplex(void) | ||||
| { | ||||
|     /* try screen, and if fails try tmux */ | ||||
|     return detect_gnu_screen () || detect_tmux (); | ||||
|     return detect_gnu_screen() || detect_tmux(); | ||||
| } | ||||
|  | ||||
| /* Detects gnu screen session attached/detached by examining permissions of | ||||
|    the session's unix socket. | ||||
|  */ | ||||
| static int gnu_screen_is_detached () | ||||
| static int gnu_screen_is_detached(void) | ||||
| { | ||||
|     if (mplex != MPLEX_SCREEN) | ||||
|     if (mplex != MPLEX_SCREEN) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     struct stat sb; | ||||
|  | ||||
|     if (stat (mplex_data, &sb) != 0) | ||||
|     if (stat(mplex_data, &sb) != 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /* execution permission (x) means attached */ | ||||
|     return ! (sb.st_mode & S_IXUSR); | ||||
|     return !(sb.st_mode & S_IXUSR); | ||||
| } | ||||
|  | ||||
| /* Detects tmux attached/detached by getting session data and finding the | ||||
|    current session's entry. An attached entry ends with "(attached)". Example: | ||||
|  | ||||
|     $ tmux list-sessions | ||||
|     0: 1 windows (created Mon Mar  2 21:48:29 2015) [80x23] (attached) | ||||
|     1: 2 windows (created Mon Mar  2 21:48:43 2015) [80x23] | ||||
|  | ||||
|     In this example, session 0 is attached and session 1 is detached. | ||||
| */ | ||||
| static int tmux_is_detached () | ||||
|    current session's entry. | ||||
|  */ | ||||
| static int tmux_is_detached(void) | ||||
| { | ||||
|     if (mplex != MPLEX_TMUX) | ||||
|     if (mplex != MPLEX_TMUX) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     FILE *session_info_stream = NULL; | ||||
|     char *dyn_buffer = NULL, *search_str = NULL; | ||||
|     char *entry_pos, *nl_pos, *attached_pos; | ||||
|     const int numstr_len = strlen (mplex_data); | ||||
|     char *entry_pos; | ||||
|     int detached; | ||||
|     const int numstr_len = strlen(mplex_data); | ||||
|  | ||||
|     session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r"); | ||||
|     /* get the number of attached clients for each session */ | ||||
|     session_info_stream = popen("tmux list-sessions -F \"#{session_id} #{session_attached}\"", "r"); | ||||
|  | ||||
|     if (!session_info_stream) | ||||
|     if (!session_info_stream) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     dyn_buffer = read_into_dyn_buffer (session_info_stream); | ||||
|     dyn_buffer = read_into_dyn_buffer(session_info_stream); | ||||
|  | ||||
|     if (!dyn_buffer) | ||||
|     if (!dyn_buffer) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     pclose (session_info_stream); | ||||
|     pclose(session_info_stream); | ||||
|     session_info_stream = NULL; | ||||
|  | ||||
|     /* prepare search string, for finding the current session's entry */ | ||||
|     search_str = (char *) malloc (numstr_len + 4); | ||||
|     search_str = malloc(numstr_len + 2); | ||||
|  | ||||
|     if (search_str == NULL) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     search_str[0] = '\n'; | ||||
|     strcpy (search_str + 1, mplex_data); | ||||
|     strcat (search_str, ": "); | ||||
|     strcpy(search_str + 1, mplex_data); | ||||
|  | ||||
|     /* do the search */ | ||||
|     if (strncmp (dyn_buffer, search_str + 1, numstr_len + 2) == 0) | ||||
|     if (strncmp(dyn_buffer, search_str + 1, numstr_len) == 0) { | ||||
|         entry_pos = dyn_buffer; | ||||
|     else | ||||
|         entry_pos = strstr (dyn_buffer, search_str); | ||||
|     } else { | ||||
|         entry_pos = strstr(dyn_buffer, search_str); | ||||
|     } | ||||
|  | ||||
|     if (! entry_pos) | ||||
|     if (! entry_pos) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* find the next \n and look for the "(attached)" before it */ | ||||
|     nl_pos = strchr (entry_pos + 1, '\n'); | ||||
|     attached_pos = strstr (entry_pos + 1, "(attached)\n"); | ||||
|     entry_pos = strchr(entry_pos, ' ') + 1; | ||||
|     detached = strncmp(entry_pos, "0\n", 2) == 0; | ||||
|  | ||||
|     free (search_str); | ||||
|     free(search_str); | ||||
|     search_str = NULL; | ||||
|  | ||||
|     free (dyn_buffer); | ||||
|     free(dyn_buffer); | ||||
|     dyn_buffer = NULL; | ||||
|  | ||||
|     return attached_pos == NULL  ||  attached_pos > nl_pos; | ||||
|     return detached; | ||||
|  | ||||
| fail: | ||||
|  | ||||
|     if (session_info_stream) | ||||
|         pclose (session_info_stream); | ||||
|     if (session_info_stream) { | ||||
|         pclose(session_info_stream); | ||||
|     } | ||||
|  | ||||
|     if (dyn_buffer) | ||||
|         free (dyn_buffer); | ||||
|     if (dyn_buffer) { | ||||
|         free(dyn_buffer); | ||||
|     } | ||||
|  | ||||
|     if (search_str) | ||||
|         free (search_str); | ||||
|     if (search_str) { | ||||
|         free(search_str); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -333,24 +372,25 @@ fail: | ||||
|    sample its state and update away status according to attached/detached state | ||||
|    of the mplex. | ||||
|  */ | ||||
| static int mplex_is_detached () | ||||
| static int mplex_is_detached(void) | ||||
| { | ||||
|     return gnu_screen_is_detached ()  ||  tmux_is_detached (); | ||||
|     return gnu_screen_is_detached()  ||  tmux_is_detached(); | ||||
| } | ||||
|  | ||||
| static void mplex_timer_handler (Tox *m) | ||||
| static void mplex_timer_handler(Tox *m) | ||||
| { | ||||
|     TOX_USER_STATUS current_status, new_status; | ||||
|     Tox_User_Status current_status, new_status; | ||||
|     const char *new_note; | ||||
|  | ||||
|     if (mplex == MPLEX_NONE) | ||||
|     if (mplex == MPLEX_NONE) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int detached = mplex_is_detached (); | ||||
|     int detached = mplex_is_detached(); | ||||
|  | ||||
|     pthread_mutex_lock (&Winthread.lock); | ||||
|     current_status = tox_self_get_status (m); | ||||
|     pthread_mutex_unlock (&Winthread.lock); | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     current_status = tox_self_get_status(m); | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) { | ||||
|         auto_away_active = false; | ||||
| @@ -360,25 +400,27 @@ static void mplex_timer_handler (Tox *m) | ||||
|         auto_away_active = true; | ||||
|         prev_status = current_status; | ||||
|         new_status = TOX_USER_STATUS_AWAY; | ||||
|         pthread_mutex_lock (&Winthread.lock); | ||||
|         pthread_mutex_lock(&Winthread.lock); | ||||
|         size_t slen = tox_self_get_status_message_size(m); | ||||
|         tox_self_get_status_message (m, (uint8_t *) prev_note); | ||||
|         tox_self_get_status_message(m, (uint8_t *) prev_note); | ||||
|         prev_note[slen] = '\0'; | ||||
|         pthread_mutex_unlock (&Winthread.lock); | ||||
|         pthread_mutex_unlock(&Winthread.lock); | ||||
|         new_note = user_settings->mplex_away_note; | ||||
|     } else | ||||
|     } else { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char argv[3][MAX_STR_SIZE]; | ||||
|     strcpy (argv[0], "/status"); | ||||
|     strcpy (argv[1], (new_status == TOX_USER_STATUS_AWAY ? "away" : | ||||
|                       new_status == TOX_USER_STATUS_BUSY ? "busy" : "online")); | ||||
|     argv[2][0] = '\"'; | ||||
|     strcpy (argv[2] + 1, new_note); | ||||
|     strcat (argv[2], "\""); | ||||
|     pthread_mutex_lock (&Winthread.lock); | ||||
|     cmd_status (prompt->chatwin->history, prompt, m, 2, argv); | ||||
|     pthread_mutex_unlock (&Winthread.lock); | ||||
|     char status_str[MAX_STR_SIZE]; | ||||
|     char note_str[MAX_STR_SIZE]; | ||||
|     const char *status = new_status == TOX_USER_STATUS_AWAY ? "away" : | ||||
|                          new_status == TOX_USER_STATUS_BUSY ? "busy" : "online"; | ||||
|     snprintf(status_str, sizeof(status_str), "/status %s", status); | ||||
|     snprintf(note_str, sizeof(status_str), "/note %s", new_note); | ||||
|  | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     execute(prompt->chatwin->history, prompt, m, status_str, GLOBAL_COMMAND_MODE); | ||||
|     execute(prompt->chatwin->history, prompt, m, note_str, GLOBAL_COMMAND_MODE); | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
| } | ||||
|  | ||||
| /* Time in seconds between calls to mplex_timer_handler */ | ||||
| @@ -394,20 +436,24 @@ void *mplex_timer_thread(void *data) | ||||
|     } | ||||
| } | ||||
|  | ||||
| int init_mplex_away_timer (Tox *m) | ||||
| int init_mplex_away_timer(Tox *m) | ||||
| { | ||||
|     if (! detect_mplex ()) | ||||
|     if (! detect_mplex()) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (! user_settings->mplex_away) | ||||
|     if (! user_settings->mplex_away) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /* status access mutex */ | ||||
|     if (pthread_mutex_init (&status_lock, NULL) != 0) | ||||
|     if (pthread_mutex_init(&status_lock, NULL) != 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (pthread_create(&mplex_tid, NULL, mplex_timer_thread, (void *) m) != 0) | ||||
|     if (pthread_create(&mplex_tid, NULL, mplex_timer_thread, (void *) m) != 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -24,12 +24,12 @@ | ||||
| #define TERM_MPLEX_H | ||||
|  | ||||
| /* Checks if Toxic runs inside a terminal multiplexer (GNU screen or tmux). If | ||||
|    yes, it initializes a timer which periodically checks the attached/detached | ||||
|    state of the terminal and updates away status accordingly. | ||||
|  * yes, it initializes a timer which periodically checks the attached/detached | ||||
|  * state of the terminal and updates away status accordingly. | ||||
|  */ | ||||
| int init_mplex_away_timer (Tox *m); | ||||
| int init_mplex_away_timer(Tox *m); | ||||
|  | ||||
| void lock_status (); | ||||
| void unlock_status (); | ||||
| void lock_status(void); | ||||
| void unlock_status(void); | ||||
|  | ||||
| #endif /* #define TERM_MPLEX_H */ | ||||
| #endif /* TERM_MPLEX_H */ | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user