1
0
mirror of https://github.com/Tha14/toxic.git synced 2025-06-30 08:26:45 +02:00

Compare commits

..

60 Commits

Author SHA1 Message Date
b1d8ab102f Bump to version 0.8.1 2017-11-17 17:44:56 -05:00
c387df35f8 Merge branch 'f8l-pkgsrc' 2017-11-16 16:03:20 -05:00
351a50c214 Do not assume what is not Linux or a BSD descendant is OSX 2017-11-15 21:02:10 +01:00
93175314b5 Merge branch 'aSourceFish-master' 2017-11-15 02:25:55 -05:00
b905a1a3c5 One more bit of standard... 2017-11-15 05:56:13 +02:00
c4386b195f A bit of standard... 2017-11-15 05:54:46 +02:00
ed1e617380 Make Toxic compile at NetBSD, STAGE 2 2017-11-15 05:50:00 +02:00
1382adb1f6 Make Toxic compile at NetBSD, STAGE 1 2017-11-15 05:46:47 +02:00
ecf1c317b7 Make Toxic compile at NetBSD, STAGE 0 2017-11-15 05:24:19 +02:00
cf0b99f1e5 Merge branch 'aramchandran-master' 2017-11-13 15:37:40 -05:00
3605a296a9 Update install.mk 2017-11-13 13:30:22 -05:00
9375d220f9 Merge branch 'aSourceFish-master' 2017-11-11 16:51:34 -05:00
8f94b0a218 Shut down the warning at *BSD 2017-11-11 21:17:34 +02:00
85a0becbf9 Make Toxic compile at OpenBSD 2017-11-11 21:04:45 +02:00
fec36ad9e6 Make Toxic compile at OpenBSD 2017-11-11 21:03:28 +02:00
ecdf6f01d2 Merge branch 'EnniRosario-refresh_devices' 2017-10-31 16:24:53 -04:00
e1bfa30769 Refresh device list on /lsdev. 2017-10-31 18:08:06 +02:00
ebcbc7497b Indicate selected device when printing. 2017-10-31 17:45:39 +02:00
e844ece28b Merge branch 'zugz-reduceRedrawing' 2017-10-29 16:00:46 -04:00
8508451ba6 avoid unnecessary redrawing 2017-10-29 16:50:42 +01:00
5cc83a7cb5 Merge branch 'patheticpat-fix-tmux' 2017-09-05 17:43:13 -04:00
febc725763 Fix tmux detached detection
The old code failed if the session had a custom name instead of the
default numeric id. To be safe in both cases, look for the session_id in
the tmux list-sessions output.
2017-09-05 12:49:48 +02:00
f2c116feb3 Fix potential string truncations with snprintf 2017-09-01 17:36:17 -04:00
52dd60dc86 Fix potential int truncation and double-check lengths before copy 2017-08-28 19:37:19 -04:00
80c0500299 Fix formatting bug caused by strings containing \r 2017-08-28 18:26:52 -04:00
ab490d28b4 Merge branch 'avoidr-status_cmd_feedback' 2017-08-28 17:13:53 -04:00
a9f7f85617 /status: give feedback to user on status change 2017-08-27 12:27:04 +02:00
1bfc1ba371 Merge branch 'Dako300-master' 2017-07-07 20:03:07 -04:00
2ede39369a added /bitrate 2017-07-07 23:47:59 +00:00
922c184195 Only include python libs when necessary 2017-06-06 19:03:40 -04:00
56a9571509 Make sure message id fits inside a signed int 2017-06-02 01:15:44 -04:00
0136f22076 Fix UI bugs & format
This fixes a bug where lines would sometimes be incorrectly marked as unread, as well as
a bug where inbound messages would sometimes be coloured incorrectly
2017-06-01 16:46:12 -04:00
c4ace288af Bump to v0.8.0 2017-05-24 15:10:50 -04:00
6d3fbfee59 Merge branch 'TsarFox-master' 2017-05-24 14:57:32 -04:00
=
be5e7906da Fix fortune.py input issues 2017-05-24 10:10:18 -04:00
369f26932e Fix error messages being printed when autorun_path is unset. 2017-05-23 19:12:53 -04:00
22ea522baf Add "/run" to autocompleter and fix example Python script. 2017-05-22 20:47:56 -04:00
4f60d546e6 Disable Python support in the default build 2017-05-21 16:18:39 -04:00
76d1eafdc0 Change visibility on Python commands list 2017-05-20 08:18:28 -04:00
37912f2d88 Update INSTALL.md 2017-05-19 21:09:29 -04:00
09710327c5 Merge branch 'Dako300-master' 2017-05-19 20:05:52 -04:00
acee4615f8 double signal handler prevents accidental closes 2017-05-19 14:38:39 -05:00
5ed26eda9b Added constants to the Python interface for calls to execute. 2017-05-19 15:35:31 -04:00
6d2b90ac9f Error reporting on failure of invoke_autoruns and solved issue with call to Py_FinalizeEx 2017-05-18 21:37:28 -04:00
02ea0fac44 get_status returns a string rather than an integer. 2017-05-17 08:39:29 -04:00
7d3d129624 Updated code style 2017-05-16 23:19:39 -04:00
b3ed8bc35c Finalized and documented the Python scripting interface. 2017-05-16 20:31:23 -04:00
90210daca7 Python interface for getting the user's nickname/status and executing commands 2017-05-15 22:12:28 -04:00
0e13a1f1bc Implemented a basic Python scripting engine, optional compilation. 2017-05-15 20:29:00 -04:00
09e2690211 Fixed INSTALL formatting (#443) 2017-04-12 12:00:43 -04:00
e65e3af274 Merge branch 'peetcamron-fix/fflush-for-musl' 2017-01-28 00:45:28 -05:00
c6c60d018e Added some fflush after the printf with no \n so we don't have a blank
screen when compiled with musl.
2017-01-28 00:06:03 -05:00
451d4ced80 Add option that prints toxic and toxcore version 2017-01-25 22:13:59 -05:00
7a7402ff86 Update text files with new toktok toxcore link 2016-12-21 17:08:32 -05:00
600e013adc Bump to version 0.7.2 2016-12-18 00:11:24 -05:00
1d71e2eb18 Port to toktok-c-toxcore branch 2016-12-18 00:01:51 -05:00
f858714edd Fix small nodeslist parsing bug 2016-10-23 12:26:18 -04:00
4df44a7274 Merge branch 'tharvik-master' 2016-10-18 12:21:58 -04:00
a26ed9d28f add missing implicit include 2016-10-18 09:38:28 +02:00
2bd5083b8f Corrected brew errors (#413)
Corrected brew errors
2016-10-07 13:24:31 -04:00
62 changed files with 2012 additions and 2449 deletions

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ stamp-h1
build/toxic build/toxic
build/*.o build/*.o
build/*.d build/*.d
apidoc/python/build

View File

@ -1,57 +1,53 @@
# Installation # Installation
* [Dependencies](#deps) * [Dependencies](#dependencies)
* [OS X Notes](#deps_osx) * [OS X Notes](#os-x-notes)
* [Compiling](#compiling) * [Compiling](#compiling)
* [Documentation](#docs) * [Documentation](#documentation)
* [Notes](#notes) * [Notes](#notes)
* [Compilation variables](#comp_vars) * [Compilation variables](#compilation-variables)
* [Packaging](#packaging) * [Packaging](#packaging)
<a name="deps" />
## Dependencies ## Dependencies
| Name | Needed by | Debian package | | Name | Needed by | Debian package |
|------------------------------------------------------|----------------------------|---------------------| |------------------------------------------------------|----------------------------|---------------------|
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* | | [Tox Core](https://github.com/toktok/c-toxcore) | BASE | *None* |
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev | | [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev | | [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
| [GNUmake](https://www.gnu.org/software/make) | BASE | make | | [GNUmake](https://www.gnu.org/software/make) | BASE | make |
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev| | [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev | | [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev |
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* | | [Tox Core AV](https://github.com/toktok/c-toxcore) | AUDIO | *None* |
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev | | [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev | | [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev | | [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
| [Python 3](http://www.python.org/) | PYTHON | python3-dev |
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc | | [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
<sup>1</sup>: see [Documentation](#docs)
<a name="deps_osx" /> <sup>1</sup>: see [Documentation](#documentation)
#### OS X Notes #### OS X Notes
Using [Homebrew](http://brew.sh): Using [Homebrew](http://brew.sh):
``` ```
brew install openal-soft freealut libconfig brew install openal-soft freealut libconfig
brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb brew install libnotify
``` ```
You can omit `libnotify` if you intend to build without desktop notifications enabled. You can omit `libnotify` if you intend to build without desktop notifications enabled.
<a name="Compiling">
## Compiling ## Compiling
``` ```
make PREFIX="/where/to/install" make PREFIX="/where/to/install"
sudo make install PREFIX="/where/to/install" sudo make install PREFIX="/where/to/install"
``` ```
<a name="docs" />
#### Documentation #### Documentation
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br /> Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br /> **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be 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 EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
<a name="notes" />
## Notes ## Notes
<a name="comp_vars" />
#### Compilation variables #### Compilation variables
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""` * You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones) * You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
@ -60,11 +56,11 @@ Run `make doc` in the build directory after editing the asciidoc files to regene
* `DISABLE_AV=1` → build toxic without audio call support * `DISABLE_AV=1` → build toxic without audio call support
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support * `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support * `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
* Features excluded from the default build must be explicitly enabled using special variables:
* `ENABLE_PYTHON=1` → build toxic with Python scripting support
<a name="packaging" />
#### Packaging #### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files * 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=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg") * `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") * `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")

View File

@ -30,6 +30,9 @@ endif
ifeq ($(UNAME_S), OpenBSD) ifeq ($(UNAME_S), OpenBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk -include $(CFG_DIR)/systems/FreeBSD.mk
endif endif
ifeq ($(UNAME_S), NetBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), Darwin) ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk -include $(CFG_DIR)/systems/Darwin.mk
endif endif

View File

@ -3,7 +3,7 @@
src="https://scan.coverity.com/projects/4975/badge.svg"/> src="https://scan.coverity.com/projects/4975/badge.svg"/>
</a> </a>
Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application. Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/toktok/c-toxcore), and is now available as a standalone application.
[![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png) [![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png)

20
apidoc/python/Makefile Normal file
View 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)

View 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.8.1'
# The full version, including alpha/beta/rc tags.
release = '0.8.1'
# 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'),
]

View File

@ -0,0 +1,8 @@
============
API Examples
============
Fortune
=======
.. literalinclude:: fortune.py
:language: python

View 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)

View File

@ -0,0 +1,9 @@
Toxic Scripting Interface Documentation
=======================================
.. toctree::
:maxdepth: 2
intro
reference
examples

View 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.

View 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

View File

@ -18,4 +18,4 @@ else ifneq ($(MAKECMDGOALS), clean)
$(warning WARNING -- Toxic will be compiled without audio support) $(warning WARNING -- Toxic will be compiled without audio support)
$(warning WARNING -- You need these libraries for audio support) $(warning WARNING -- You need these libraries for audio support)
$(warning WARNING -- $(MISSING_AUDIO_LIBS)) $(warning WARNING -- $(MISSING_AUDIO_LIBS))
endif endif

View File

@ -40,6 +40,12 @@ ifneq ($(QR_PNG), disabled)
-include $(CHECKS_DIR)/qr_png.mk -include $(CHECKS_DIR)/qr_png.mk
endif endif
# Check if we want build Python scripting support
PYTHON = $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
ifneq ($(PYTHON), disabled)
-include $(CHECKS_DIR)/python.mk
endif
# Check if we can build Toxic # Check if we can build Toxic
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error) ifneq ($(CHECK_LIBS), error)

15
cfg/checks/python.mk Normal file
View 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

View File

@ -1,5 +1,5 @@
# Version # Version
TOXIC_VERSION = 0.7.1 TOXIC_VERSION = 0.8.1
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
ifneq (, $(findstring error, $(REV))) ifneq (, $(findstring error, $(REV)))
VERSION = $(TOXIC_VERSION) VERSION = $(TOXIC_VERSION)
@ -28,5 +28,6 @@ BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man MANDIR = $(PREFIX)/share/man
APPDIR = $(PREFIX)/share/applications APPDIR = $(PREFIX)/share/applications
# Platform tools # Platform tools
PKG_CONFIG = pkg-config PKG_CONFIG = pkg-config

View File

@ -15,6 +15,7 @@ help:
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support" @echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" @echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support" @echo " 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 " USER_CFLAGS: Add custom flags to default CFLAGS" @echo " USER_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS" @echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" @echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"

View File

@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic
mv temp_file $$file ;\ mv temp_file $$file ;\
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
mv temp_file $$file ;\ mv temp_file $$file ;\
gzip -f -9 $$file ;\ gzip -f -n -9 $$file ;\
done done
.PHONY: install .PHONY: install

View File

@ -1,13 +1,13 @@
'\" t '\" t
.\" Title: toxic.conf .\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section] .\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" Date: 2016-07-21 .\" Date: 2016-09-20
.\" Manual: Toxic Manual .\" Manual: Toxic Manual
.\" Source: toxic __VERSION__ .\" Source: toxic __VERSION__
.\" Language: English .\" Language: English
.\" .\"
.TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual" .TH "TOXIC\&.CONF" "5" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * Define some portability stuff .\" * Define some portability stuff
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
@ -148,11 +148,6 @@ Indicator for alert messages\&.
Indicator for normal messages\&. Indicator for normal messages\&.
.RE .RE
.PP .PP
\fBline_special\fR
.RS 4
Indicator for special messages\&.
.RE
.PP
\fBmplex_away\fR \fBmplex_away\fR
.RS 4 .RS 4
Set user status when attaching and detaching from GNU screen or tmux\&. true or false Set user status when attaching and detaching from GNU screen or tmux\&. true or false
@ -232,6 +227,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) Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
.RE .RE
.PP .PP
\fBautorun_path\fR
.RS 4
Path for any scripts that should be run on startup
.RE
.PP
\fBchatlogs_path\fR \fBchatlogs_path\fR
.RS 4 .RS 4
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.

View File

@ -94,9 +94,6 @@ OPTIONS
*line_normal*;; *line_normal*;;
Indicator for normal messages. Indicator for normal messages.
*line_special*;;
Indicator for special messages.
*mplex_away*;; *mplex_away*;;
Set user status when attaching and detaching from GNU screen or tmux. Set user status when attaching and detaching from GNU screen or tmux.
true or false true or false
@ -146,6 +143,9 @@ OPTIONS
*avatar_path*;; *avatar_path*;;
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB) Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
*autorun_path*;;
Path for any scripts that should be run on startup
*chatlogs_path*;; *chatlogs_path*;;
Default path for chatlogs. String value. Absolute path for chatlog files. Default path for chatlogs. String value. Absolute path for chatlog files.

View File

@ -62,9 +62,6 @@ ui = {
// Indicator for normal messages. // Indicator for normal messages.
line_normal="---"; line_normal="---";
// Indicator for special messages (currently only used for private group messages)
line_special=">>>";
// true to change status based on screen/tmux attach/detach, false to disable // true to change status based on screen/tmux attach/detach, false to disable
mplex_away=true; mplex_away=true;
@ -90,6 +87,9 @@ tox = {
// Path for your avatar (file must be a .png and cannot exceed 64 KiB) // Path for your avatar (file must be a .png and cannot exceed 64 KiB)
// avatar_path="/home/USERNAME/Pictures/youravatar.png"; // avatar_path="/home/USERNAME/Pictures/youravatar.png";
// Path for scripts that should be run on startup
// autorun_path="/home/USERNAME/toxic_scripts/";
// Path for chatlogs // Path for chatlogs
// chatlogs_path="/home/USERNAME/toxic_chatlogs/"; // chatlogs_path="/home/USERNAME/toxic_chatlogs/";
}; };
@ -121,4 +121,3 @@ keys = {
toggle_peerlist="Ctrl+b"; toggle_peerlist="Ctrl+b";
toggle_paste_mode="Ctrl+T"; toggle_paste_mode="Ctrl+T";
}; };

210
src/api.c Normal file
View File

@ -0,0 +1,210 @@
/* 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"
#endif /* PYTHON */
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();
char timefrmt[TIME_STR_SIZE];
if (name == NULL)
return;
self_window = get_active_window();
get_time_str(timefrmt, sizeof(timefrmt));
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, timefrmt, 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, NULL, 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, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
return;
}
run_python(fp, argv[1]);
fclose(fp);
}
void invoke_autoruns(WINDOW *window, ToxWindow *self)
{
struct dirent *dir;
char abspath_buf[PATH_MAX + 1], err_buf[PATH_MAX + 1];
size_t path_len;
DIR *d;
FILE *fp;
if (user_settings->autorun_path[0] == '\0')
return;
d = opendir(user_settings->autorun_path);
if (d == NULL) {
snprintf(err_buf, PATH_MAX + 1, "Autorun path does not exist: %s", user_settings->autorun_path);
api_display(err_buf);
return;
}
cur_window = window;
self_window = self;
while ((dir = readdir(d)) != NULL) {
path_len = strlen(dir->d_name);
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
snprintf(abspath_buf, PATH_MAX + 1, "%s%s", user_settings->autorun_path, dir->d_name);
fp = fopen(abspath_buf, "r");
if (fp == NULL) {
snprintf(err_buf, PATH_MAX + 1, "Invalid path: %s", abspath_buf);
api_display(err_buf);
continue;
}
run_python(fp, abspath_buf);
fclose(fp);
}
}
closedir(d);
}

42
src/api.h Normal file
View File

@ -0,0 +1,42 @@
/* 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);
#endif /* #define API_H */

View File

@ -342,6 +342,7 @@ void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_
uint32_t video_bit_rate, void *user_data) uint32_t video_bit_rate, void *user_data)
{ {
CallControl.audio_bit_rate = audio_bit_rate; CallControl.audio_bit_rate = audio_bit_rate;
toxav_bit_rate_set(av, friend_number, audio_bit_rate, video_bit_rate, user_data);
} }
void callback_recv_invite(Tox *m, uint32_t friend_number) void callback_recv_invite(Tox *m, uint32_t friend_number)
@ -387,7 +388,7 @@ void callback_recv_starting(uint32_t friend_number)
windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state); windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state);
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */ if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!"); line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
return; return;
} }
@ -632,6 +633,9 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*
return; return;
} }
// Refresh device list.
get_devices_names();
print_devices(self, type); print_devices(self, type);
return; return;
@ -834,6 +838,55 @@ on_error:
print_err (self, error_str); print_err (self, error_str);
} }
void cmd_bitrate(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *error_str;
if ( argc != 1 ) {
error_str = "Must have value!";
goto on_error;
}
if ( self->is_call == false ) {
error_str = "Must be in a call";
goto on_error;
}
const uint32_t bitrate = strtol(argv[1], NULL, 10);
TOXAV_ERR_BIT_RATE_SET error;
audio_bit_rate_status_cb(CallControl.av, self->num, bitrate, -1, &error);
if (error != TOXAV_ERR_BIT_RATE_SET_OK) {
switch (error) {
case TOXAV_ERR_BIT_RATE_SET_SYNC:
error_str = "Syncronization error occured";
break;
case TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE:
error_str = "Invalid audio bit rate value (valid is 6-510)";
break;
case TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND:
error_str = "Friend not found";
break;
case TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL:
error_str = "Friend is not in the call";
break;
default:
error_str = "Unknown error";
}
goto on_error;
}
return;
on_error:
print_err (self, error_str);
}
void stop_current_call(ToxWindow *self) void stop_current_call(ToxWindow *self)
{ {

View File

@ -100,34 +100,7 @@ DeviceError init_devices(ToxAV *av_)
DeviceError init_devices() DeviceError init_devices()
#endif /* AUDIO */ #endif /* AUDIO */
{ {
const char *stringed_device_list; get_devices_names();
size[input] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
size[output] = 0;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
else
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
if (stringed_device_list) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
// Start poll thread // Start poll thread
if (pthread_mutex_init(&mutex, NULL) != 0) if (pthread_mutex_init(&mutex, NULL) != 0)
@ -160,6 +133,38 @@ DeviceError terminate_devices()
return (DeviceError) de_None; return (DeviceError) de_None;
} }
void get_devices_names() {
const char *stringed_device_list;
size[input] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
size[output] = 0;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
else
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
if (stringed_device_list) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
}
DeviceError device_mute(DeviceType type, uint32_t device_idx) DeviceError device_mute(DeviceType type, uint32_t device_idx)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
@ -481,8 +486,9 @@ void print_devices(ToxWindow *self, DeviceType type)
{ {
int i; int i;
for (i = 0; i < size[type]; ++i) for (i = 0; i < size[type]; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]); line_info_add(self, NULL, NULL, NULL, SYS_MSG, i == primary_device[type] ? 1 : 0, 0, "%d: %s", i, devices_names[type][i]);
}
return; return;
} }

View File

@ -61,6 +61,7 @@ DeviceError init_devices(ToxAV *av);
DeviceError init_devices(); DeviceError init_devices();
#endif /* AUDIO */ #endif /* AUDIO */
void get_devices_names();
DeviceError terminate_devices(); DeviceError terminate_devices();
/* Callback handles ready data from INPUT device */ /* Callback handles ready data from INPUT device */

View File

@ -110,6 +110,10 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile")) bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|| !strncmp(ubuf, "/avatar", strlen("/avatar")); || !strncmp(ubuf, "/avatar", strlen("/avatar"));
#ifdef PYTHON
dir_search = dir_search || !strncmp(ubuf, "/run", strlen("/run"));
#endif
/* isolate substring from space behind pos to pos */ /* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE]; char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", ubuf); snprintf(tmp, sizeof(tmp), "%s", ubuf);
@ -283,7 +287,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
if (dp == NULL) if (dp == NULL)
return -1; return -1;
char dirnames[MAX_DIRS][NAME_MAX]; char dirnames[MAX_DIRS][NAME_MAX + 1];
struct dirent *entry; struct dirent *entry;
int dircount = 0; int dircount = 0;

View File

@ -477,7 +477,7 @@ void *load_nodeslist_thread(void *data)
/* If nodeslist does not contain any valid entries we set the last_scan value /* 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. * 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}"; const char *s = "{\"last_scan\":0}";
rewind(fp); rewind(fp);
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails

View File

@ -65,10 +65,14 @@ static void init_infobox(ToxWindow *self);
static void kill_infobox(ToxWindow *self); static void kill_infobox(ToxWindow *self);
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef AUDIO #if defined(AUDIO) && defined(PYTHON)
#define AC_NUM_CHAT_COMMANDS 32
#elif AUDIO
#define AC_NUM_CHAT_COMMANDS 31 #define AC_NUM_CHAT_COMMANDS 31
#else #elif PYTHON
#define AC_NUM_CHAT_COMMANDS 23 #define AC_NUM_CHAT_COMMANDS 23
#else
#define AC_NUM_CHAT_COMMANDS 22
#endif /* AUDIO */ #endif /* AUDIO */
/* Array of chat command names used for tab completion. */ /* Array of chat command names used for tab completion. */
@ -81,7 +85,6 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/close" }, { "/close" },
{ "/connect" }, { "/connect" },
{ "/exit" }, { "/exit" },
{ "/gaccept" },
{ "/group" }, { "/group" },
{ "/help" }, { "/help" },
{ "/invite" }, { "/invite" },
@ -107,8 +110,15 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/mute" }, { "/mute" },
{ "/sense" }, { "/sense" },
{ "/video" }, { "/video" },
{ "/bitrate" },
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef PYTHON
{ "/run" },
#endif /* PYTHON */
}; };
static void set_self_typingstatus(ToxWindow *self, Tox *m, bool is_typing) static void set_self_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
@ -551,7 +561,7 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
bytes_convert_str(sizestr, sizeof(sizestr), file_size); bytes_convert_str(sizestr, sizeof(sizestr), file_size);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr);
char file_path[MAX_STR_SIZE]; char file_path[PATH_MAX + name_length + 1];
size_t path_len = name_length; size_t path_len = name_length;
/* use specified download path in config if possible */ /* use specified download path in config if possible */
@ -610,18 +620,25 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
&self->active_box, self->name, "Incoming file: %s", filename ); &self->active_box, self->name, "Incoming file: %s", filename );
} }
static void chat_onGroupInvite(ToxWindow *self, Tox *m, uint32_t friendnumber, const char *invite_data, static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, uint8_t type, const char *group_pub_key,
size_t length) uint16_t length)
{ {
if (self->num != friendnumber) if (self->num != friendnumber)
return; return;
if (Friends.list[friendnumber].group_invite.data) if (Friends.list[friendnumber].group_invite.key != NULL)
free(Friends.list[friendnumber].group_invite.data); free(Friends.list[friendnumber].group_invite.key);
Friends.list[friendnumber].group_invite.data = malloc(length * sizeof(uint8_t)); char *k = malloc(length);
memcpy(Friends.list[friendnumber].group_invite.data, invite_data, length);
if (k == NULL)
exit_toxic_err("Failed in chat_onGroupInvite", FATALERR_MEMORY);
memcpy(k, group_pub_key, length);
Friends.list[friendnumber].group_invite.key = k;
Friends.list[friendnumber].group_invite.pending = true;
Friends.list[friendnumber].group_invite.length = length; Friends.list[friendnumber].group_invite.length = length;
Friends.list[friendnumber].group_invite.type = type;
sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL); sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL);
@ -634,7 +651,7 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, uint32_t friendnumber, c
box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join group chat"); box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join group chat");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a group chat.", name); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a group chat.", name);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/gaccept\" to join the chat."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
} }
/* AV Stuff */ /* AV Stuff */
@ -879,8 +896,8 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
char timefrmt[TIME_STR_SIZE]; char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt)); get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action); int id = line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, ctx->hst->line_end->id + 1); cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id);
} }
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
@ -925,7 +942,16 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
diff = dir_match(self, m, ctx->line, L"/sendfile"); diff = dir_match(self, m, ctx->line, L"/sendfile");
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) { } else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
diff = dir_match(self, m, ctx->line, L"/avatar"); diff = dir_match(self, m, ctx->line, L"/avatar");
} else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { }
#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] = { const char status_cmd_list[3][8] = {
{"online"}, {"online"},
{"away"}, {"away"},
@ -977,8 +1003,8 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
char timefrmt[TIME_STR_SIZE]; char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt)); get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line); int id = line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1); cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id);
} }
} }
@ -1200,6 +1226,7 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
ret.onMessage = &chat_onMessage; ret.onMessage = &chat_onMessage;
ret.onConnectionChange = &chat_onConnectionChange; ret.onConnectionChange = &chat_onConnectionChange;
ret.onTypingChange = & chat_onTypingChange; ret.onTypingChange = & chat_onTypingChange;
ret.onGroupInvite = &chat_onGroupInvite;
ret.onNickChange = &chat_onNickChange; ret.onNickChange = &chat_onNickChange;
ret.onStatusChange = &chat_onStatusChange; ret.onStatusChange = &chat_onStatusChange;
ret.onStatusMessageChange = &chat_onStatusMessageChange; ret.onStatusMessageChange = &chat_onStatusMessageChange;
@ -1208,7 +1235,6 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
ret.onFileControl = &chat_onFileControl; ret.onFileControl = &chat_onFileControl;
ret.onFileRecv = &chat_onFileRecv; ret.onFileRecv = &chat_onFileRecv;
ret.onReadReceipt = &chat_onReadReceipt; ret.onReadReceipt = &chat_onReadReceipt;
ret.onGroupInvite = &chat_onGroupInvite;
#ifdef AUDIO #ifdef AUDIO
ret.onInvite = &chat_onInvite; ret.onInvite = &chat_onInvite;

View File

@ -78,47 +78,6 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent); close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
} }
void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
if (Friends.list[self->num].group_invite.length == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group invite");
return;
}
const char *passwd = NULL;
uint16_t passwd_len = 0;
if (argc > 0) {
passwd = argv[1];
passwd_len = strlen(passwd);
}
TOX_ERR_GROUP_INVITE_ACCEPT err;
uint32_t groupnumber = tox_group_invite_accept(m, Friends.list[self->num].group_invite.data,
Friends.list[self->num].group_invite.length,
(uint8_t *) passwd, passwd_len, &err);
if (err != TOX_ERR_GROUP_INVITE_ACCEPT_OK) {
if (err == TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group: Password too long.");
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to joing group (error %d).", err);
return;
}
if (init_groupchat_win(m, groupnumber, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnumber, NULL, 0, NULL);
return;
}
}
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
if (argc < 1) { if (argc < 1) {
@ -126,24 +85,61 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
return; return;
} }
int groupnum = atoi(argv[1]); long int groupnum = strtol(argv[1], NULL, 10);
if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
return; return;
} }
TOX_ERR_GROUP_INVITE_FRIEND err; TOX_ERR_CONFERENCE_INVITE err;
if (!tox_group_invite_friend(m, groupnum, self->num, &err)) { if (!tox_conference_invite(m, self->num, groupnum, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d)", err);
return; return;
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum);
} }
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
const char *groupkey = Friends.list[self->num].group_invite.key;
uint16_t length = Friends.list[self->num].group_invite.length;
uint8_t type = Friends.list[self->num].group_invite.type;
if (!Friends.list[self->num].group_invite.pending) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
return;
}
if (type != TOX_CONFERENCE_TYPE_TEXT) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
return;
}
TOX_ERR_CONFERENCE_JOIN err;
uint32_t groupnum = tox_conference_join(m, self->num, (uint8_t *) groupkey, length, &err);
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
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_conference_delete(m, groupnum, NULL);
return;
}
}
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
if (argc < 1) { if (argc < 1) {

View File

@ -26,9 +26,9 @@
#include "windows.h" #include "windows.h"
#include "toxic.h" #include "toxic.h"
void cmd_groupaccept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
@ -41,6 +41,7 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef VIDEO #ifdef VIDEO

View File

@ -33,8 +33,7 @@
#include "line_info.h" #include "line_info.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "notify.h" #include "notify.h"
#include "api.h"
#define MAX_NUM_ARGS 10 /* Includes command */
struct cmd_func { struct cmd_func {
const char *name; const char *name;
@ -51,7 +50,6 @@ static struct cmd_func global_commands[] = {
{ "/exit", cmd_quit }, { "/exit", cmd_quit },
{ "/group", cmd_groupchat }, { "/group", cmd_groupchat },
{ "/help", cmd_prompt_help }, { "/help", cmd_prompt_help },
{ "/join", cmd_join },
{ "/log", cmd_log }, { "/log", cmd_log },
{ "/myid", cmd_myid }, { "/myid", cmd_myid },
{ "/myqr", cmd_myqr }, { "/myqr", cmd_myqr },
@ -68,15 +66,18 @@ static struct cmd_func global_commands[] = {
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef VIDEO #ifdef VIDEO
{ "/lsvdev", cmd_list_video_devices }, { "/lsvdev", cmd_list_video_devices },
{ "/svdev" , cmd_change_video_device }, { "/svdev", cmd_change_video_device },
#endif /* VIDEO */ #endif /* VIDEO */
#ifdef PYTHON
{ "/run", cmd_run },
#endif /* PYTHON */
{ NULL, NULL }, { NULL, NULL },
}; };
static struct cmd_func chat_commands[] = { static struct cmd_func chat_commands[] = {
{ "/cancel", cmd_cancelfile }, { "/cancel", cmd_cancelfile },
{ "/gaccept", cmd_groupaccept },
{ "/invite", cmd_groupinvite }, { "/invite", cmd_groupinvite },
{ "/join", cmd_join_group },
{ "/savefile", cmd_savefile }, { "/savefile", cmd_savefile },
{ "/sendfile", cmd_sendfile }, { "/sendfile", cmd_sendfile },
#ifdef AUDIO #ifdef AUDIO
@ -86,6 +87,7 @@ static struct cmd_func chat_commands[] = {
{ "/hangup", cmd_hangup }, { "/hangup", cmd_hangup },
{ "/mute", cmd_mute }, { "/mute", cmd_mute },
{ "/sense", cmd_sense }, { "/sense", cmd_sense },
{ "/bitrate", cmd_bitrate },
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef VIDEO #ifdef VIDEO
{ "/video", cmd_video }, { "/video", cmd_video },
@ -94,99 +96,19 @@ static struct cmd_func chat_commands[] = {
}; };
static struct cmd_func group_commands[] = { static struct cmd_func group_commands[] = {
{ "/ban", cmd_ban }, { "/title", cmd_set_title },
{ "/chatid", cmd_chatid },
{ "/ignore", cmd_ignore },
{ "/kick", cmd_kick },
{ "/mod", cmd_mod },
{ "/mykey", cmd_mykey },
{ "/passwd", cmd_set_passwd },
{ "/peerlimit", cmd_set_peerlimit },
{ "/privacy", cmd_set_privacy },
{ "/rejoin", cmd_rejoin },
{ "/silence", cmd_silence },
{ "/topic", cmd_set_topic },
{ "/unban", cmd_unban },
{ "/unignore", cmd_unignore },
{ "/unmod", cmd_unmod },
{ "/unsilence", cmd_unsilence },
{ "/whois", cmd_whois },
#ifdef AUDIO #ifdef AUDIO
{ "/mute", cmd_mute }, { "/mute", cmd_mute },
{ "/sense", cmd_sense }, { "/sense", cmd_sense },
#endif /* AUDIO */ #endif /* AUDIO */
{ NULL, NULL }, { NULL, NULL },
}; };
#define NUM_SPECIAL_COMMANDS 15 /* Parses input command and puts args into arg array.
static const char special_commands[NUM_SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = { Returns number of arguments on success, -1 on failure. */
"/ban",
"/gaccept",
"/group",
"/ignore",
"/kick",
"/mod",
"/nick",
"/note",
"/passwd",
"/silence",
"/topic",
"/unignore",
"/unmod",
"/unsilence",
"/whois",
};
/* return true if input command is in the special_commands array. False otherwise.*/
static bool is_special_command(const char *input)
{
int s = char_find(0, input, ' ');
if (s == strlen(input))
return false;
int i;
for (i = 0; i < NUM_SPECIAL_COMMANDS; ++i) {
if (strncmp(input, special_commands[i], s) == 0)
return true;
}
return false;
}
/* Parses commands in the special_commands array which take exactly one argument that may contain spaces.
* Unlike parse_command, this function does not split the input string at spaces.
*
* Returns number of arguments on success
* Returns -1 on failure
*/
static int parse_special_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{
int len = strlen(input);
int s = char_find(0, input, ' ');
if (s + 1 >= len)
return -1;
memcpy(args[0], input, s);
args[0][s++] = '\0'; /* increment to remove space after /command */
memcpy(args[1], input + s, len - s);
args[1][len - s] = '\0';
return 2;
}
/* Parses input command and puts args (split by spaces) into args array.
*
* Returns number of arguments on success
* Returns -1 on failure.
*/
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{ {
if (is_special_command(input))
return parse_special_command(w, self, input, args);
char *cmd = strdup(input); char *cmd = strdup(input);
if (cmd == NULL) if (cmd == NULL)
@ -224,19 +146,11 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
strcpy(cmd, tmp); /* tmp will always fit inside cmd */ strcpy(cmd, tmp); /* tmp will always fit inside cmd */
} }
/* Ugly special case concatinates all args after arg1 for multi-word group passwords */
if (num_args > 2 && strcmp(args[0], "/join") == 0)
strcpy(args[2], input + strlen(args[0]) + 1 + strlen(args[1]) + 1);
free(cmd); free(cmd);
return num_args; return num_args;
} }
/* Matches command to respective function. /* Matches command to respective function. Returns 0 on match, 1 on no match */
*
* Returns 0 on match,
* Returns -1 on no match
*/
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
char (*args)[MAX_STR_SIZE]) char (*args)[MAX_STR_SIZE])
{ {
@ -249,7 +163,7 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c
} }
} }
return -1; return 1;
} }
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
@ -284,5 +198,12 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
if (do_command(w, self, m, num_args, global_commands, args) == 0) if (do_command(w, self, m, num_args, global_commands, args) == 0)
return; return;
#ifdef PYTHON
if (do_plugin_command(num_args, args) == 0)
return;
#endif
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
} }

View File

@ -26,11 +26,13 @@
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"
#define MAX_NUM_ARGS 4 /* Includes command */
enum { enum {
GLOBAL_COMMAND_MODE, GLOBAL_COMMAND_MODE,
CHAT_COMMAND_MODE, CHAT_COMMAND_MODE,
GROUPCHAT_COMMAND_MODE, GROUPCHAT_COMMAND_MODE,
} COMMAND_MODE; };
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);

View File

@ -113,11 +113,11 @@ static void realloc_blocklist(int n)
void kill_friendlist(void) void kill_friendlist(void)
{ {
size_t i; int i;
for (i = 0; i < Friends.max_idx; ++i) { for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].group_invite.data != NULL) if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL)
free(Friends.list[i].group_invite.data); free(Friends.list[i].group_invite.key);
} }
realloc_blocklist(0); realloc_blocklist(0);
@ -153,10 +153,14 @@ static int save_blocklist(char *path)
} }
if (Blocked.list[i].active) { if (Blocked.list[i].active) {
if (Blocked.list[i].namelength > TOXIC_MAX_NAME_LENGTH) {
continue;
}
BlockedFriend tmp; BlockedFriend tmp;
memset(&tmp, 0, sizeof(BlockedFriend)); memset(&tmp, 0, sizeof(BlockedFriend));
tmp.namelength = htons(Blocked.list[i].namelength); tmp.namelength = htons(Blocked.list[i].namelength);
memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1); memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1); // Include null byte
memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE); memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE);
uint8_t lastonline[sizeof(uint64_t)]; uint8_t lastonline[sizeof(uint64_t)];
@ -250,10 +254,15 @@ int load_blocklist(char *path)
memset(&Blocked.list[i], 0, sizeof(BlockedFriend)); memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend)); memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
Blocked.list[i].namelength = ntohs(tmp.namelength);
if (Blocked.list[i].namelength > TOXIC_MAX_NAME_LENGTH) {
continue;
}
Blocked.list[i].active = true; Blocked.list[i].active = true;
Blocked.list[i].num = i; Blocked.list[i].num = i;
Blocked.list[i].namelength = MIN(TOXIC_MAX_NAME_LENGTH, ntohs(tmp.namelength)); memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1); // copy null byte
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1);
memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE); memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE);
uint8_t lastonline[sizeof(uint64_t)]; uint8_t lastonline[sizeof(uint64_t)];
@ -520,7 +529,8 @@ static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
} }
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, uint32_t num, const char *data, size_t length) static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, const char *group_pub_key,
uint16_t length)
{ {
if (num >= Friends.max_idx) if (num >= Friends.max_idx)
return; return;
@ -580,6 +590,9 @@ static void delete_friend(Tox *m, uint32_t f_num)
} }
} }
if (Friends.list[f_num].group_invite.key != NULL)
free(Friends.list[f_num].group_invite.key);
memset(&Friends.list[f_num], 0, sizeof(ToxicFriend)); memset(&Friends.list[f_num], 0, sizeof(ToxicFriend));
int i; int i;
@ -653,7 +666,7 @@ static void draw_del_popup(void)
wattroff(PendingDelete.popup, A_BOLD); wattroff(PendingDelete.popup, A_BOLD);
wprintw(PendingDelete.popup, "? y/n"); wprintw(PendingDelete.popup, "? y/n");
wrefresh(PendingDelete.popup); wnoutrefresh(PendingDelete.popup);
} }
/* deletes contact from blocked list */ /* deletes contact from blocked list */
@ -882,7 +895,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
wprintw(self->window, "%02X", Blocked.list[selected_num].pub_key[i] & 0xff); wprintw(self->window, "%02X", Blocked.list[selected_num].pub_key[i] & 0xff);
} }
wrefresh(self->window); wnoutrefresh(self->window);
draw_del_popup(); draw_del_popup();
if (self->help->active) if (self->help->active)
@ -1099,7 +1112,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff); wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff);
} }
wrefresh(self->window); wnoutrefresh(self->window);
draw_del_popup(); draw_del_popup();
if (self->help->active) if (self->help->active)

View File

@ -35,14 +35,16 @@ struct LastOnline {
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */ char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
}; };
struct GroupInvite { struct GroupChatInvite {
uint8_t *data; char *key;
uint16_t length; uint16_t length;
uint8_t type;
bool pending;
}; };
typedef struct { typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1]; char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength; uint16_t namelength;
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
size_t statusmsg_len; size_t statusmsg_len;
char pub_key[TOX_PUBLIC_KEY_SIZE]; char pub_key[TOX_PUBLIC_KEY_SIZE];
@ -55,7 +57,7 @@ typedef struct {
uint8_t status; uint8_t status;
struct LastOnline last_online; struct LastOnline last_online;
struct GroupInvite group_invite; struct GroupChatInvite group_invite;
struct FileTransfer file_receiver[MAX_FILES]; struct FileTransfer file_receiver[MAX_FILES];
struct FileTransfer file_sender[MAX_FILES]; struct FileTransfer file_sender[MAX_FILES];
@ -63,7 +65,7 @@ typedef struct {
typedef struct { typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1]; char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength; uint16_t namelength;
char pub_key[TOX_PUBLIC_KEY_SIZE]; char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num; uint32_t num;
bool active; bool active;

View File

@ -329,133 +329,42 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
} }
if (argc < 1) { if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name required"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio");
return; return;
} }
const char *tmp_name = argv[1]; uint8_t type;
int len = strlen(tmp_name);
if (len == 0 || len > TOX_GROUP_MAX_GROUP_NAME_LENGTH) { if (!strcasecmp(argv[1], "audio"))
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group name."); type = TOX_CONFERENCE_TYPE_AV;
else if (!strcasecmp(argv[1], "text"))
type = TOX_CONFERENCE_TYPE_TEXT;
else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
return; return;
} }
char name[TOX_GROUP_MAX_GROUP_NAME_LENGTH]; if (type != TOX_CONFERENCE_TYPE_TEXT) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
snprintf(name, sizeof(name), "%s", &argv[1][1]);
len -= 2;
name[len] = '\0';
} else {
snprintf(name, sizeof(name), "%s", argv[1]);
}
TOX_ERR_GROUP_NEW err;
uint32_t groupnum = tox_group_new(m, TOX_GROUP_PRIVACY_STATE_PUBLIC, (uint8_t *) name, len, &err);
if (err != TOX_ERR_GROUP_NEW_OK) {
switch (err) {
case TOX_ERR_GROUP_NEW_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name length cannot exceed %d.",
TOX_GROUP_MAX_GROUP_NAME_LENGTH);
break;
}
case TOX_ERR_GROUP_NEW_EMPTY: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group name cannot be empty.");
break;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d).", err);
break;
}
}
return; return;
} }
int init = init_groupchat_win(m, groupnum, name, len); TOX_ERR_CONFERENCE_NEW err;
if (init == -1) { uint32_t groupnum = tox_conference_new(m, &err);
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
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."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnum, NULL, 0, NULL); tox_conference_delete(m, groupnum, NULL);
} else if (init == -2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"You have been kicked from a group. Close the window and try again.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
}
}
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return; return;
} }
if (argc < 1) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Chat ID is required.");
return;
}
const char *chat_id = argv[1];
if (strlen(chat_id) != TOX_GROUP_CHAT_ID_SIZE * 2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID");
return;
}
char id_bin[TOX_GROUP_CHAT_ID_SIZE] = {0};
size_t i;
char xch[3];
uint32_t x;
for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
xch[0] = chat_id[2 * i];
xch[1] = chat_id[2 * i + 1];
xch[2] = '\0';
if (sscanf(xch, "%02x", &x) != 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid chat ID.");
return;
}
id_bin[i] = x;
}
const char *passwd = NULL;
uint16_t passwd_len = 0;
if (argc > 1) {
passwd = argv[2];
passwd_len = strlen(passwd);
}
TOX_ERR_GROUP_JOIN err;
uint32_t groupnum = tox_group_join(m, (uint8_t *) id_bin, (uint8_t *) passwd, passwd_len, &err);
if (err != TOX_ERR_GROUP_JOIN_OK) {
if (err == TOX_ERR_GROUP_JOIN_TOO_LONG)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length cannot exceed %d.", TOX_GROUP_MAX_PASSWORD_SIZE);
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err);
return;
}
int init = init_groupchat_win(m, groupnum, NULL, 0);
if (init == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
} else if (init == -2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"You have been kicked from a group. Close the window and try again.");
tox_group_leave(m, groupnum, NULL, 0, NULL);
}
} }
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -609,7 +518,6 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
tox_self_set_name(m, (uint8_t *) nick, len, NULL); tox_self_set_name(m, (uint8_t *) nick, len, NULL);
prompt_update_nick(prompt, nick); prompt_update_nick(prompt, nick);
set_nick_all_groups(m, nick, len);
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
} }
@ -621,7 +529,19 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
return; return;
} }
prompt_update_statusmessage(prompt, m, argv[1]); 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);
} }
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -721,7 +641,6 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
tox_self_set_status(m, status); tox_self_set_status(m, status);
prompt_update_status(prompt, status); prompt_update_status(prompt, status);
set_status_all_groups(m, status);
if (have_note) { if (have_note) {
if (argv[2][0] != '\"') { if (argv[2][0] != '\"') {
@ -736,6 +655,9 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
msg[len] = '\0'; msg[len] = '\0';
prompt_update_statusmessage(prompt, m, msg); prompt_update_statusmessage(prompt, m, msg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s: \"%s\".", status_str, msg);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
} }
finish: finish:

View File

@ -33,7 +33,6 @@ void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE
void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
@ -57,4 +56,8 @@ void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* VIDEO */ #endif /* VIDEO */
#ifdef PYTHON
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif
#endif /* #define GLOBAL_COMMANDS_H */ #endif /* #define GLOBAL_COMMANDS_H */

View File

@ -21,707 +21,66 @@
*/ */
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"
#include "line_info.h" #include "line_info.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "log.h" #include "log.h"
#include "groupchat.h"
extern GroupChat groupchats[MAX_GROUPCHAT_NUM]; void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
char chatid[TOX_GROUP_CHAT_ID_SIZE * 2 + 1] = {0}; TOX_ERR_CONFERENCE_TITLE err;
char chat_public_key[TOX_GROUP_CHAT_ID_SIZE]; char title[MAX_STR_SIZE];
TOX_ERR_GROUP_STATE_QUERIES err;
if (!tox_group_get_chat_id(m, self->num, (uint8_t *) chat_public_key, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve the Chat ID (error %d).", err);
return;
}
size_t i;
for (i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
char xx[3];
snprintf(xx, sizeof(xx), "%02X", chat_public_key[i] & 0xff);
strcat(chatid, xx);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", chatid);
}
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) { if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified."); size_t tlen = tox_conference_get_title_size(m, self->num, &err);
if (err != TOX_ERR_CONFERENCE_TITLE_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
return;
}
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
return;
}
title[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
return; return;
} }
const char *nick = argv[1]; if (argv[1][0] != '\"') {
uint32_t peer_id; line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return; return;
} }
TOX_ERR_GROUP_TOGGLE_IGNORE err; /* remove opening and closing quotes */
snprintf(title, sizeof(title), "%s", &argv[1][1]);
int len = strlen(title) - 1;
title[len] = '\0';
if (!tox_group_toggle_ignore(m, self->num, peer_id, true, &err)) { if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to ignore %s (error %d).", nick, err); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
return; return;
} }
set_window_title(self, title, len);
char timefrmt[TIME_STR_SIZE]; char timefrmt[TIME_STR_SIZE];
char selfnick[TOX_MAX_NAME_LENGTH];
get_time_str(timefrmt, sizeof(timefrmt)); get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- Ignoring %s", nick); tox_self_get_name(m, (uint8_t *) selfnick);
} size_t sn_len = tox_self_get_name_size(m);
static void cmd_kickban_helper(ToxWindow *self, Tox *m, const char *nick, bool set_ban)
{
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
const char *type_str = set_ban ? "ban" : "kick";
TOX_ERR_GROUP_MOD_REMOVE_PEER err;
tox_group_mod_remove_peer(m, self->num, peer_id, set_ban, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_REMOVE_PEER_OK: {
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to %s %s.", type_str, nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to %s %s from the group (error %d).", type_str, nick,
err);
return;
}
}
}
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
cmd_kickban_helper(self, m, argv[1], false);
}
void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
TOX_ERR_GROUP_BAN_QUERY err;
if (argc < 1) {
size_t num_banned = tox_group_ban_get_list_size(m, self->num, &err);
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list size (error %d).", err);
return;
}
if (num_banned == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list is empty.");
return;
}
uint32_t ban_list[num_banned];
if (!tox_group_ban_get_list(m, self->num, ban_list, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to get the ban list (error %d).", err);
return;
}
uint16_t i;
for (i = 0; i < num_banned; ++i) {
uint32_t id = ban_list[i];
size_t len = tox_group_ban_get_name_size(m, self->num, id, &err);
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name length for ban %d (error %d).", id, err);
continue;
}
char tmp_nick[len];
if (!tox_group_ban_get_name(m, self->num, id, (uint8_t *) tmp_nick, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve name for ban %d (error %d).", id, err);
continue;
}
char nick[len + 1];
copy_tox_str(nick, sizeof(nick), tmp_nick, len);
uint64_t time_set = tox_group_ban_get_time_set(m, self->num, id, &err);
if (err != TOX_ERR_GROUP_BAN_QUERY_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve timestamp for ban %d (error %d).", id, err);
continue;
}
struct tm tm_set = *localtime((const time_t *) &time_set);
char time_str[64];
strftime(time_str, sizeof(time_str), "%e %b %Y %H:%M:%S%p", &tm_set);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "ID %d : %s [Set:%s]", id, nick, time_str);
}
return;
}
cmd_kickban_helper(self, m, argv[1], true);
}
void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be specified.");
return;
}
int ban_id = atoi(argv[1]);
if (ban_id == 0 && strcmp(argv[1], "0")) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID must be a non-negative interger.");
return;
}
TOX_ERR_GROUP_MOD_REMOVE_BAN err;
tox_group_mod_remove_ban(m, self->num, ban_id, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_REMOVE_BAN_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban list entry with id %d has been removed.", ban_id);
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unban peers.");
return;
}
case TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ban ID does not exist.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to remove ban list entry (error %d).", err);
return;
}
}
}
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_MODERATOR, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to promote moderators.");
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "This peer is already a moderator.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to promote peer to moderator (error %d).", err);
return;
}
}
}
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_MODERATOR) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not a moderator", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Nice try.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to revoke moderator powers from %s (error %d).", nick,
err);
return;
}
}
}
void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
char pk[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
TOX_ERR_GROUP_SELF_QUERY err;
if (!tox_group_self_get_public_key(m, self->num, (uint8_t *) pk, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch your public key (error %d)", err);
return;
}
size_t i;
for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
char d[3];
snprintf(d, sizeof(d), "%02X", pk[i] & 0xff);
strcat(pk_string, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", pk_string);
}
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *passwd = NULL;
size_t len = 0;
if (argc > 0) {
passwd = argv[1];
len = strlen(passwd);
}
TOX_ERR_GROUP_FOUNDER_SET_PASSWORD err;
tox_group_founder_set_password(m, self->num, (uint8_t *) passwd, len, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK: {
if (len > 0)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been set to %s.", passwd);
else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password has been unset.");
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Password length must not exceed %d.",
TOX_GROUP_MAX_PASSWORD_SIZE);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the password.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set password (error %d).", err);
return;
}
}
}
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
int maxpeers = 0;
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
uint32_t maxpeers = tox_group_get_peer_limit(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve peer limit (error %d).", err);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit is set to %d", maxpeers);
return;
}
maxpeers = atoi(argv[1]);
if (maxpeers <= 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit must be a value greater than 0.");
return;
}
TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT err;
tox_group_founder_set_peer_limit(m, self->num, maxpeers, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer limit has been set to %d.", maxpeers);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the peer limit.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the peer limit (error %d).", err);
return;
}
}
}
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *pstate_str = NULL;
TOX_GROUP_PRIVACY_STATE privacy_state;
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
privacy_state = tox_group_get_privacy_state(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve privacy state (error %d).", err);
return;
}
pstate_str = privacy_state == TOX_GROUP_PRIVACY_STATE_PRIVATE ? "private" : "public";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state is set to %s.", pstate_str);
return;
}
pstate_str = argv[1];
if (strcasecmp(pstate_str, "private") != 0 && strcasecmp(pstate_str, "public") != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state must be \"private\" or \"public\".");
return;
}
privacy_state = strcasecmp(pstate_str,
"private") == 0 ? TOX_GROUP_PRIVACY_STATE_PRIVATE : TOX_GROUP_PRIVACY_STATE_PUBLIC;
TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE err;
tox_group_founder_set_privacy_state(m, self->num, privacy_state, &err);
switch (err) {
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Privacy state has been set to %s.", pstate_str);
return;
}
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the privacy state.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error setting privacy state (error %d).", err);
return;
}
}
}
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_OBSERVER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to silence %s.", nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to silence %s (error %d).", nick, err);
return;
}
}
}
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer name must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
if (tox_group_peer_get_role(m, self->num, peer_id, NULL) != TOX_GROUP_ROLE_OBSERVER) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick);
return;
}
TOX_ERR_GROUP_MOD_SET_ROLE err;
tox_group_mod_set_role(m, self->num, peer_id, TOX_GROUP_ROLE_USER, &err);
switch (err) {
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
return;
}
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unsilence %s.", nick);
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unsilence %s (error %d).", nick, err);
return;
}
}
}
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
TOX_ERR_GROUP_RECONNECT err;
if (!tox_group_reconnect(m, self->num, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to rejoin group (error %d).", err);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Reconnecting to group...");
}
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
TOX_ERR_GROUP_STATE_QUERIES err;
size_t tlen = tox_group_get_topic_size(m, self->num, &err);
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic length (error %d).", err);
return;
}
if (tlen > 0) {
char cur_topic[tlen];
if (!tox_group_get_topic(m, self->num, (uint8_t *) cur_topic, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic (error %d).", err);
return;
}
cur_topic[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is set to: %s", cur_topic);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic is not set.");
}
return;
}
const char *topic = argv[1];
TOX_ERR_GROUP_TOPIC_SET err;
tox_group_set_topic(m, self->num, (uint8_t *) topic, strlen(topic), &err);
switch (err) {
case TOX_ERR_GROUP_TOPIC_SET_OK: {
/* handled below switch */
break;
}
case TOX_ERR_GROUP_TOPIC_SET_TOO_LONG: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Topic length must not exceed %d.", TOX_GROUP_MAX_TOPIC_LENGTH);
return;
}
case TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic.");
return;
}
default: {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the topic (error %d).", err);
return;
}
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
TOX_ERR_GROUP_SELF_QUERY sn_err;
size_t sn_len = tox_group_self_get_name_size(m, self->num, &sn_err);
char selfnick[sn_len];
if (!tox_group_self_get_name(m, self->num, (uint8_t *) selfnick, &sn_err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve your own name (error %d).", sn_err);
return;
}
selfnick[sn_len] = '\0'; selfnick[sn_len] = '\0';
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, MAGENTA, "-!- You set the topic to: %s", topic); line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
char tmp_event[MAX_STR_SIZE]; char tmp_event[MAX_STR_SIZE];
snprintf(tmp_event, sizeof(tmp_event), "set topic to %s", topic); snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
write_to_log(tmp_event, selfnick, self->chatwin->log, true); write_to_log(tmp_event, selfnick, self->chatwin->log, true);
} }
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
TOX_ERR_GROUP_TOGGLE_IGNORE err;
if (!tox_group_toggle_ignore(m, self->num, peer_id, false, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unignore %s (error %d).", nick, err);
return;
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 1, BLUE, "-!- You are no longer ignoring %s", nick);
}
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
return;
}
GroupChat *chat = &groupchats[self->num];
if (!chat) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois failed.");
return;
}
const char *nick = argv[1];
uint32_t peer_id;
if (group_get_nick_peer_id(self->num, nick, &peer_id) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid peer name '%s'.", nick);
return;
}
int peer_index = get_peer_index(self->num, peer_id);
if (peer_index < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois failed.");
return;
}
const char *status_str = "Online";
if (chat->peer_list[peer_index].status == TOX_USER_STATUS_BUSY)
status_str = "Busy";
else if (chat->peer_list[peer_index].status == TOX_USER_STATUS_AWAY)
status_str = "Away";
const char *role_str = "User";
if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_FOUNDER)
role_str = "Founder";
else if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_MODERATOR)
role_str = "Moderator";
else if (chat->peer_list[peer_index].role == TOX_GROUP_ROLE_OBSERVER)
role_str = "Observer";
char last_seen_str[128];
get_elapsed_time_str_2(last_seen_str, sizeof(last_seen_str), get_unix_time() - chat->peer_list[peer_index].last_active);
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
size_t i;
for (i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
char d[3];
snprintf(d, sizeof(d), "%02X", chat->peer_list[peer_index].public_key[i] & 0xff);
strcat(pk_string, d);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Whois for %s", nick);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Role: %s", role_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Status: %s", status_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Last active: %s", last_seen_str);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Public key: %s", pk_string);
}

View File

@ -26,23 +26,6 @@
#include "windows.h" #include "windows.h"
#include "toxic.h" #include "toxic.h"
void cmd_ban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mykey(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prune(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unban(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* GROUP_COMMANDS_H */ #endif /* GROUP_COMMANDS_H */

File diff suppressed because it is too large Load Diff

View File

@ -31,37 +31,25 @@
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2 #define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
#define GROUP_EVENT_WAIT 3 #define GROUP_EVENT_WAIT 3
struct GroupPeer {
bool active;
char name[TOX_MAX_NAME_LENGTH];
size_t name_length;
uint32_t peer_id;
uint8_t public_key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
TOX_USER_STATUS status;
TOX_GROUP_ROLE role;
uint64_t last_active;
};
typedef struct { typedef struct {
struct GroupPeer *peer_list; int chatwin;
char *name_list; /* List of peer names, needed for tab completion */ bool active;
uint32_t num_peers; /* Number of peers in the chat/name_list array */ uint8_t type;
uint32_t max_idx; /* Maximum peer list index - 1 */ uint32_t num_peers;
uint32_t groupnumber; int side_pos; /* current position of the sidebar - used for scrolling up and down */
int chatwin; time_t start_time;
bool active; uint8_t *peer_names;
uint64_t time_connected; /* The time we successfully connected to the group */ uint8_t *oldpeer_names;
int side_pos; /* current position of the sidebar - used for scrolling up and down */ uint16_t *peer_name_lengths;
uint16_t *oldpeer_name_lengths;
} GroupChat; } GroupChat;
void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum); void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum);
int init_groupchat_win(Tox *m, uint32_t groupnum, const char *groupname, size_t length); int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type);
void set_nick_all_groups(Tox *m, const char *nick, size_t length);
void set_status_all_groups(Tox *m, uint8_t status);
int group_get_nick_peer_id(uint32_t groupnum, const char *nick, uint32_t *peer_id);
int get_peer_index(uint32_t groupnum, uint32_t peer_id);
/* destroys and re-creates groupchat window */ /* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self); void redraw_groupchat_win(ToxWindow *self);
ToxWindow new_group_chat(Tox *m, uint32_t groupnum);
#endif /* #define GROUPCHAT_H */ #endif /* #define GROUPCHAT_H */

View File

@ -27,7 +27,15 @@
#include "help.h" #include "help.h"
#include "misc_tools.h" #include "misc_tools.h"
#ifdef PYTHON
#include "api.h"
#endif /* PYTHON */
#ifdef PYTHON
#define HELP_MENU_HEIGHT 10
#else
#define HELP_MENU_HEIGHT 9 #define HELP_MENU_HEIGHT 9
#endif /* PYTHON */
#define HELP_MENU_WIDTH 26 #define HELP_MENU_WIDTH 26
void help_init_menu(ToxWindow *self) void help_init_menu(ToxWindow *self)
@ -95,6 +103,13 @@ static void help_draw_menu(ToxWindow *self)
wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "oup commands\n"); wprintw(win, "oup commands\n");
#ifdef PYTHON
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " p");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "lugin commands\n");
#endif /* PYTHON */
wattron(win, A_BOLD | COLOR_PAIR(BLUE)); wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " f"); wprintw(win, " f");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -112,7 +127,7 @@ static void help_draw_menu(ToxWindow *self)
wprintw(win, "it menu\n"); wprintw(win, "it menu\n");
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
static void help_draw_bottom_menu(WINDOW *win) static void help_draw_bottom_menu(WINDOW *win)
@ -153,11 +168,10 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n"); wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
wprintw(win, " /status <type> <msg> : Set status with optional note\n"); wprintw(win, " /status <type> <msg> : Set status with optional note\n");
wprintw(win, " /note <msg> : Set a personal note\n"); wprintw(win, " /note <msg> : Set a personal note\n");
wprintw(win, " /group : Create a group chat\n");
wprintw(win, " /join <chat id> <passwd> : Join a group chat with optional password\n");
wprintw(win, " /nick <nick> : Set your nickname\n"); wprintw(win, " /nick <nick> : Set your nickname\n");
wprintw(win, " /nospam <value> : Change part of your Tox ID to stop spam\n"); wprintw(win, " /nospam <value> : Change part of your Tox ID to stop spam\n");
wprintw(win, " /log <on> or <off> : Enable/disable logging\n"); wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
wprintw(win, " /myid : Print your Tox ID\n"); wprintw(win, " /myid : Print your Tox ID\n");
#ifdef QRPNG #ifdef QRPNG
wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n"); wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n");
@ -186,10 +200,18 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /svdev <type> <id> : Set active video device\n"); wprintw(win, " /svdev <type> <id> : Set active video device\n");
#endif /* VIDEO */ #endif /* VIDEO */
#ifdef PYTHON
wattron(win, A_BOLD);
wprintw(win, "\n Scripting:\n");
wattroff(win, A_BOLD);
wprintw(win, " /run <path> : Load and run the script at path\n");
#endif /* PYTHON */
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
static void help_draw_chat(ToxWindow *self) static void help_draw_chat(ToxWindow *self)
@ -202,7 +224,8 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, "Chat Commands:\n"); wprintw(win, "Chat Commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED)); wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /gaccept <password> : Accept a group invite with optional password\n"); wprintw(win, " /invite <n> : Invite contact to a group chat\n");
wprintw(win, " /join : Join a pending group chat\n");
wprintw(win, " /sendfile <path> : Send a file\n"); wprintw(win, " /sendfile <path> : Send a file\n");
wprintw(win, " /savefile <id> : Receive a file\n"); wprintw(win, " /savefile <id> : Receive a file\n");
wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n"); wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n");
@ -219,6 +242,7 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, " /sdev <type> <id> : Change active device\n"); wprintw(win, " /sdev <type> <id> : Change active device\n");
wprintw(win, " /mute <type> : Mute active device if in call\n"); wprintw(win, " /mute <type> : Mute active device if in call\n");
wprintw(win, " /sense <n> : VAD sensitivity threshold\n"); wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
wprintw(win, " /bitrate <n> : Set the audio encoding bitrate\n");
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef VIDEO #ifdef VIDEO
@ -231,7 +255,7 @@ static void help_draw_chat(ToxWindow *self)
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
static void help_draw_keys(ToxWindow *self) static void help_draw_keys(ToxWindow *self)
@ -257,7 +281,7 @@ static void help_draw_keys(ToxWindow *self)
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
static void help_draw_group(ToxWindow *self) static void help_draw_group(ToxWindow *self)
@ -270,39 +294,34 @@ static void help_draw_group(ToxWindow *self)
wprintw(win, "Group commands:\n"); wprintw(win, "Group commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED)); wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /chatid : Print the group chat id to share with others\n"); wprintw(win, " /title <msg> : Set group title (show current title if no msg)\n\n");
wprintw(win, " /mykey : Print your group public key\n");
wprintw(win, " /ignore <nick> : Ignore peer\n");
wprintw(win, " /unignore <nick> : Unignore peer \n");
wprintw(win, " /rejoin : Rejoin the group\n");
wprintw(win, " /topic <msg> : Set group topic (show current topic if no msg)\n");
wprintw(win, " /whisper <nick> <msg> : Send private message to nick\n");
wprintw(win, " /whois <nick> : Display info about nick.\n");
wattron(win, A_BOLD);
wprintw(win, " Moderator commands:\n");
wattroff(win, A_BOLD);
wprintw(win, " /kick <nick> : Kick peer\n");
wprintw(win, " /ban <nick> : Ban peer (leave nick blank to see ban list)\n");
wprintw(win, " /unban <Ban ID> : Unban entry\n");
wprintw(win, " /silence <nick> : Silences peer for the entire group\n");
wprintw(win, " /unsilence <nick> : Unsilences peer\n");
wattron(win, A_BOLD);
wprintw(win, " Founder commands:\n");
wattroff(win, A_BOLD);
wprintw(win, " /mod <nick> : Promote peer to moderator\n");
wprintw(win, " /unmod <nick> : Demote moderator to normal user\n");
wprintw(win, " /passwd <password> : Set group password (leave blank to unset)\n");
wprintw(win, " /peerlimit <num> : Set group peer limit\n");
wprintw(win, " /privacy <state> : Set group privacy state: private|public\n");
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
#ifdef PYTHON
static void help_draw_plugin(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Plugin commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
draw_handler_help(win);
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wnoutrefresh(win);
}
#endif
static void help_draw_contacts(ToxWindow *self) static void help_draw_contacts(ToxWindow *self)
{ {
WINDOW *win = self->help->win; WINDOW *win = self->help->win;
@ -322,11 +341,13 @@ static void help_draw_contacts(ToxWindow *self)
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
void help_onKey(ToxWindow *self, wint_t key) void help_onKey(ToxWindow *self, wint_t key)
{ {
int height;
switch (key) { switch (key) {
case 'x': case 'x':
case T_KEY_ESC: case T_KEY_ESC:
@ -335,9 +356,9 @@ void help_onKey(ToxWindow *self, wint_t key)
case 'c': case 'c':
#ifdef VIDEO #ifdef VIDEO
help_init_window(self, 21, 80); help_init_window(self, 23, 80);
#elif AUDIO #elif AUDIO
help_init_window(self, 18, 80); help_init_window(self, 20, 80);
#else #else
help_init_window(self, 10, 80); help_init_window(self, 10, 80);
#endif #endif
@ -345,21 +366,32 @@ void help_onKey(ToxWindow *self, wint_t key)
break; break;
case 'g': case 'g':
height = 22;
#ifdef VIDEO #ifdef VIDEO
help_init_window(self, 30, 80); height += 8;
#elif AUDIO #elif AUDIO
help_init_window(self, 26, 80); height += 4;
#else
help_init_window(self, 22, 80);
#endif #endif
#ifdef PYTHON
height += 2;
#endif
help_init_window(self, height, 80);
self->help->type = HELP_GLOBAL; self->help->type = HELP_GLOBAL;
break; break;
case 'r': case 'r':
help_init_window(self, 25, 80); help_init_window(self, 6, 80);
self->help->type = HELP_GROUP; self->help->type = HELP_GROUP;
break; break;
#ifdef PYTHON
case 'p':
help_init_window(self, 4 + num_registered_handlers(), help_max_width());
self->help->type = HELP_PLUGIN;
break;
#endif
case 'f': case 'f':
help_init_window(self, 10, 80); help_init_window(self, 10, 80);
self->help->type = HELP_CONTACTS; self->help->type = HELP_CONTACTS;
@ -379,8 +411,6 @@ void help_onKey(ToxWindow *self, wint_t key)
void help_onDraw(ToxWindow *self) void help_onDraw(ToxWindow *self)
{ {
curs_set(0);
switch (self->help->type) { switch (self->help->type) {
case HELP_MENU: case HELP_MENU:
help_draw_menu(self); help_draw_menu(self);
@ -405,5 +435,12 @@ void help_onDraw(ToxWindow *self)
case HELP_GROUP: case HELP_GROUP:
help_draw_group(self); help_draw_group(self);
break; break;
#ifdef PYTHON
case HELP_PLUGIN:
help_draw_plugin(self);
break;
#endif /* PYTHON */
} }
} }

View File

@ -33,6 +33,9 @@ typedef enum {
HELP_GROUP, HELP_GROUP,
HELP_KEYS, HELP_KEYS,
HELP_CONTACTS, HELP_CONTACTS,
#ifdef PYTHON
HELP_PLUGIN,
#endif
} HELP_TYPES; } HELP_TYPES;
void help_onDraw(ToxWindow *self); void help_onDraw(ToxWindow *self);

View File

@ -129,17 +129,21 @@ static struct line_info *line_info_ret_queue(struct history *hst)
return line; return line;
} }
/* creates new line_info line and puts it in the queue. */ /* creates new line_info line and puts it in the queue.
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, *
uint8_t bold, uint8_t colour, const char *msg, ...) * Returns the id of the new line.
* Returns -1 on failure.
*/
int 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, ...)
{ {
if (!self) if (!self)
return; return -1;
struct history *hst = self->chatwin->hst; struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
return; return -1;
struct line_info *new_line = calloc(1, sizeof(struct line_info)); struct line_info *new_line = calloc(1, sizeof(struct line_info));
@ -171,11 +175,6 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
len += strlen(user_settings->line_normal) + 3; len += strlen(user_settings->line_normal) + 3;
break; break;
case IN_PRVT_MSG:
case OUT_PRVT_MSG:
len += strlen(user_settings->line_special) + 3;
break;
case CONNECTION: case CONNECTION:
len += strlen(user_settings->line_join) + 2; len += strlen(user_settings->line_join) + 2;
break; break;
@ -227,6 +226,7 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
len += strlen(new_line->name2); len += strlen(new_line->name2);
} }
new_line->id = (hst->line_end->id + 1 + hst->queue_sz) % INT_MAX;
new_line->len = len; new_line->len = len;
new_line->type = type; new_line->type = type;
new_line->bold = bold; new_line->bold = bold;
@ -235,6 +235,8 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
new_line->timestamp = get_unix_time(); new_line->timestamp = get_unix_time();
hst->queue[hst->queue_sz++] = new_line; hst->queue[hst->queue_sz++] = new_line;
return new_line->id;
} }
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */ /* adds a single queue item to hst if possible. only called once per call to line_info_print() */
@ -249,10 +251,10 @@ static void line_info_check_queue(ToxWindow *self)
if (hst->start_id > user_settings->history_size) if (hst->start_id > user_settings->history_size)
line_info_root_fwd(hst); line_info_root_fwd(hst);
line->id = hst->line_end->id + 1;
line->prev = hst->line_end; line->prev = hst->line_end;
hst->line_end->next = line; hst->line_end->next = line;
hst->line_end = line; hst->line_end = line;
hst->line_end->id = line->id;
int y, y2, x, x2; int y, y2, x, x2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
@ -317,8 +319,6 @@ void line_info_print(ToxWindow *self)
/* fallthrough */ /* fallthrough */
case IN_MSG: case IN_MSG:
case IN_PRVT_MSG:
case OUT_PRVT_MSG:
wattron(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr); wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE)); wattroff(win, COLOR_PAIR(BLUE));
@ -331,10 +331,7 @@ void line_info_print(ToxWindow *self)
nameclr = CYAN; nameclr = CYAN;
wattron(win, COLOR_PAIR(nameclr)); wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s %s: ", (type != OUT_PRVT_MSG && type != IN_PRVT_MSG) ? wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
user_settings->line_normal :
user_settings->line_special,
line->name1);
wattroff(win, COLOR_PAIR(nameclr)); wattroff(win, COLOR_PAIR(nameclr));
char *msg = line->msg; char *msg = line->msg;

View File

@ -35,12 +35,10 @@ enum {
SYS_MSG, SYS_MSG,
IN_MSG, IN_MSG,
OUT_MSG, OUT_MSG,
OUT_MSG_READ, /* for sent messages that have received a read reply. */ OUT_MSG_READ, /* for sent messages that have received a read reply. don't set this with line_info_add */
IN_ACTION, IN_ACTION,
OUT_ACTION, OUT_ACTION,
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */ OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
IN_PRVT_MSG, /* PRVT should only be used for groups */
OUT_PRVT_MSG,
PROMPT, PROMPT,
CONNECTION, CONNECTION,
DISCONNECTION, DISCONNECTION,
@ -76,9 +74,13 @@ struct history {
int queue_sz; int queue_sz;
}; };
/* creates new line_info line and puts it in the queue. */ /* creates new line_info line and puts it in the queue.
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, *
uint8_t bold, uint8_t colour, const char *msg, ...); * Returns the id of the new line.
* Returns -1 on failure.
*/
int 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 section of history starting at line_start */ /* Prints a section of history starting at line_start */
void line_info_print(ToxWindow *self); void line_info_print(ToxWindow *self);

View File

@ -42,8 +42,12 @@ void cqueue_cleanup(struct chat_queue *q)
free(q); free(q);
} }
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id) void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id)
{ {
if (line_id < 0) {
return;
}
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg)); struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
if (new_m == NULL) if (new_m == NULL)

View File

@ -40,7 +40,7 @@ struct chat_queue {
}; };
void cqueue_cleanup(struct chat_queue *q); void cqueue_cleanup(struct chat_queue *q);
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id); void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id);
/* Tries to send the oldest unsent message in queue. */ /* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m); void cqueue_try_send(ToxWindow *self, Tox *m);

View File

@ -26,12 +26,11 @@
#include <time.h> #include <time.h>
#include <limits.h> #include <limits.h>
#include <dirent.h> #include <dirent.h>
#if defined(__FreeBSD__) #if SYSTEM == BSD
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h> #include <sys/socket.h>
#else #endif /* BSD! */
#include <arpa/inet.h> #include <arpa/inet.h>
#endif
#include <sys/stat.h> #include <sys/stat.h>
#include "toxic.h" #include "toxic.h"
@ -78,7 +77,7 @@ struct tm *get_time(void)
return timeinfo; return timeinfo;
} }
/* Puts the current time in buf in the format of [HH:mm:ss] */ /*Puts the current time in buf in the format of [HH:mm:ss] */
void get_time_str(char *buf, int bufsize) void get_time_str(char *buf, int bufsize)
{ {
if (user_settings->timestamps == TIMESTAMPS_OFF) { if (user_settings->timestamps == TIMESTAMPS_OFF) {
@ -108,24 +107,6 @@ void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
} }
/* Converts seconds to string in format H hours, m minutes, s seconds */
void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs)
{
if (!secs)
return;
long int seconds = secs % 60;
long int minutes = (secs % 3600) / 60;
long int hours = secs / 3600;
if (!minutes && !hours)
snprintf(buf, bufsize, "%ld seconds", seconds);
else if (!hours)
snprintf(buf, bufsize, "%ld minutes, %ld seconds", minutes, seconds);
else
snprintf(buf, bufsize, "%ld hours, %ld minutes, %ld seconds", hours, minutes, seconds);
}
/* /*
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output. * Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
* output_size must be exactly half of hex_len. * output_size must be exactly half of hex_len.
@ -341,36 +322,14 @@ void str_to_lower(char *str)
Returns nick len */ Returns nick len */
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum) size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
{ {
size_t len = tox_friend_get_name_size(m, friendnum, NULL); TOX_ERR_FRIEND_QUERY err;
size_t len = tox_friend_get_name_size(m, friendnum, &err);
if (len == 0) { if (err != TOX_ERR_FRIEND_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME); goto on_error;
len = strlen(UNKNOWN_NAME);
} else { } else {
tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL); if (!tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL)) {
} goto on_error;
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
}
/* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum)
{
TOX_ERR_GROUP_PEER_QUERY err;
size_t len = tox_group_peer_get_name_size(m, groupnum, peer_id, &err);
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
} else {
tox_group_peer_get_name(m, groupnum, peer_id, (uint8_t *) buf, &err);
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
} }
} }
@ -378,21 +337,56 @@ int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum)
buf[len] = '\0'; buf[len] = '\0';
filter_str(buf, len); filter_str(buf, len);
return len; return len;
on_error:
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
buf[len] = '\0';
return len;
} }
/* copies data to msg buffer. /* same as get_nick_truncate but for groupchats */
returns length of msg. int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum)
returns 0 and nulls msg if length is too big for buffer size */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
{ {
if (length > size - 1) { TOX_ERR_CONFERENCE_PEER_QUERY err;
msg[0] = '\0'; size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err);
return 0;
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
goto on_error;
} else {
if (!tox_conference_peer_get_name(m, groupnum, peernum, (uint8_t *) buf, NULL)) {
goto on_error;
}
} }
memcpy(msg, data, length); len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
msg[length] = '\0'; buf[len] = '\0';
return length; 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, 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 i;
size_t j = 0;
for (i = 0; (i < length) && (j < size - 1); ++i) {
if (data[i] != '\r') {
msg[j++] = data[i];
}
}
msg[j] = '\0';
return j;
} }
/* returns index of the first instance of ch in s starting at idx. /* returns index of the first instance of ch in s starting at idx.

View File

@ -69,9 +69,6 @@ void get_time_str(char *buf, int bufsize);
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
void get_elapsed_time_str(char *buf, int bufsize, time_t secs); void get_elapsed_time_str(char *buf, int bufsize, time_t secs);
/* Converts seconds to string in format H hours, m minutes, s seconds */
void get_elapsed_time_str_2(char *buf, int bufsize, uint64_t secs);
/* get the current local time (not thread safe) */ /* get the current local time (not thread safe) */
struct tm *get_time(void); struct tm *get_time(void);
@ -129,11 +126,10 @@ void str_to_lower(char *str);
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum); size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
/* same as get_nick_truncate but for groupchats */ /* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peer_id, int groupnum); int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum);
/* copies data to msg buffer. /* copies data to msg buffer.
returns length of msg. returns length of msg, which will be no larger than size-1 */
returns 0 and nulls msg if length is too big for buffer size */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length); size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length);
/* returns index of the first instance of ch in s starting at idx. /* returns index of the first instance of ch in s starting at idx.

View File

@ -21,6 +21,7 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h>
#include <string.h> #include <string.h>
#include <curl/curl.h> #include <curl/curl.h>

View File

@ -208,10 +208,10 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
// TODO possibly get a better pixel format // TODO possibly get a better pixel format
if (_shouldMangleDimensions) { if (_shouldMangleDimensions) {
[_linkerVideo setVideoSettings: @ { [_linkerVideo setVideoSettings: @ {
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey: @640, (id)kCVPixelBufferWidthKey: @640,
(id)kCVPixelBufferHeightKey: @480 (id)kCVPixelBufferHeightKey: @480
}]; }];
} else { } else {
[_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}]; [_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
} }

View File

@ -49,12 +49,18 @@ extern struct Winthread Winthread;
extern FriendsList Friends; extern FriendsList Friends;
FriendRequests FrndRequests; FriendRequests FrndRequests;
#ifdef VIDEO #if defined(PYTHON) && defined(VIDEO)
#define AC_NUM_GLOB_COMMANDS 23 #define AC_NUM_GLOB_COMMANDS 23
#elif AUDIO #elif defined(PYTHON) && defined(AUDIO)
#define AC_NUM_GLOB_COMMANDS 21 #define AC_NUM_GLOB_COMMANDS 21
#else #elif VIDEO
#define AC_NUM_GLOB_COMMANDS 22
#elif AUDIO
#define AC_NUM_GLOB_COMMANDS 20
#elif PYTHON
#define AC_NUM_GLOB_COMMANDS 19 #define AC_NUM_GLOB_COMMANDS 19
#else
#define AC_NUM_GLOB_COMMANDS 18
#endif #endif
/* Array of global command names used for tab completion. */ /* Array of global command names used for tab completion. */
@ -68,7 +74,6 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/exit" }, { "/exit" },
{ "/group" }, { "/group" },
{ "/help" }, { "/help" },
{ "/join" },
{ "/log" }, { "/log" },
{ "/myid" }, { "/myid" },
{ "/myqr" }, { "/myqr" },
@ -93,6 +98,12 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
#endif /* VIDEO */ #endif /* VIDEO */
#ifdef PYTHON
{ "/run" },
#endif /* PYTHON */
}; };
void kill_prompt_window(ToxWindow *self) void kill_prompt_window(ToxWindow *self)
@ -215,6 +226,13 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0)
diff = dir_match(self, m, ctx->line, L"/avatar"); diff = dir_match(self, m, ctx->line, L"/avatar");
#ifdef PYTHON
else if (wcsncmp(ctx->line, L"/run \"", wcslen(L"/run \"")) == 0)
diff = dir_match(self, m, ctx->line, L"/run");
#endif
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) { else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
const char status_cmd_list[3][8] = { const char status_cmd_list[3][8] = {
{"online"}, {"online"},
@ -377,7 +395,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
help_onDraw(self); help_onDraw(self);
} }
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status) static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum, TOX_CONNECTION connection_status)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;

347
src/python_api.c Normal file
View File

@ -0,0 +1,347 @@
/* 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"
#endif /* PYTHON */
#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;
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)
{
size_t i, ii;
FriendsList friends;
PyObject *cur, *ret;
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (!PyArg_ParseTuple(args, ""))
return NULL;
friends = api_get_friendslist();
ret = PyList_New(0);
for (i = 0; i < friends.num_friends; i++) {
for (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';
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 *groupchat_command_const = Py_BuildValue("i", GROUPCHAT_COMMAND_MODE);
PyObject_SetAttrString(m, "GLOBAL_COMMAND", global_command_const);
PyObject_SetAttrString(m, "CHAT_COMMAND", chat_command_const);
PyObject_SetAttrString(m, "GROUPCHAT_COMMAND", groupchat_command_const);
Py_DECREF(global_command_const);
Py_DECREF(chat_command_const);
Py_DECREF(groupchat_command_const);
return m;
}
void terminate_python(void)
{
struct python_registered_func *cur, *old;
if (python_commands.name != NULL)
free(python_commands.name);
for (cur = python_commands.next; cur != NULL;) {
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);
}
}

39
src/python_api.h Normal file
View File

@ -0,0 +1,39 @@
/* python_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 PYTHON_API_H
#define PYTHON_API_H
#ifdef PYTHON
#include <Python.h>
#endif /* PYTHON */
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);
#endif /* #define PYTHON_API_H */

View File

@ -23,6 +23,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <qrencode.h> #include <qrencode.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"

View File

@ -68,7 +68,6 @@ static struct ui_strings {
const char *line_quit; const char *line_quit;
const char *line_alert; const char *line_alert;
const char *line_normal; const char *line_normal;
const char *line_special;
const char *mplex_away; const char *mplex_away;
const char *mplex_away_note; const char *mplex_away_note;
@ -95,7 +94,6 @@ static struct ui_strings {
"line_quit", "line_quit",
"line_alert", "line_alert",
"line_normal", "line_normal",
"line_special",
"mplex_away", "mplex_away",
"mplex_away_note", "mplex_away_note",
}; };
@ -124,7 +122,6 @@ static void ui_defaults(struct user_settings *settings)
snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT); snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT);
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL); snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
snprintf(settings->line_special, LINE_HINT_MAX + 1, "%s", LINE_SPECIAL);
settings->mplex_away = MPLEX_ON; settings->mplex_away = MPLEX_ON;
snprintf (settings->mplex_away_note, snprintf (settings->mplex_away_note,
@ -182,12 +179,14 @@ static const struct tox_strings {
const char *download_path; const char *download_path;
const char *chatlogs_path; const char *chatlogs_path;
const char *avatar_path; const char *avatar_path;
const char *autorun_path;
const char *password_eval; const char *password_eval;
} tox_strings = { } tox_strings = {
"tox", "tox",
"download_path", "download_path",
"chatlogs_path", "chatlogs_path",
"avatar_path", "avatar_path",
"autorun_path",
"password_eval", "password_eval",
}; };
@ -196,6 +195,7 @@ static void tox_defaults(struct user_settings *settings)
strcpy(settings->download_path, ""); strcpy(settings->download_path, "");
strcpy(settings->chatlogs_path, ""); strcpy(settings->chatlogs_path, "");
strcpy(settings->avatar_path, ""); strcpy(settings->avatar_path, "");
strcpy(settings->autorun_path, "");
strcpy(settings->password_eval, ""); strcpy(settings->password_eval, "");
} }
@ -383,10 +383,6 @@ int settings_load(struct user_settings *s, const char *patharg)
snprintf(s->line_normal, sizeof(s->line_normal), "%s", str); snprintf(s->line_normal, sizeof(s->line_normal), "%s", str);
} }
if ( config_setting_lookup_string(setting, ui_strings.line_special, &str) ) {
snprintf(s->line_special, sizeof(s->line_special), "%s", str);
}
config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away); config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away);
if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) { if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) {
@ -425,6 +421,20 @@ int settings_load(struct user_settings *s, const char *patharg)
s->avatar_path[0] = '\0'; s->avatar_path[0] = '\0';
} }
#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) ) { if ( config_setting_lookup_string(setting, tox_strings.password_eval, &str) ) {
snprintf(s->password_eval, sizeof(s->password_eval), "%s", str); snprintf(s->password_eval, sizeof(s->password_eval), "%s", str);
int len = strlen(str); int len = strlen(str);

View File

@ -59,11 +59,11 @@ struct user_settings {
char line_quit[LINE_HINT_MAX + 1]; char line_quit[LINE_HINT_MAX + 1];
char line_alert[LINE_HINT_MAX + 1]; char line_alert[LINE_HINT_MAX + 1];
char line_normal[LINE_HINT_MAX + 1]; char line_normal[LINE_HINT_MAX + 1];
char line_special[LINE_HINT_MAX + 1];
char download_path[PATH_MAX]; char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX]; char chatlogs_path[PATH_MAX];
char avatar_path[PATH_MAX]; char avatar_path[PATH_MAX];
char autorun_path[PATH_MAX];
char password_eval[PASSWORD_EVAL_MAX]; char password_eval[PASSWORD_EVAL_MAX];
int key_next_tab; int key_next_tab;
@ -120,7 +120,6 @@ enum {
#define LINE_QUIT "<--" #define LINE_QUIT "<--"
#define LINE_ALERT "-!-" #define LINE_ALERT "-!-"
#define LINE_NORMAL "---" #define LINE_NORMAL "---"
#define LINE_SPECIAL ">>>"
#define TIMESTAMP_DEFAULT "%H:%M:%S" #define TIMESTAMP_DEFAULT "%H:%M:%S"
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]" #define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
#define MPLEX_AWAY_NOTE "Detached from screen" #define MPLEX_AWAY_NOTE "Detached from screen"

View File

@ -214,8 +214,8 @@ static int detect_tmux ()
if (!pos) if (!pos)
return 0; return 0;
/* store the session number string for later use */ /* store the session id for later use */
snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1); snprintf (mplex_data, sizeof(mplex_data), "$%s", pos + 1);
mplex = MPLEX_TMUX; mplex = MPLEX_TMUX;
return 1; return 1;
} }
@ -252,14 +252,8 @@ static int gnu_screen_is_detached ()
} }
/* Detects tmux attached/detached by getting session data and finding the /* Detects tmux attached/detached by getting session data and finding the
current session's entry. An attached entry ends with "(attached)". Example: current session's entry.
*/
$ 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 () static int tmux_is_detached ()
{ {
if (mplex != MPLEX_TMUX) if (mplex != MPLEX_TMUX)
@ -267,10 +261,12 @@ static int tmux_is_detached ()
FILE *session_info_stream = NULL; FILE *session_info_stream = NULL;
char *dyn_buffer = NULL, *search_str = NULL; char *dyn_buffer = NULL, *search_str = NULL;
char *entry_pos, *nl_pos, *attached_pos; char *entry_pos;
int detached;
const int numstr_len = strlen (mplex_data); const int numstr_len = strlen (mplex_data);
session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r"); /* 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; goto fail;
@ -284,13 +280,12 @@ static int tmux_is_detached ()
session_info_stream = NULL; session_info_stream = NULL;
/* prepare search string, for finding the current session's entry */ /* prepare search string, for finding the current session's entry */
search_str = (char *) malloc (numstr_len + 4); search_str = (char *) malloc (numstr_len + 2);
search_str[0] = '\n'; search_str[0] = '\n';
strcpy (search_str + 1, mplex_data); strcpy (search_str + 1, mplex_data);
strcat (search_str, ": ");
/* do the search */ /* 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; entry_pos = dyn_buffer;
else else
entry_pos = strstr (dyn_buffer, search_str); entry_pos = strstr (dyn_buffer, search_str);
@ -298,9 +293,8 @@ static int tmux_is_detached ()
if (! entry_pos) if (! entry_pos)
goto fail; goto fail;
/* find the next \n and look for the "(attached)" before it */ entry_pos = strchr (entry_pos, ' ') + 1;
nl_pos = strchr (entry_pos + 1, '\n'); detached = strncmp (entry_pos, "0\n", 2) == 0;
attached_pos = strstr (entry_pos + 1, "(attached)\n");
free (search_str); free (search_str);
search_str = NULL; search_str = NULL;
@ -308,7 +302,7 @@ static int tmux_is_detached ()
free (dyn_buffer); free (dyn_buffer);
dyn_buffer = NULL; dyn_buffer = NULL;
return attached_pos == NULL || attached_pos > nl_pos; return detached;
fail: fail:

View File

@ -26,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdarg.h>
#include <signal.h> #include <signal.h>
#include <locale.h> #include <locale.h>
#include <string.h> #include <string.h>
@ -49,7 +50,6 @@
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"
#include "friendlist.h" #include "friendlist.h"
#include "groupchat.h"
#include "prompt.h" #include "prompt.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "file_transfers.h" #include "file_transfers.h"
@ -76,6 +76,11 @@
ToxAV *av; ToxAV *av;
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef PYTHON
#include "api.h"
#include "python_api.h"
#endif
#ifndef PACKAGE_DATADIR #ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "." #define PACKAGE_DATADIR "."
#endif #endif
@ -105,9 +110,17 @@ static struct user_password {
int len; int len;
} user_password; } user_password;
static time_t last_signal_time;
static void catch_SIGINT(int sig) static void catch_SIGINT(int sig)
{ {
Winthread.sig_exit_toxic = 1; time_t cur_time = get_unix_time();
if (difftime(cur_time, last_signal_time) <= 1) {
Winthread.sig_exit_toxic = 1;
} else {
last_signal_time = cur_time;
}
} }
static void catch_SIGSEGV(int sig) static void catch_SIGSEGV(int sig)
@ -163,6 +176,10 @@ void exit_toxic_success(Tox *m)
terminate_audio(); terminate_audio();
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef PYTHON
terminate_python();
#endif /* PYTHON */
free_global_data(); free_global_data();
tox_kill(m); tox_kill(m);
endwin(); endwin();
@ -283,17 +300,6 @@ static void print_init_messages(ToxWindow *toxwin)
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]); line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
} }
static void load_groups(Tox *m)
{
size_t i;
size_t numgroups = tox_group_get_number_groups(m);
for (i = 0; i < numgroups; ++i) {
if (init_groupchat_win(m, i, NULL, 0) == -1)
tox_group_leave(m, i, NULL, 0, NULL);
}
}
static void load_friendlist(Tox *m) static void load_friendlist(Tox *m)
{ {
size_t i; size_t i;
@ -394,6 +400,7 @@ static void first_time_encrypt(const char *msg)
do { do {
system("clear"); system("clear");
printf("%s ", msg); printf("%s ", msg);
fflush(stdout);
if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n") if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n")
|| !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n")) || !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n"))
@ -414,6 +421,7 @@ static void first_time_encrypt(const char *msg)
printf("Enter a new password (must be at least %d characters) ", MIN_PASSWORD_LEN); printf("Enter a new password (must be at least %d characters) ", MIN_PASSWORD_LEN);
while (valid_password == false) { while (valid_password == false) {
fflush(stdout); // Flush all before user input
len = password_prompt(user_password.pass, sizeof(user_password.pass)); len = password_prompt(user_password.pass, sizeof(user_password.pass));
user_password.len = len; user_password.len = len;
@ -532,33 +540,23 @@ int store_data(Tox *m, const char *path)
static void init_tox_callbacks(Tox *m) static void init_tox_callbacks(Tox *m)
{ {
tox_callback_self_connection_status(m, prompt_onSelfConnectionChange, NULL); tox_callback_self_connection_status(m, prompt_onSelfConnectionChange);
tox_callback_friend_connection_status(m, on_connectionchange, NULL); tox_callback_friend_connection_status(m, on_connectionchange);
tox_callback_friend_typing(m, on_typing_change, NULL); tox_callback_friend_typing(m, on_typing_change);
tox_callback_friend_request(m, on_request, NULL); tox_callback_friend_request(m, on_request);
tox_callback_friend_message(m, on_message, NULL); tox_callback_friend_message(m, on_message);
tox_callback_friend_name(m, on_nickchange, NULL); tox_callback_friend_name(m, on_nickchange);
tox_callback_friend_status(m, on_statuschange, NULL); tox_callback_friend_status(m, on_statuschange);
tox_callback_friend_status_message(m, on_statusmessagechange, NULL); tox_callback_friend_status_message(m, on_statusmessagechange);
tox_callback_friend_read_receipt(m, on_read_receipt, NULL); tox_callback_friend_read_receipt(m, on_read_receipt);
tox_callback_file_recv(m, on_file_recv, NULL); tox_callback_conference_invite(m, on_groupinvite);
tox_callback_file_chunk_request(m, on_file_chunk_request, NULL); tox_callback_conference_message(m, on_groupmessage);
tox_callback_file_recv_control(m, on_file_control, NULL); tox_callback_conference_namelist_change(m, on_group_namelistchange);
tox_callback_file_recv_chunk(m, on_file_recv_chunk, NULL); tox_callback_conference_title(m, on_group_titlechange);
tox_callback_group_invite(m, on_group_invite, NULL); tox_callback_file_recv(m, on_file_recv);
tox_callback_group_message(m, on_group_message, NULL); tox_callback_file_chunk_request(m, on_file_chunk_request);
tox_callback_group_private_message(m, on_group_private_message, NULL); tox_callback_file_recv_control(m, on_file_control);
tox_callback_group_peer_join(m, on_group_peer_join, NULL); tox_callback_file_recv_chunk(m, on_file_recv_chunk);
tox_callback_group_peer_exit(m, on_group_peer_exit, NULL);
tox_callback_group_peer_name(m, on_group_nick_change, NULL);
tox_callback_group_peer_status(m, on_group_status_change, NULL);
tox_callback_group_topic(m, on_group_topic_change, NULL);
tox_callback_group_peer_limit(m, on_group_peer_limit, NULL);
tox_callback_group_privacy_state(m, on_group_privacy_state, NULL);
tox_callback_group_password(m, on_group_password, NULL);
tox_callback_group_self_join(m, on_group_self_join, NULL);
tox_callback_group_join_fail(m, on_group_rejected, NULL);
tox_callback_group_moderation(m, on_group_moderation, NULL);
} }
static void init_tox_options(struct Tox_Options *tox_opts) static void init_tox_options(struct Tox_Options *tox_opts)
@ -581,7 +579,7 @@ static void init_tox_options(struct Tox_Options *tox_opts)
tox_opts->proxy_host = arg_opts.proxy_address; tox_opts->proxy_host = arg_opts.proxy_address;
const char *ps = tox_opts->proxy_type == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP"; const char *ps = tox_opts->proxy_type == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP";
char tmp[48]; char tmp[sizeof(arg_opts.proxy_address) + MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "Using %s proxy %s : %d", ps, arg_opts.proxy_address, arg_opts.proxy_port); snprintf(tmp, sizeof(tmp), "Using %s proxy %s : %d", ps, arg_opts.proxy_address, arg_opts.proxy_port);
queue_init_message("%s", tmp); queue_init_message("%s", tmp);
} }
@ -649,6 +647,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
char plain[plain_len]; char plain[plain_len];
while (true) { while (true) {
fflush(stdout); // Flush before prompts so the user sees the question/message
if (pweval) { if (pweval) {
pwlen = password_eval(user_password.pass, sizeof(user_password.pass)); pwlen = password_eval(user_password.pass, sizeof(user_password.pass));
} else { } else {
@ -768,7 +768,7 @@ static void do_toxic(Tox *m)
return; return;
} }
tox_iterate(m); tox_iterate(m, NULL);
do_tox_connection(m); do_tox_connection(m);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
} }
@ -857,6 +857,13 @@ static void print_usage(void)
fprintf(stderr, " -t, --force-tcp Force toxic to use a TCP connection (use with proxies)\n"); fprintf(stderr, " -t, --force-tcp Force toxic to use a TCP connection (use with proxies)\n");
fprintf(stderr, " -T, --tcp-server Act as a TCP relay server: Requires [port]\n"); fprintf(stderr, " -T, --tcp-server Act as a TCP relay server: Requires [port]\n");
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n"); fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
fprintf(stderr, " -v, --version Print the version\n");
}
static void print_version(void)
{
fprintf(stderr, "Toxic version %s\n", TOXICVER);
fprintf(stderr, "Toxcore version %d.%d.%d\n", tox_version_major(), tox_version_minor(), tox_version_patch());
} }
static void set_default_opts(void) static void set_default_opts(void)
@ -887,10 +894,11 @@ static void parse_args(int argc, char *argv[])
{"SOCKS5-proxy", required_argument, 0, 'p'}, {"SOCKS5-proxy", required_argument, 0, 'p'},
{"HTTP-proxy", required_argument, 0, 'P'}, {"HTTP-proxy", required_argument, 0, 'P'},
{"unencrypt-data", no_argument, 0, 'u'}, {"unencrypt-data", no_argument, 0, 'u'},
{"version", no_argument, 0, 'v'},
{NULL, no_argument, NULL, 0}, {NULL, no_argument, NULL, 0},
}; };
const char *opts_str = "4bdehotuxc:f:n:r:p:P:T:"; const char *opts_str = "4bdehotuxvc:f:n:r:p:P:T:";
int opt, indexptr; int opt, indexptr;
long int port = 0; long int port = 0;
@ -1013,6 +1021,10 @@ static void parse_args(int argc, char *argv[])
arg_opts.unencrypt_data = 1; arg_opts.unencrypt_data = 1;
break; break;
case 'v':
print_version();
exit(EXIT_SUCCESS);
case 'h': case 'h':
default: default:
print_usage(); print_usage();
@ -1219,6 +1231,13 @@ int main(int argc, char **argv)
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef PYTHON
init_python(m);
invoke_autoruns(prompt->chatwin->history, prompt);
#endif /* PYTHON */
init_notify(60, 3000); init_notify(60, 3000);
/* screen/tmux auto-away timer */ /* screen/tmux auto-away timer */
@ -1232,14 +1251,13 @@ int main(int argc, char **argv)
} }
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
load_groups(m);
print_init_messages(prompt); print_init_messages(prompt);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
cleanup_init_messages(); cleanup_init_messages();
/* set user avatar from config file. if no path is supplied tox_unset_avatar is called */ /* set user avatar from config file. if no path is supplied tox_unset_avatar is called */
char avatarstr[MAX_STR_SIZE]; char avatarstr[PATH_MAX + 11];
snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path); snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path);
execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE);

View File

@ -114,11 +114,13 @@ void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t
void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata); void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata);
void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_friendadded(Tox *m, uint32_t friendnumber, bool sort); void on_friendadded(Tox *m, uint32_t friendnumber, bool sort);
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
void *userdata); const uint8_t *message, size_t length, void *userdata);
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length, void on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key,
void *userdata); size_t length, void *userdata);
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, void on_group_namelistchange(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_CONFERENCE_STATE_CHANGE change,
void *userdata);
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *userdata); void *userdata);
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length,
void *userdata); void *userdata);
@ -129,25 +131,5 @@ void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t k
const uint8_t *filename, size_t filename_length, void *userdata); const uint8_t *filename, size_t filename_length, void *userdata);
void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata); void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata);
void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata); void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length, void *userdata);
void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *userdata);
void on_group_private_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *message, size_t length,
void *userdata);
void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peernumber, void *userdata);
void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *partmsg, size_t length,
void *userdata);
void on_group_topic_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *topic, size_t length,
void *userdata);
void on_group_peer_limit(Tox *m, uint32_t groupnumber, uint32_t peer_limit, void *userdata);
void on_group_privacy_state(Tox *m, uint32_t groupnumber, TOX_GROUP_PRIVACY_STATE privacy_state, void *userdata);
void on_group_password(Tox *m, uint32_t groupnumber, const uint8_t *password, size_t length, void *userdata);
void on_group_nick_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *newname, size_t length,
void *userdata);
void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_USER_STATUS status, void *userdata);
void on_group_self_join(Tox *m, uint32_t groupnumber, void *userdata);
void on_group_rejected(Tox *m, uint32_t groupnumber, TOX_GROUP_JOIN_FAIL type, void *userdata);
void on_group_moderation(Tox *m, uint32_t groupnumber, uint32_t source_peernum, uint32_t target_peernum,
TOX_GROUP_MOD_EVENT type, void *userdata);
#endif /* #define TOXIC_H */ #endif /* #define TOXIC_H */

View File

@ -30,14 +30,18 @@
#include <vpx/vpx_image.h> #include <vpx/vpx_image.h>
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__OSX__)
#import "osx_video.h"
#else
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <fcntl.h> #include <fcntl.h>
#if defined(__linux__)
#include <linux/videodev2.h> #include <linux/videodev2.h>
#else /* __OSX__ */ #else
#import "osx_video.h" #include <sys/videoio.h>
#endif
#endif #endif
#include "line_info.h" #include "line_info.h"
@ -65,7 +69,7 @@ typedef struct VideoDevice {
void *cb_data; /* Data to be passed to callback */ void *cb_data; /* Data to be passed to callback */
int32_t friend_number; /* ToxAV friend number */ int32_t friend_number; /* ToxAV friend number */
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || SYSTEM == BSD
int fd; /* File descriptor of video device selected/opened */ int fd; /* File descriptor of video device selected/opened */
struct v4l2_format fmt; struct v4l2_format fmt;
struct VideoBuffer *buffers; struct VideoBuffer *buffers;
@ -132,7 +136,7 @@ static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
} }
} }
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || SYSTEM == BSD
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
uint8_t *input, uint16_t width, uint16_t height) uint8_t *input, uint16_t width, uint16_t height)
{ {
@ -170,7 +174,7 @@ static int xioctl(int fh, unsigned long request, void *arg)
return r; return r;
} }
#endif /* __linux__ */ #endif
/* Meet devices */ /* Meet devices */
#ifdef VIDEO #ifdef VIDEO
@ -181,7 +185,10 @@ VideoDeviceError init_video_devices()
{ {
size[vdt_input] = 0; size[vdt_input] = 0;
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__OSX__)
if( osx_video_init((char**)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
return vde_InternalError;
#else /* not __OSX__*/
for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) { for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
int fd; int fd;
@ -216,11 +223,6 @@ VideoDeviceError init_video_devices()
} }
} }
#else /* __OSX__ */
if ( osx_video_init((char **)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
return vde_InternalError;
#endif #endif
size[vdt_output] = 1; size[vdt_output] = 1;
@ -271,17 +273,17 @@ VideoDeviceError terminate_video_devices()
VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx, VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx,
VideoDataHandleCallback callback, void *data) VideoDataHandleCallback callback, void *data)
{ {
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__OSX__)
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] )
return vde_InvalidSelection;
#else /* not __OSX__ */
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx]
|| !video_devices_running[vdt_input][device_idx]->fd ) || !video_devices_running[vdt_input][device_idx]->fd )
return vde_InvalidSelection; return vde_InvalidSelection;
#else /* __OSX__ */
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] )
return vde_InvalidSelection;
#endif #endif
lock; lock;
@ -355,10 +357,16 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
if ( type == vdt_input ) { if ( type == vdt_input ) {
video_thread_paused = true; video_thread_paused = true;
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__OSX__)
if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
free(device);
unlock;
return vde_FailedStart;
}
#else /* not __OSX__*/
/* Open selected device */ /* Open selected device */
char device_address[] = "/dev/videoXX"; char device_address[] = "/dev/videoXX";
snprintf(device_address + 10 , sizeof(device_address) - 10, "%i", selection); snprintf(device_address + 10, sizeof(device_address) - 10, "%i", selection);
device->fd = open(device_address, O_RDWR); device->fd = open(device_address, O_RDWR);
@ -482,14 +490,6 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
return vde_FailedStart; return vde_FailedStart;
} }
#else /* __OSX__ */
if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
free(device);
unlock;
return vde_FailedStart;
}
#endif #endif
/* Create X11 window associated to device */ /* Create X11 window associated to device */
@ -666,7 +666,12 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
uint8_t *u = device->input.planes[1]; uint8_t *u = device->input.planes[1];
uint8_t *v = device->input.planes[2]; uint8_t *v = device->input.planes[2];
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__OSX__)
if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
unlock;
continue;
}
#else /* not __OSX__*/
struct v4l2_buffer buf; struct v4l2_buffer buf;
memset(&(buf), 0, sizeof(buf)); memset(&(buf), 0, sizeof(buf));
@ -683,13 +688,6 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
/* Convert frame image data to YUV420 for ToxAV */ /* Convert frame image data to YUV420 for ToxAV */
yuv422to420(y, u, v, data, video_width, video_height); yuv422to420(y, u, v, data, video_width, video_height);
#else /* __OSX__*/
if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
unlock;
continue;
}
#endif #endif
/* Send frame data to friend through ToxAV */ /* Send frame data to friend through ToxAV */
@ -726,14 +724,14 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
XFlush(device->x_display); XFlush(device->x_display);
free(img_data); free(img_data);
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || SYSTEM == BSD
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) { if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
unlock; unlock;
continue; continue;
} }
#endif /* __linux__ */ #endif /* __linux__ / BSD */
} }
@ -765,7 +763,10 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
if ( !device->ref_count ) { if ( !device->ref_count ) {
if ( type == vdt_input ) { if ( type == vdt_input ) {
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__OSX__)
osx_video_close_device(device_idx);
#else /* not __OSX__ */
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {} if ( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {}
@ -779,8 +780,6 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
close(device->fd); close(device->fd);
#else /* __OSX__ */
osx_video_close_device(device_idx);
#endif #endif
vpx_img_free(&device->input); vpx_img_free(&device->input);
XDestroyWindow(device->x_display, device->x_window); XDestroyWindow(device->x_display, device->x_window);
@ -788,9 +787,9 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
XCloseDisplay(device->x_display); XCloseDisplay(device->x_display);
pthread_mutex_destroy(device->mutex); pthread_mutex_destroy(device->mutex);
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || SYSTEM == BSD
free(device->buffers); free(device->buffers);
#endif /* __linux__ */ #endif /* __linux__ / BSD */
free(device); free(device);
} else { } else {

View File

@ -151,18 +151,8 @@ void on_friendadded(Tox *m, uint32_t friendnumber, bool sort)
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
} }
void on_group_invite(Tox *m, uint32_t friendnumber, const uint8_t *invite_data, size_t length, void *userdata) void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
{ const uint8_t *message, size_t length, void *userdata)
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupInvite != NULL)
windows[i].onGroupInvite(&windows[i], m, friendnumber, (char *) invite_data, length);
}
}
void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *userdata)
{ {
char msg[MAX_STR_SIZE + 1]; char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length); length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
@ -171,151 +161,43 @@ void on_group_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_MESSAG
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupMessage != NULL) if (windows[i].onGroupMessage != NULL)
windows[i].onGroupMessage(&windows[i], m, groupnumber, peer_id, type, msg, length); windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, type, msg, length);
} }
} }
void on_group_private_message(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *message, void on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key,
size_t length, void *userdata) size_t length, void *userdata)
{
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPrivateMessage != NULL)
windows[i].onGroupPrivateMessage(&windows[i], m, groupnumber, peer_id, msg, length);
}
}
void on_group_status_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, TOX_USER_STATUS status, void *userdata)
{ {
size_t i; size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupStatusChange != NULL) if (windows[i].onGroupInvite != NULL)
windows[i].onGroupStatusChange(&windows[i], m, groupnumber, peer_id, status); windows[i].onGroupInvite(&windows[i], m, friendnumber, type, (char *) group_pub_key, length);
} }
} }
void on_group_peer_join(Tox *m, uint32_t groupnumber, uint32_t peer_id, void *userdata) void on_group_namelistchange(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_CONFERENCE_STATE_CHANGE change,
void *userdata)
{ {
size_t i; size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPeerJoin != NULL) if (windows[i].onGroupNamelistChange != NULL)
windows[i].onGroupPeerJoin(&windows[i], m, groupnumber, peer_id); windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
} }
} }
void on_group_peer_exit(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *partmsg, size_t length, void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *userdata)
{
char msg[MAX_STR_SIZE + 1];
if (length == 0 || !partmsg) {
strcpy(msg, "Quit");
length = strlen(msg);
} else {
length = copy_tox_str(msg, sizeof(msg), (const char *) partmsg, length);
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPeerExit != NULL)
windows[i].onGroupPeerExit(&windows[i], m, groupnumber, peer_id, msg, length);
}
}
void on_group_topic_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *topic, size_t length,
void *userdata)
{
char data[MAX_STR_SIZE + 1];
length = copy_tox_str(data, sizeof(data), (const char *) topic, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupTopicChange != NULL)
windows[i].onGroupTopicChange(&windows[i], m, groupnumber, peer_id, data, length);
}
}
void on_group_peer_limit(Tox *m, uint32_t groupnumber, uint32_t peer_limit, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPeerLimit != NULL)
windows[i].onGroupPeerLimit(&windows[i], m, groupnumber, peer_limit);
}
}
void on_group_privacy_state(Tox *m, uint32_t groupnumber, TOX_GROUP_PRIVACY_STATE privacy_state, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPrivacyState != NULL)
windows[i].onGroupPrivacyState(&windows[i], m, groupnumber, privacy_state);
}
}
void on_group_password(Tox *m, uint32_t groupnumber, const uint8_t *password, size_t length, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPassword != NULL)
windows[i].onGroupPassword(&windows[i], m, groupnumber, (char *) password, length);
}
}
void on_group_nick_change(Tox *m, uint32_t groupnumber, uint32_t peer_id, const uint8_t *newname, size_t length,
void *userdata) void *userdata)
{ {
char name[TOXIC_MAX_NAME_LENGTH + 1]; char data[MAX_STR_SIZE + 1];
length = copy_tox_str(name, sizeof(name), (const char *) newname, length); length = copy_tox_str(data, sizeof(data), (const char *) title, length);
filter_str(name, length);
size_t i; size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupNickChange != NULL) if (windows[i].onGroupTitleChange != NULL)
windows[i].onGroupNickChange(&windows[i], m, groupnumber, peer_id, name, length); windows[i].onGroupTitleChange(&windows[i], m, groupnumber, peernumber, data, length);
}
}
void on_group_self_join(Tox *m, uint32_t groupnumber, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupSelfJoin != NULL)
windows[i].onGroupSelfJoin(&windows[i], m, groupnumber);
}
}
void on_group_rejected(Tox *m, uint32_t groupnumber, TOX_GROUP_JOIN_FAIL type, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupRejected != NULL)
windows[i].onGroupRejected(&windows[i], m, groupnumber, type);
}
}
void on_group_moderation(Tox *m, uint32_t groupnumber, uint32_t source_peer_id, uint32_t target_peer_id,
TOX_GROUP_MOD_EVENT type, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupModeration != NULL)
windows[i].onGroupModeration(&windows[i], m, groupnumber, source_peer_id, target_peer_id, type);
} }
} }
@ -593,10 +475,6 @@ static void draw_bar(void)
move(LINES - 1, 0); move(LINES - 1, 0);
attron(COLOR_PAIR(BLUE) | A_BOLD);
printw(" TOXIC " TOXICVER " |");
attroff(COLOR_PAIR(BLUE) | A_BOLD);
size_t i; size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
@ -632,8 +510,9 @@ static void draw_bar(void)
void draw_active_window(Tox *m) void draw_active_window(Tox *m)
{ {
pthread_mutex_lock(&Winthread.lock);
ToxWindow *a = active_window; ToxWindow *a = active_window;
pthread_mutex_lock(&Winthread.lock);
a->alert = WINDOW_ALERT_NONE; a->alert = WINDOW_ALERT_NONE;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
@ -705,6 +584,12 @@ ToxWindow *get_window_ptr(int i)
return toxwin; return toxwin;
} }
/* returns a pointer to the currently open ToxWindow. */
ToxWindow *get_active_window(void)
{
return active_window;
}
void force_refresh(WINDOW *w) void force_refresh(WINDOW *w)
{ {
wclear(w); wclear(w);

View File

@ -121,6 +121,10 @@ struct ToxWindow {
void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t); void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS); void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS);
void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t); void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
void(*onGroupNamelistChange)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_CONFERENCE_STATE_CHANGE);
void(*onGroupTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t); void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL); void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
@ -128,21 +132,6 @@ struct ToxWindow {
void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool); void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool);
void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t); void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t);
void(*onGroupInvite)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t);
void(*onGroupPrivateMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupPeerJoin)(ToxWindow *, Tox *, uint32_t, uint32_t);
void(*onGroupPeerExit)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupNickChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupStatusChange)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_USER_STATUS);
void(*onGroupTopicChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupPeerLimit)(ToxWindow *, Tox *, uint32_t, uint32_t);
void(*onGroupPrivacyState)(ToxWindow *, Tox *, uint32_t, TOX_GROUP_PRIVACY_STATE);
void(*onGroupPassword)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onGroupSelfJoin)(ToxWindow *, Tox *, uint32_t);
void(*onGroupRejected)(ToxWindow *, Tox *, uint32_t, TOX_GROUP_JOIN_FAIL);
void(*onGroupModeration)(ToxWindow *, Tox *, uint32_t, uint32_t, uint32_t, TOX_GROUP_MOD_EVENT);
#ifdef AUDIO #ifdef AUDIO
void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int); void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int);
@ -270,6 +259,7 @@ void kill_all_windows(Tox *m); /* should only be called on shutdown */
void on_window_resize(void); void on_window_resize(void);
void force_refresh(WINDOW *w); void force_refresh(WINDOW *w);
ToxWindow *get_window_ptr(int i); ToxWindow *get_window_ptr(int i);
ToxWindow *get_active_window(void);
/* refresh inactive windows to prevent scrolling bugs. /* refresh inactive windows to prevent scrolling bugs.
call at least once per second */ call at least once per second */