Browse Source

Merge commit 'c643f1ff' into Release_2.1

PYADPT-110 2.1.1
heck 4 years ago
parent
commit
c9864de24e
  1. 30
      .editorconfig
  2. 50
      .gitignore
  3. 28
      .hgignore
  4. 9
      AUTHORS.txt
  5. 91
      Makefile
  6. 12
      Makefile.conf
  7. 42
      README.md
  8. 22
      README.rst
  9. 19
      docs/Makefile
  10. 35
      docs/make.bat
  11. 12
      docs/source/api/pEp.rst
  12. 206
      docs/source/conf.py
  13. 21
      docs/source/index.rst
  14. 143
      docs/source/install.rst
  15. 7
      docs/source/software_using.rst
  16. 18
      pyproject.toml
  17. 28
      setup.cfg
  18. 57
      setup.py
  19. 49
      src/pEp/__init__.py
  20. 161
      src/pEp/_pEp/basic_api.cc
  21. 37
      src/pEp/_pEp/basic_api.hh
  22. 257
      src/pEp/_pEp/identity.cc
  23. 121
      src/pEp/_pEp/identity.hh
  24. 385
      src/pEp/_pEp/message.cc
  25. 194
      src/pEp/_pEp/message.hh
  26. 161
      src/pEp/_pEp/message_api.cc
  27. 30
      src/pEp/_pEp/message_api.hh
  28. 656
      src/pEp/_pEp/pEpmodule.cc
  29. 35
      src/pEp/_pEp/pEpmodule.hh
  30. 172
      src/pEp/_pEp/str_attr.cc
  31. 0
      src/pEp/_pEp/str_attr.hh
  32. 123
      src/pEp/_pEp/user_interface.cc
  33. 61
      src/pEp/_pEp/user_interface.hh
  34. 172
      src/pEp/native_pEp/basic_api.cc
  35. 30
      src/pEp/native_pEp/basic_api.hh
  36. 285
      src/pEp/native_pEp/identity.cc
  37. 102
      src/pEp/native_pEp/identity.hh
  38. 413
      src/pEp/native_pEp/message.cc
  39. 154
      src/pEp/native_pEp/message.hh
  40. 172
      src/pEp/native_pEp/message_api.cc
  41. 27
      src/pEp/native_pEp/message_api.hh
  42. 673
      src/pEp/native_pEp/pEpmodule.cc
  43. 28
      src/pEp/native_pEp/pEpmodule.hh
  44. 184
      src/pEp/native_pEp/str_attr.cc
  45. 130
      src/pEp/native_pEp/user_interface.cc
  46. 60
      src/pEp/native_pEp/user_interface.hh
  47. 27
      test/basic_doctest.py
  48. 3
      tests/__init__.py
  49. 64
      tests/conftest.py
  50. 12
      tests/constants.py
  51. 24
      tests/data/1A97F263D8319D6885F638C5AA81E1B5457A2B40.pub.asc
  52. 26
      tests/data/1A97F263D8319D6885F638C5AA81E1B5457A2B40.sec.asc
  53. 24
      tests/data/2D35731B9C754564CBAD15D2D18F7444594F2283.pub.asc
  54. 26
      tests/data/2D35731B9C754564CBAD15D2D18F7444594F2283.sec.asc
  55. 24
      tests/data/3E45175EE953EBBEB948F11A6A03DB2A17FB9D15.pub.asc
  56. 26
      tests/data/3E45175EE953EBBEB948F11A6A03DB2A17FB9D15.sec.asc
  57. 24
      tests/data/6A9835699EF1215F1558A496D9C1D4B0984094E5.pub.asc
  58. 26
      tests/data/6A9835699EF1215F1558A496D9C1D4B0984094E5.sec.asc
  59. 126
      tests/model.py
  60. 55
      tests/test_basic.py
  61. 107
      tests/test_identity.py
  62. 169
      tests/test_message.py
  63. 15
      tests/test_pep.py
  64. 13
      tests/utils.py
  65. 2
      utils/pEp

30
.editorconfig

@ -0,0 +1,30 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 300
tab_width = 4
# C++ Coding style
# I know these keys are unknown, regard it as documentation
[*.{cc,hh}]
indent_brace_style = K&R
curly_bracket_next_line = false
spaces_around_operators = true
spaces_around_brackets = true
[*.py]
indent_size = 4
[Makefile]
indent_style = tab
[*.ini]
indent_size = 2
[*.cfg]
indent_size = 2

50
.gitignore

@ -1,10 +1,6 @@
syntax: glob syntax: glob
local.conf
*.swp
ws/ ws/
tags/
.DS_Store/
pEp.egg-info/ pEp.egg-info/
dist/ dist/
build/ build/
@ -34,3 +30,49 @@ settings.json
docs/build/ docs/build/
.eggs/ .eggs/
.tox/ .tox/
=======
# Build config
local.conf
# Python artifacts
/build/
/dist/
__pycache__/
*.py[co]
/.tox
.pytest_cache
/venv/
/_venv/
*.egg-info/
/*.egg-link
/*.egg
/.eggs/
/pip-wheel-metadata/
# file generated by setuptools_scm
/src/pEp/__version__.py
# Doc artifacts
/docs/build/
# Test artifacts
/test/Laptop
/test/Library
/test/Phone
/test/TestInbox
/test/Backup
/test/lib
/test/imap_settings.py
# Editor artifacts and config
.idea/
*~
.\#*
\#*#
*.swp
.idea
.spyproject
*.code-workspace
# platform artifacts
.DS_store

28
.hgignore

@ -1,28 +0,0 @@
syntax: glob
local.conf
*.swp
ws
tags
.DS_Store
pEp.egg-info
dist
build
.pythonhist
.gnupg
.lldb
.pEp_management.db*
.python_history
__pycache__
test/Laptop
test/Library
test/Phone
test/TestInbox
test/Backup
test/lib
test/imap_settings.py
venv
launch.json
settings.json
# Default ignored files
.idea/

9
AUTHORS.txt

@ -0,0 +1,9 @@
# This is the list of pEpPythonAdapter's significant contributors.
#
# This does not necessarily list everyone who has contributed code
# To see the full list of contributors, see the revision history in
# source control.
Volker Birk <vb@pep-project.org> (original author and maintainer)
Heck <heck@pep.foundation> (current maintainer and author)
Hartmut Goebel <h.goebel@crazy-compilers.com> (contributions)
juga <juga@pep.foundation> (contributions)

91
Makefile

@ -1,9 +1,18 @@
include Makefile.conf include Makefile.conf
.PHONY: all dist dist-egg dist-whl install install-prefix install-sys compile clean devenv envtest .PHONY: all compile compile-inplace dist dist-egg dist-whl install install-user venv envtest install-test test develop docs clean clean-all clean-docs
all: dist all: dist
# Build
# =====
compile:
python3 setup.py build_ext $(DEBUG_OPT) $(PREFIX_OPT)
compile-inplace:
python3 setup.py build_ext $(DEBUG_OPT) $(PREFIX_OPT) --inplace
# Packaging
# =========
# create wheel and egg package in dist/ # create wheel and egg package in dist/
dist: dist-whl dist-egg dist: dist-whl dist-egg
@ -16,34 +25,66 @@ dist-egg: compile
python3 setup.py bdist_egg python3 setup.py bdist_egg
# installs the package into the user home # Installation
install: compile # ============
python3 setup.py install --force --user
# installs the package into PREFIX path
install-prefix: compile
python3 setup.py install --force $(PREFIX_OPT)
# installs the package system wide # installs the package system wide
install-sys: compile install: compile
python3 setup.py install --force pip3 install .
# build the module into build/ # installs the package into your user home
compile: install-user: compile
python3 setup.py build_ext $(DEBUG_OPT) $(PREFIX_OPT) pip3 install . --user
clean:
rm -r $(BUILD_DIR)
rm -r $(DIST_DIR)
devenv: # Envrionment
# ===========
# Creates and activates a new venv that has the LD_LIBRARY_PATH/DYLD_LIBRARY_PATH
# already set for the prefix specified in local.conf
# Only activates venv if already existing
venv:
python3 -m venv $(VENV_DIR)
LD_LIBRARY_PATH=$(PREFIX)/lib \ LD_LIBRARY_PATH=$(PREFIX)/lib \
DYLD_LIBRARY_PATH=$(PREFIX)/lib \ DYLD_LIBRARY_PATH=$(PREFIX)/lib \
PYTHONPATH=$PYTHONPATH:`pwd`/build/lib.linux-x86_64-3.7:\ bash --rcfile $(VENV_DIR)/bin/activate
PYTHONPATH=$PYTHONPATH:`pwd`/build/lib.macosx-10.9-x86_64-3.8:\
`pwd`/src \
bash -l
# Tests if the current environment is able to load the pEp module
envtest: envtest:
python3 -c 'import pEp' python3 -c 'import pEp'
# Test
# ====
# Use these targets only in venv created with 'make venv'
install-test: compile
pip3 install .[test]
# TODO: maybe use setup.py test?
# --forked, because every test needs a separate process, see PYADPT-100
test:
pytest
# Development
develop: compile
pip install -e .
# Documentation
# =============
docs: compile-inplace
make html -C docs/
# Housekeeping
# ============
clean-all: clean
rm -rf $(VENV_DIR)
clean: clean-docs
rm -rf $(BUILD_DIR)
rm -rf $(DIST_DIR)
rm -rf $(PYTHON_ARTIFACTS)
rm -rf $(VERSION_FILE)
rm -rf $(BUILD_INPLACE)
clean-docs:
make clean -C docs/

12
Makefile.conf

@ -3,10 +3,16 @@ HERE:=$(dir $(lastword $(MAKEFILE_LIST)))
# Constants # Constants
BUILD_DIR = ./build BUILD_DIR = ./build
DIST_DIR = ./dist DIST_DIR = ./dist
VERSION_FILE = ./src/pEp/__version__.py
BUILD_INPLACE = ./src/pEp/_pEp.cpython-38-darwin.so
PYTHON_ARTIFACTS += ./.eggs
PYTHON_ARTIFACTS += ./src/pEp.egg-info
PYTHON_ARTIFACTS += ./.pytest_cache
VENV_DIR = ./venv
# Build config Defaults # Build config Defaults
DEBUG=0 DEBUG=0
PREFIX?=$(HOME) PREFIX=
######### Overrides ######### ######### Overrides #########
-include $(HERE)local.conf -include $(HERE)local.conf
@ -15,4 +21,6 @@ ifeq ($(DEBUG),1)
DEBUG_OPT+=--debug DEBUG_OPT+=--debug
endif endif
PREFIX_OPT += --prefix=$(PREFIX) ifneq ($(PREFIX),)
PREFIX_OPT += --prefix=$(PREFIX)
endif

42
README.md

@ -1,42 +0,0 @@
# pEpPythonAdapter
## Build Insttructions
These build instructions should work on:
* Linux (Verified 26.4.20 - heck)
* MacOS (Verified 26.4.20 - heck)
* Windows
### Build
To build against system wide pEp installation (libs/includes)
```bash
python3 setup.py build_ext
```
To build against a pEp installation in your home dir (libs/includes):
```bash
python3 setup.py build_ext --local
```
To build against a pEp installation in a custom installation root (libs/includes)
```bash
python3 setup.py build_ext --prefix=<path_to_your_install_root>
```
### Install
To install the extension module system wide, as root, run:
```bash
python3 setup.py install
```
To install the extension module into you home dir
```bash
python3 setup.py install --user
```
To install the extension module into a custom destination
```bash
python3 setup.py install --prefix=<custom_destination_root>
```
Attention: The ~ (tilde) does not get expanded, but env vars work ($HOME).

22
README.rst

@ -0,0 +1,22 @@
pEpPythonAdapter
================
Python adapter for the `pEpEngine <https://pep.foundation/dev/repos/pEpEngine/>`_
Documentation
-------------
Please find the documentation in the ``docs`` directory.
Issues
------
If you are not pEp internal, please send a mail to: heck@pep.foundation
If you are pEp internal, please open a ticket in our `jira bugtracker <https://pep.foundation/jira/projects/PYADPT/)`_ and for any questions, you are always welcome on #adapter>.
License
-------
|GNU AFFERO GENERAL PUBLIC LICENSE
|Version 3, 19 November 2007
|See LICENSE.txt

19
docs/Makefile

@ -0,0 +1,19 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
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)

35
docs/make.bat

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd

12
docs/source/api/pEp.rst

@ -0,0 +1,12 @@
pEp package
===========
Module contents
---------------
.. automodule:: pEp
:members:
:imported-members:
:undoc-members:
:show-inheritance:

206
docs/source/conf.py

@ -0,0 +1,206 @@
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# 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('../../src'))
# -- Project information -----------------------------------------------------
project = "pEpPythonAdapter"
copyright = "2020, Volker Birk, heck, Hartmut Goebel, juga"
author = "Volker Birk, heck, Hartmut Goebel, juga"
import pEp
# The full version, including alpha/beta/rc tags
release = pEp.__version__
# The short X.Y version
version = ".".join(release.split(".")[:2])
# DEBUG
print("release:", release)
print("version:", version)
# -- 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 = [
"sphinx.ext.autodoc",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.todo",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
]
# 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"
# 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 pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
# -- 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 = "nature"
# 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"]
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = "pEpPythonAdapterdoc"
# -- 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,
"pEpPythonAdapter.tex",
"pEpPythonAdapter Documentation",
"2020, Volker Birk, heck, juga",
"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,
"peppythonadapter",
"pEpPythonAdapter 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,
"pEpPythonAdapter",
"pEpPythonAdapter Documentation",
author,
"pEpPythonAdapter",
"One line description of project.",
"Miscellaneous",
)
]
# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ["search.html"]
# -- Extension configuration -------------------------------------------------
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {"https://docs.python.org/": None}
# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

21
docs/source/index.rst

@ -0,0 +1,21 @@
.. pEpPythonAdapter documentation master file, created by
sphinx-quickstart on Wed Oct 21 12:35:11 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to pEpPythonAdapter's documentation!
============================================
.. toctree::
:maxdepth: 2
:caption: Contents:
install
software_using
api/pEp
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

143
docs/source/install.rst

@ -0,0 +1,143 @@
Installation
============
Requirements
------------
In order to build, use or install this extension module, you need to have some
system dependencies already installed:
* pEp-base (sequoia, libetpan, asn1c, yml2, pEpEngine, libpEpAdapter)
* boost-python
These `build instructions <https://dev.pep.foundation/Common%20Adapter%20Documentation/Adapter_Build_Instructions>`_ will get you all setup.
Additionally, there is a `build script <http://pep-security.lu/gitlab/juga/Internal-Deployment/-/blob/master/build-pep-stack.sh>`_
that executes these build instructions automatically (Debian and MacOS):
.. Note:: If you dont install pEp-base system wide, but under a prefix, like /home/foo/local/,
you will need to have LD_LIBARY_PATH/DYLD_LIBRARY_PATH adjusted for all the following operations.
Build
-----
The pEpPythonAdapter is a python extension module that contains C/C++ code that needs to be
compiled first. So, before any use or installation, the module needs to be built.
Build config
~~~~~~~~~~~~
Create a local build config by creating a 'local.conf' file. There is a 'local.conf.example' describing
all the build options. You can use this as a template.
``cp local.conf.example local.conf``
If you have pEp-base installed under a custom prefix (e.g. /home/foo/local) it is important
that you specify "PREFIX".
Build
~~~~~
To build the module just type:
``make``
This will compile the C/C++ parts of the module and create the python packages in the .egg and .wheel format
in the dist/ dir.
Virtualenv
----------
We recommend using a venv to work on/with the pEpPythonAdapter.
There is a convenience make target that will create and activate a venv that already has the LD_LIBRARY_PATH
or DYLD_LIBRARY_PATH set according to your ``local.conf``.
If the venv does not exist yet it will be created and activated.
If the venv already exists it will only be activated.
``make venv``
After that, to install the pEp module into the venv, do:
``make install``
Installation
------------
You can install the module in the in the following ways.
To install the extension module system wide or into a venv, use:
``make install``
To install the extension module into your home dir, use:
``make install-user``
Test
----
To run the whole testsuite you need to first create/activate the venv:
``make venv``
then install the test-dependencies:
``make install-test``
And finally run the test-suite:
``make test``
Module Development
------------------
To develop on the module itself, first of all create and activate a venv:
``make venv``
Then, in the venv install the module in development mode.
``make develop``
While developing there are two levels of changes. Changes to the python part of the module (pEp), and
changes to the C/C++ part of the module (_pEp). If you change just python code, the changes are effective immediately.
If you do changes to the C/C++ part you need to issue ``make develop`` again, to recompile the extension and install
the new binary (.so/.dylib) of the module into the venv.
Documentation
-------------
The documentation of the pEpPythonAdapter uses `Sphinx <https://www.sphinx-doc.org/>`_
Refer to the `Sphinx installation instructions <https://www.sphinx-doc.org/en/master/usage/installation.html>`_ to install it.
To generate the documentation in the HTML format, there is a make target "docs"
But first, you need to create/activate the venv or set the LD_LIBRARY_PATH manually.
``make venv``
``make docs``
You can see the generated HTML documentation in a browser opening the directory
`docs/build/html`.
Housekeeping
------------
There are the following "clean" targets.
To delete all the generated documentation, run:
``make docs-clean``
To delete all the "derived" files including eggs, wheels, shared libs, build files and caches, run:
``make clean``
To delete all of make clean plus the venv (should equal a complete reset), run:
``make clean-all``
Docker
------
If you know how to use docker, you can avoid having to install all
the dependencies using the image
https://registry.gitlab.com/juga0/pepdocker/peppythonadapter.
.. Note:: This docker image is not officially maintained and it exists only
until there is an official Debian one.

7
docs/source/software_using.rst

@ -0,0 +1,7 @@
Software using pEpPythonAdapter
===============================
- `pEpProxy <https://pep-security.lu/gitlab/marcel/pepproxy>`_
- `command-line-tool <https://pep-security.lu/gitlab/enterprise-editon/command-line-tool>`_
- `pEpPythonMixnet <https://gitea.pep.foundation/pEp.foundation/pEpPythonMixnet>`_
- `pEpSimulator <URL???>`_

18
pyproject.toml

@ -0,0 +1,18 @@
[build-system]
# Preparing for PEP-517/PEP-518, but not in effect yet.
# These requires are not effective yet, setup.cfg is.
requires =[
"setuptools >=39.2.0",
"setuptools_scm >= 4.1.2",
"wheel >= 0.35.1" ]
build-backend = "setuptools.build_meta"
[tool.pytest.ini_options]
minversion = "6.0"
addopts = "-rP --forked"
testpaths = [
"tests",
]

28
setup.cfg

@ -1,16 +1,18 @@
[metadata] [metadata]
name = pEp name = pEp
version = 2.1.0-RC2
url = https://pep.foundation url = https://pep.foundation
author = Volker Birk download_url =
ext_package = pEp
author = Volker Birk, heck, Hartmut Goebel, juga
author_email = vb@pep-project.org author_email = vb@pep-project.org
maintainer = Matthias Heckmann maintainer = heck
maintainer_email = heck@pep.foundation maintainer_email = heck@pep.foundation
description = p≡p for Python description = p≡p for Python
long_description = file: README.md long_description = file: README.md
keywords = pEp, crypto, end-to-end, python adapter, key management, high-level keywords = pEp, crypto, end-to-end, python adapter, key management, high-level
license = GNU Affero General Public License license = GNU Affero General Public License
license_files = LICENSE license_files = LICENSE.txt
platforms = linux, macOs
classifiers = classifiers =
Intended Audience :: Developers Intended Audience :: Developers
Topic :: Utilities Topic :: Utilities
@ -21,3 +23,21 @@ classifiers =
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Development Status :: 3 - Alpha Development Status :: 3 - Alpha
[options]
zip_safe = false
include_package_data = true
python_requires = >= 3.6
test_suite = tests
install_requires =
# deprecated/redundant with pyproject.toml, but lets keep both ways around for now
setup_requires =
setuptools >=39.2.0
setuptools_scm >= 4.1.2
wheel >= 0.35.1
[options.extras_require]
# To install these dependencies, run pip install .[test]
test =
pytest
pytest-forked
doc = sphinx

57
setup.py

@ -27,18 +27,16 @@ def pEpLog(*msg):
msgstr += str(m) msgstr += str(m)
msgstr += separator msgstr += separator
func = inspect.currentframe().f_back.f_code func = inspect.currentframe().f_back.f_code
print(func.co_filename + " : " + func.co_name + " : " + msgstr) print(func.co_filename + " : " + func.co_name + " : " + msgstr)
class BuildExtCommand(build_ext):
class BuildExtCommand(build_ext):
user_options = build_ext.user_options + [ user_options = build_ext.user_options + [
('local', None, 'Use local pEp install in HOME/USERPROFILE for libs/includes'), ('prefix=', None, 'Use pEp-base installation in prefix (libs/includes)'),
('prefix=', None, 'Use local pEp install in prefix for libs/includes'),
] ]
def initialize_options(self): def initialize_options(self):
build_ext.initialize_options(self) build_ext.initialize_options(self)
self.local = None != environ.get('PER_USER_DIRECTORY')
self.prefix = getattr(self, "prefix=", None) self.prefix = getattr(self, "prefix=", None)
def windowsGetInstallLocation(self): def windowsGetInstallLocation(self):
@ -139,7 +137,6 @@ class BuildExtCommand(build_ext):
def finalize_options(self): def finalize_options(self):
build_ext.finalize_options(self) build_ext.finalize_options(self)
pEpLog("local: ", self.local)
pEpLog("prefix: ", self.prefix) pEpLog("prefix: ", self.prefix)
pEpLog("sys.platform: ", sys.platform) pEpLog("sys.platform: ", sys.platform)
@ -162,18 +159,11 @@ class BuildExtCommand(build_ext):
includes = [] includes = []
libdirs = [] libdirs = []
# Append home-dir
if self.local:
pEpLog("local mode")
home_include=[ join(home, 'include') ]
home_libdirs=[ join(home, 'lib') ]
includes += home_include
libdirs += home_libdirs
# Append prefix-dir # Append prefix-dir
if self.prefix: if self.prefix:
prefix_include=[ join(self.prefix, 'include') ] prefix_include = [join(self.prefix, 'include')]
prefix_libdirs=[ join(self.prefix, 'lib') ] prefix_libdirs = [join(self.prefix, 'lib')]
includes += prefix_include includes += prefix_include
libdirs += prefix_libdirs libdirs += prefix_libdirs
@ -206,33 +196,32 @@ if sys.platform == 'win32':
if sys.version_info[0] < 3: if sys.version_info[0] < 3:
FileNotFoundError = EnvironmentError FileNotFoundError = EnvironmentError
module_pEp = Extension( module_pEp = Extension(
'native_pEp', 'pEp._pEp',
sources = [ sources=[
'src/pEp/native_pEp/pEpmodule.cc', 'src/pEp/_pEp/pEpmodule.cc',
'src/pEp/native_pEp/basic_api.cc', 'src/pEp/_pEp/basic_api.cc',
'src/pEp/native_pEp/identity.cc', 'src/pEp/_pEp/identity.cc',
'src/pEp/native_pEp/message.cc', 'src/pEp/_pEp/message.cc',
'src/pEp/native_pEp/message_api.cc', 'src/pEp/_pEp/message_api.cc',
'src/pEp/native_pEp/str_attr.cc', 'src/pEp/_pEp/str_attr.cc',
# 'src/pEp/native_pEp/user_interface.cc', # 'src/pEp/_pEp/user_interface.cc',
], ],
) )
# "MAIN" Function # "MAIN" Function
setup( setup(
name='pEp', package_dir={'': 'src'},
version='2.1.0-RC2',
description='p≡p for Python',
author="Volker Birk",
author_email="vb@pep-project.org",
maintainer="Heck",
maintainer_email="heck@pep.foundation",
package_dir={'':'src'},
packages=['pEp'], packages=['pEp'],
ext_modules=[module_pEp], ext_modules=[module_pEp],
cmdclass={ cmdclass={
'build_ext': BuildExtCommand, 'build_ext': BuildExtCommand,
}, },
# While not using a pyproject.toml, support setuptools_scm setup.cfg usage,
# see https://github.com/pypa/setuptools_scm/#setupcfg-usage
use_scm_version={
'write_to': 'src/pEp/__version__.py',
#TODO: fallback_version does not seem to work in case os missing tag
'fallback_version' : '0.0.0-RC0'
}
) )

49
src/pEp/__init__.py

@ -1,20 +1,39 @@
# -*- coding: UTF-8 -*-
# pEp package # pEp package
# This file is being exectued upon 'import pEp'
# #
# __all__ could be used to limit the symbols exported when using from <pkg> import * # The names that are in _pEp that do not begin with an underscore, will be be imported into, and "re-exported" from this module.
# They are defined in boost-python/C++, and are directly part of the pEpPythonAdapter API
# The names that are in _pEp that DO begin with an underscore, will not be imported into this module, but will be accessible like _pEp._underscore_function().
# They are not directly part of the pEpPythonAdapter API, and are meant to be wrapped in this module.
# Example:
# def underscore_function():
# _pEp._underscore_function()
# Import all symbols EXCEPT the ones beginning with underscore into the current namespace # __all__ could be used to limit the symbols exported when using from <pkg> import *
from native_pEp import * try:
# TODO: inter-pkg ref to make sure which native_pEp in sys.path gets loaded from .__version__ import version as __version__
# like: pEp.native_pEp except ImportError:
# import the module import warnings
import native_pEp warnings.warn("Error loading build-time defined __version__.py, trying setuptools now...")
try:
import setuptools_scm
__version__ = setuptools_scm.get_version()
del setuptools_scm
except Exception:
warnings.warn('could not determine %s package version' % __name__)
__version__ = '0.0.dev0+unknown'
# Imports all symbols EXCEPT the ones beginning with underscore
from ._pEp import *
# import the native module into the current namespace because we also need to access the names beginning
# with an underscore (of _pEp), but we dont want to import them into this module
import pEp._pEp
# Executed on module import # Executed on module import
def init(): def init():
print(init, "called") print(init, "called")
native_pEp._init_after_main_module() _pEp._init_after_main_module()
def message_to_send(msg): def message_to_send(msg):
@ -41,12 +60,4 @@ def notify_handshake(me, partner, signal):
print("overwrite this method") print("overwrite this method")
# Executed when run as script init()
def main():
print("I am being run as a script")
# MAIN
if __name__ == "__main__":
main()
else:
init()

161
src/pEp/_pEp/basic_api.cc

@ -0,0 +1,161 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <sstream>
// Engine
#include <pEp/keymanagement.h>
#include <pEp/message_api.h>
#include <pEp/Adapter.hh>
// local
#include "basic_api.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
void update_identity(Identity &ident) {
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.user_id() == PEP_OWN_USERID)
throw runtime_error("update_identity: '"
PEP_OWN_USERID
"' may only be used for own identities");
PEP_STATUS status = update_identity(Adapter::session(), ident);
_throw_status(status);
}
void myself(Identity &ident) {
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.username() == "")
throw invalid_argument("username needed");
if (ident.user_id() == "")
ident.user_id(ident.address());
PEP_STATUS status = myself(Adapter::session(), ident);
_throw_status(status);
}
string _trustwords(Identity me, Identity partner, string lang, bool full) {
if (me.fpr() == "" || partner.fpr() == "")
throw invalid_argument("fingerprint needed in Identities");
if (lang == "" && me.lang() == partner.lang())
lang = me.lang();
char *words = NULL;
size_t size = 0;
PEP_STATUS status = get_trustwords(Adapter::session(), me, partner,
lang.c_str(), &words, &size, full);
_throw_status(status);
return words;
}
void trust_personal_key(Identity ident) {
if (ident.fpr() == "")
throw invalid_argument("fingerprint needed in Identities");
if (ident.user_id() == "")
throw invalid_argument("user_id must be provided");
PEP_STATUS status = trust_personal_key(Adapter::session(), ident);
_throw_status(status);
}
void set_identity_flags(Identity ident, identity_flags_t flags) {
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.user_id() == "")
throw invalid_argument("user_id needed");
PEP_STATUS status = set_identity_flags(Adapter::session(), ident, flags);
_throw_status(status);
}
void unset_identity_flags(Identity ident, identity_flags_t flags) {
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.user_id() == "")
throw invalid_argument("user_id needed");
PEP_STATUS status = unset_identity_flags(Adapter::session(), ident, flags);
_throw_status(status);
}
void key_reset_trust(Identity ident) {
if (ident.fpr() == "")
throw invalid_argument("fpr needed");
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.user_id() == "")
throw invalid_argument("user_id needed");
PEP_STATUS status = key_reset_trust(Adapter::session(), ident);
_throw_status(status);
}
boost::python::list import_key(string key_data) {
::identity_list *private_keys = NULL;
PEP_STATUS status = ::import_key(Adapter::session(), key_data.c_str(), key_data.size(), &private_keys);
if (status && status != PEP_KEY_IMPORTED)
_throw_status(status);
auto result = boost::python::list();
for (::identity_list *il = private_keys; il && il->ident; il = il->next) {
::pEp_identity *ident = ::identity_dup(il->ident);
if (!ident) {
free_identity_list(private_keys);
throw bad_alloc();
}
result.append(Identity(ident));
}
free_identity_list(private_keys);
return result;
}
string export_key(Identity ident) {
PEP_STATUS status = PEP_STATUS_OK;
char *key_data = NULL;
size_t size;
status = ::export_key(Adapter::session(), ident.fpr().c_str(), &key_data, &size);
_throw_status(status);
return key_data;
}
string export_secret_key(Identity ident) {
PEP_STATUS status = PEP_STATUS_OK;
char *key_data = NULL;
size_t size;
status = ::export_secret_key(Adapter::session(), ident.fpr().c_str(), &key_data, &size);
_throw_status(status);
return key_data;
}
void set_own_key(Identity &ident, string fpr) {
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.username() == "")
throw invalid_argument("username needed");
if (ident.user_id() == "")
throw invalid_argument("user_id needed");
if (fpr == "")
throw invalid_argument("fpr needed");
const char *fpr_c = fpr.c_str();
PEP_STATUS status = set_own_key(Adapter::session(), ident, fpr_c);
_throw_status(status);
}
} // namespace PythonAdapter
} // namespace pEp

37
src/pEp/_pEp/basic_api.hh

@ -0,0 +1,37 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef BASIC_API_HH
#define BASIC_API_HH
#include "pEpmodule.hh"
namespace pEp {
namespace PythonAdapter {
void update_identity(Identity &ident);
void myself(Identity &ident);
string _trustwords(Identity me, Identity partner, string lang, bool full);
void trust_personal_key(Identity ident);
void set_identity_flags(Identity ident, identity_flags_t flags);
void unset_identity_flags(Identity ident, identity_flags_t flags);
void key_reset_trust(Identity ident);
boost::python::list import_key(string key_data);
string export_key(Identity ident);
string export_secret_key(Identity ident);
void set_own_key(Identity &ident, string fpr);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* BASIC_API_HH */

257
src/pEp/_pEp/identity.cc

@ -0,0 +1,257 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <typeinfo>
#include <sstream>
// Engine
#include <pEp/identity_list.h>
#include <pEp/keymanagement.h>
#include <pEp/key_reset.h>
// local
#include "identity.hh"
#include "pEpmodule.hh"
#include "basic_api.hh"
#include "message_api.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
Identity::Identity(string address, string username, string user_id,
string fpr, int comm_type, string lang, identity_flags_t flags)
: _ident(new_identity(address.c_str(), fpr.c_str(), user_id.c_str(),
username.c_str()), &::free_identity) {
if (!_ident)
throw bad_alloc();
_ident->comm_type = (PEP_comm_type) comm_type;
_ident->flags = (identity_flags_t) flags;
this->lang(lang);
}
Identity::Identity(const Identity &second)
: _ident(second._ident) {
}
Identity::Identity(pEp_identity *ident)
: _ident(ident, &::free_identity) {
}
Identity::~Identity() {
}
Identity::operator pEp_identity *() {
return _ident.get();
}
Identity::operator const pEp_identity *() const {
return _ident.get();
}
string Identity::_repr() {
stringstream build;
build << "Identity(";
string address;
if (_ident->address)
address = string(_ident->address);
build << repr(address) << ", ";
string username;
if (_ident->username)
username = string(_ident->username);
build << repr(username) << ", ";
string user_id;
if (_ident->user_id)
user_id = string(_ident->user_id);
build << repr(user_id) << ", ";
string fpr;
if (_ident->fpr)
fpr = string(_ident->fpr);
build << repr(fpr) << ", ";
build << (int) _ident->comm_type << ", ";
string lang = _ident->lang;
build << repr(lang) << ")";
return build.str();
}
string Identity::_str() {
if (!(_ident->address && _ident->address[0]))
return "";
if (!(_ident->username && _ident->username[0]))
return _ident->address;
return string(_ident->username) + " <" + _ident->address + ">";
}
void Identity::username(string value) {
if (value.length() && value.length() < 5)
throw length_error("username must be at least 5 characters");
str_attr(_ident->username, value);
}
void Identity::lang(string value) {
if (value == "")
memset(_ident->lang, 0, 3);
else if (value.length() != 2)
throw length_error("length of lang must be 2");
else
memcpy(_ident->lang, value.c_str(), 3);
}
string Identity::lang() {
return _ident->lang;
}
int Identity::rating() {
if (!(_ident->address))
throw invalid_argument("address must be given");
PEP_rating rating = PEP_rating_undefined;
PEP_STATUS status = ::identity_rating(Adapter::session(), _ident.get(), &rating);
_throw_status(status);
return (int) rating;
}
PEP_color Identity::color() {
return _color(rating());
}
Identity Identity::copy() {
pEp_identity *dup = ::identity_dup(*this);
if (!dup)
throw bad_alloc();
return Identity(dup);
}
Identity Identity::deepcopy(dict &) {
return copy();
}
void Identity::update() {
update_identity(*this);
}
void Identity::key_reset(string fpr) {
PEP_STATUS status = ::key_reset_identity(Adapter::session(), *this,
fpr != "" ? fpr.c_str() : nullptr);
_throw_status(status);
}
void Identity::key_mistrusted() {
PEP_STATUS status = ::key_mistrusted(Adapter::session(), *this);
_throw_status(status);
}
bool Identity::is_pEp_user() {
bool result;
PEP_STATUS status = ::is_pEp_user(Adapter::session(), *this, &result);
_throw_status(status);
return result;
}
void Identity::enable_for_sync() {
PEP_STATUS status = ::enable_identity_for_sync(Adapter::session(), *this);
_throw_status(status);
}
void Identity::disable_for_sync() {
PEP_STATUS status = ::disable_identity_for_sync(Adapter::session(), *this);
_throw_status(status);
}
// Myself::Myself(string address, string username, string user_id, string lang)
// : Identity(address, username, user_id, "", 0, lang) {
// if (!(address.length() && username.length()))
// throw invalid_argument("address and username must be set");
// if (lang.length() && lang.length() != 2)
// throw length_error("lang must be an ISO 639-1 language code or empty");
//
// // FIXME: should set .me
// // _ident->me = true;
// if (user_id.length())
// throw runtime_error("user_id feature not yet implemented for Myself");
// }
// void Myself::update() {
// pEp::PythonAdapter::myself(*this);
// }
Identity identity_attr(pEp_identity *&ident) {
if (!ident)
throw out_of_range("no identity assigned");
pEp_identity *_dup = ::identity_dup(ident);
if (!_dup)
throw bad_alloc();
Identity _ident(_dup);
return _ident;
}
void identity_attr(pEp_identity *&ident, object value) {
Identity &_ident = extract<Identity &>(value);
pEp_identity *_dup = ::identity_dup(_ident);
if (!_dup)
throw bad_alloc();
PEP_STATUS status = ::update_identity(Adapter::session(), _dup);
_throw_status(status);
::free_identity(ident);
ident = _dup;
}
boost::python::list identitylist_attr(identity_list *&il) {
boost::python::list result;
for (identity_list *_il = il; _il && _il->ident; _il = _il->next) {
pEp_identity *ident = ::identity_dup(_il->ident);
if (!ident)
throw bad_alloc();
result.append(object(Identity(ident)));
}
return result;
}
void identitylist_attr(identity_list *&il, boost::python::list value) {
identity_list *_il = ::new_identity_list(NULL);
if (!_il)
throw bad_alloc();
identity_list *_i = _il;
for (int i = 0; i < len(value); i++) {
extract < Identity & > extract_identity(value[i]);
if (!extract_identity.check()) {
free_identity_list(_il);
}
pEp_identity *_ident = extract_identity();
pEp_identity *_dup = ::identity_dup(_ident);
if (!_dup) {
::free_identity_list(_il);
throw bad_alloc();
}
PEP_STATUS status = ::update_identity(Adapter::session(), _dup);
if (status != PEP_STATUS_OK) {
::free_identity_list(_il);
_throw_status(status);
}
_i = ::identity_list_add(_i, _dup);
if (!_i) {
::free_identity_list(_il);
throw bad_alloc();
}
}
::free_identity_list(il);
il = _il;
}
} // namespace PythonAdapter
} // namespace pEp

121
src/pEp/_pEp/identity.hh

@ -0,0 +1,121 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef IDENTITY_HH
#define IDENTITY_HH
// System
#include <boost/python.hpp>
#include <string>
#include <memory>
#include <cstddef>
// Engine
#include <pEp/pEpEngine.h>
#include <pEp/message_api.h>
//libpEpAdapter
#include "pEp/Adapter.hh"
// local
#include "str_attr.hh"
namespace pEp {
namespace PythonAdapter {
using std::string;
using std::shared_ptr;
// Identity is owning a pEp_identity
class Identity {
protected:
shared_ptr <pEp_identity> _ident;
public:
Identity(string address = "", string username = "",
string user_id = "", string fpr = "", int comm_type = 0,
string lang = "", identity_flags_t flags = 0);
Identity(const Identity &second);
Identity(pEp_identity *ident);
virtual ~Identity();
operator pEp_identity *();
operator const pEp_identity *() const;
string _repr();
string _str();
string address() { return str_attr(_ident->address); }
void address(string value) { str_attr(_ident->address, value); }
string fpr() { return str_attr(_ident->fpr); }
void fpr(string value) { str_attr(_ident->fpr, value); }
string user_id() { return str_attr(_ident->user_id); }
void user_id(string value) { str_attr(_ident->user_id, value); }
string username() { return str_attr(_ident->username); }
void username(string value);
PEP_comm_type comm_type() { return _ident->comm_type; }
void comm_type(PEP_comm_type value) { _ident->comm_type = value; };
std::string lang();
void lang(std::string value);
identity_flags_t flags() { return _ident->flags; }
void flags(identity_flags_t flags) { _ident->flags = flags; }
int rating();
PEP_color color();
Identity copy();
Identity deepcopy(dict &memo);
virtual void update();
void key_reset(string fpr = "");
void key_mistrusted();
bool is_pEp_user();
void enable_for_sync();
void disable_for_sync();
};
// class Myself : public Identity {
// public:
// Myself(string address, string username, string user_id = "", string lang = "");
//
// virtual void update();
// };
Identity identity_attr(pEp_identity *&ident);
void identity_attr(pEp_identity *&ident, object value);
boost::python::list identitylist_attr(identity_list *&il);
void identitylist_attr(identity_list *&il, boost::python::list value);
} // namespace PythonAdapter
} // namespace pEp
#endif /* IDENTITY_HH */

385
src/pEp/_pEp/message.cc

@ -0,0 +1,385 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <sstream>
#include <vector>
#include <Python.h>
// Engine
#include <pEp/mime.h>
#include <pEp/keymanagement.h>
#include <pEp/message_api.h>
// local
#include "message.hh"
#include "message_api.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
Message::Blob::Blob(bloblist_t *bl, bool chained) :
_bl(bl), part_of_chain(chained) {
if (!_bl)
throw bad_alloc();
}
Message::Blob::Blob(object data, string mime_type, string filename) :
_bl(new_bloblist(NULL, 0, NULL, NULL)), part_of_chain(false) {
if (!_bl)
throw bad_alloc();
Py_buffer src;
int result = PyObject_GetBuffer(data.ptr(), &src, PyBUF_CONTIG_RO);
if (result)
throw invalid_argument("need a contiguous buffer to read");
char *mem = (char *) malloc(src.len);
if (!mem) {
PyBuffer_Release(&src);
throw bad_alloc();
}
memcpy(mem, src.buf, src.len);
free(_bl->value);
_bl->size = src.len;
_bl->value = mem;
PyBuffer_Release(&src);
this->mime_type(mime_type);
this->filename(filename);
}
Message::Blob::Blob(const Message::Blob &second) :
_bl(second._bl), part_of_chain(true) {
}
Message::Blob::~Blob() {
if (!part_of_chain) {
free(_bl->value);
free(_bl);
}
}
string Message::Blob::_repr() {
stringstream build;
build << "Blob(";
if (!_bl) {
build << "b'', '', ''";
} else {
build << "bytes(" << _bl->size << "), ";
string mime_type;
if (_bl->mime_type)
mime_type = string(_bl->mime_type);
string filename;
if (_bl->filename)
filename = string(_bl->filename);
build << repr(mime_type) << ", ";
build << repr(filename);
}
build << ")";
return build.str();
}
int Message::Blob::getbuffer(PyObject *self, Py_buffer *view, int flags) {
bloblist_t *bl = NULL;
try {
Message::Blob &blob = extract<Message::Blob &>(self);
bl = blob._bl;
}
catch (exception &e) {
PyErr_SetString(PyExc_RuntimeError, "extract not possible");
view->obj = NULL;
return -1;
}
if (!(bl && bl->value)) {
PyErr_SetString(PyExc_RuntimeError, "no data available");
view->obj = NULL;
return -1;
}
return PyBuffer_FillInfo(view, self, bl->value, bl->size, 0, flags);
}
string Message::Blob::decode(string encoding) {
if (encoding == "") {
string _mime_type = _bl->mime_type ? _bl->mime_type : "";
encoding = "ascii";
if (_mime_type == "application/pEp.sync")
encoding = "pep.sync";
if (_mime_type == "application/pEp.keyreset")
encoding = "pep.distribution";
}
object codecs = import("codecs");
object _decode = codecs.attr("decode");
return call<string>(_decode.ptr(), this, encoding);
}
PyBufferProcs Message::Blob::bp = {getbuffer, NULL};
Message::Message(int dir, Identity *from)
: _msg(new_message((PEP_msg_direction) dir), &free_message) {
if (!_msg)
throw bad_alloc();
if (from) {
_msg->from = ::identity_dup(*from);
if (!_msg->from)
throw bad_alloc();
_msg->dir = (PEP_msg_direction) dir;
}
}
Message::Message(string mimetext)
: _msg(NULL, &free_message) {
message *_cpy;
PEP_STATUS status = mime_decode_message(mimetext.c_str(),
mimetext.size(), &_cpy, NULL);
switch (status) {
case PEP_STATUS_OK:
if (_cpy)
_cpy->dir = PEP_dir_outgoing;
else
_cpy = new_message(PEP_dir_outgoing);
if (!_cpy)
throw bad_alloc();
_msg = shared_ptr<message>(_cpy);
break;
case PEP_BUFFER_TOO_SMALL:
throw runtime_error("mime_decode_message: buffer too small");
case PEP_CANNOT_CREATE_TEMP_FILE:
throw runtime_error("mime_decode_message: cannot create temp file");
case PEP_OUT_OF_MEMORY:
throw bad_alloc();
default:
stringstream build;
build << "mime_decode_message: unknown error (" << (int) status << ")";
throw runtime_error(build.str());
}
}
Message::Message(const Message &second)
: _msg(second._msg) {
if (!_msg.get())
throw bad_alloc();
}
Message::Message(message *msg)
: _msg(::message_dup(msg), &free_message) {
}
Message::~Message() {
}
Message::operator message *() {
return _msg.get();
}
Message::operator const message *() const {
return _msg.get();
}
string Message::_str() {
if (!(_msg->from && _msg->from->address && _msg->from->address[0]))
throw out_of_range(".from_.address missing");
char *mimetext;
string result;
PEP_STATUS status = mime_encode_message(*this, false, &mimetext, false);
switch (status) {
case PEP_STATUS_OK:
result = mimetext;
free(mimetext);
break;
case PEP_BUFFER_TOO_SMALL:
throw runtime_error("mime_encode_message: buffer too small");
case PEP_CANNOT_CREATE_TEMP_FILE:
throw runtime_error("mime_encode_message: cannot create temp file");
case PEP_OUT_OF_MEMORY:
throw bad_alloc();
default:
stringstream build;
build << "mime_encode_message: unknown error (" << (int) status << ")";
throw runtime_error(build.str());
}
return result;
}
string Message::_repr() {
stringstream build;
build << "Message(" << repr(_str()) << ")";
return build.str();
}
boost::python::tuple Message::attachments() {
boost::python::list l;
for (bloblist_t *bl = _msg->attachments; bl && bl->value; bl =
bl->next) {
l.append(Blob(bl, true));
}
return boost::python::tuple(l);
}
void Message::attachments(boost::python::list value) {
bloblist_t *bl = new_bloblist(NULL, 0, NULL, NULL);
if (!bl)
throw bad_alloc();
bloblist_t *_l = bl;
for (int i = 0; i < len(value); i++) {
Message::Blob &blob = extract<Message::Blob &>(value[i]);
_l = bloblist_add(_l, blob._bl->value, blob._bl->size,
blob._bl->mime_type, blob._bl->filename);
if (!_l) {
for (_l = bl; _l && _l->value;) {
free(_l->mime_type);
free(_l->filename);
bloblist_t *_ll = _l;
_l = _l->next;
free(_ll);
}
throw bad_alloc();
}
}
for (int i = 0; i < len(value); i++) {
Message::Blob &blob = extract<Message::Blob &>(value[i]);
blob._bl->value = NULL;
blob._bl->size = 0;
free(blob._bl->mime_type);
blob._bl->mime_type = NULL;
free(blob._bl->filename);
blob._bl->filename = NULL;
}
free_bloblist(_msg->attachments);
_msg->attachments = bl;
}
Message Message::encrypt() {
boost::python::list extra;
return encrypt_message(*this, extra, PEP_enc_PGP_MIME, 0);
}
Message Message::_encrypt(boost::python::list extra, int enc_format, int flags) {
if (!enc_format)
enc_format = PEP_enc_PGP_MIME;
return encrypt_message(*this, extra, enc_format, flags);
}
boost::python::tuple Message::decrypt(int flags) {
return pEp::PythonAdapter::decrypt_message(*this, flags);
}
PEP_rating Message::outgoing_rating() {
if (_msg->dir != PEP_dir_outgoing)
throw invalid_argument("Message.dir must be outgoing");
if (from().address() == "")
throw invalid_argument("from.address needed");
if (from().username() == "")
throw invalid_argument("from.username needed");
if (len(to()) + len(cc()) == 0)
throw invalid_argument("either to or cc needed");
PEP_STATUS status = myself(Adapter::session(), _msg->from);
_throw_status(status);
PEP_rating rating = PEP_rating_undefined;
status = outgoing_message_rating(Adapter::session(), *this, &rating);
_throw_status(status);
return rating;
}
PEP_color Message::outgoing_color() {
return _color(outgoing_rating());
}
Message Message::copy() {
message *dup = message_dup(*this);
if (!dup)
throw bad_alloc();
return Message(dup);
}
Message Message::deepcopy(dict &) {
return copy();
}
Message outgoing_message(Identity me) {
if (me.address().empty() || me.user_id().empty())
throw runtime_error("at least address and user_id of own user needed");
::myself(Adapter::session(), me);
auto m = Message(PEP_dir_outgoing, &me);
return m;
}
static object update(Identity ident) {
if (ident.address().empty())
throw runtime_error("at least address needed");
update_identity(Adapter::session(), ident);
return object(ident);
}
static boost::python::list update(boost::python::list il) {
for (int i = 0; i < len(il); i++) {
update(extract<Identity>(il[i]));
}
return il;
}
Message incoming_message(string mime_text) {
auto m = Message(mime_text);
m.dir(PEP_dir_incoming);
try {
m.from(update(m.from()));
}
catch (out_of_range &) {}
try {
m.recv_by(update(m.recv_by()));
}
catch (out_of_range &) {}
m.to(update(m.to()));
m.cc(update(m.cc()));
m.reply_to(update(m.reply_to()));
return m;
}
} // namespace PythonAdapter
} // namespace pEp

194
src/pEp/_pEp/message.hh

@ -0,0 +1,194 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef MESSAGE_HH
#define MESSAGE_HH
// System
#include <string>
#include <boost/python.hpp>
#include <boost/lexical_cast.hpp>
// Engine
#include <pEp/message.h>
#include <pEp/message_api.h>
// local
#include "str_attr.hh"
#include "identity.hh"
namespace pEp {
namespace PythonAdapter {
using std::string;
using std::runtime_error;
using std::invalid_argument;
using boost::lexical_cast;
// Message is owning a message struct
class Message {
shared_ptr<::message> _msg;
public:
// Blob is owning a bloblist_t struct - or not and just managing
// one depending on part_of_chain
class Blob {
bloblist_t *_bl;
bool part_of_chain;
public:
Blob(bloblist_t *bl = new_bloblist(NULL, 0, NULL, NULL),
bool chained = false);
Blob(object data, string mime_type = "", string filename = "");
Blob(const Blob &second);
~Blob();
string _repr();
string mime_type() { return _bl ? str_attr(_bl->mime_type) : ""; }
void mime_type(string value) { str_attr(_bl->mime_type, value); }
string filename() { return str_attr(_bl->filename); }
void filename(string value) { str_attr(_bl->filename, value); }
size_t size() { return _bl->size; }
string decode(string encoding);
string decode() { return decode(""); }
static PyBufferProcs bp;
friend class Message;
protected:
static int getbuffer(PyObject *self, Py_buffer *view, int flags);
};
Message(int dir = PEP_dir_outgoing, Identity *from = NULL);
Message(string mimetext);
Message(const Message &second);
Message(message *msg);
~Message();
operator message *();
operator const message *() const;
string _str();
string _repr();
PEP_msg_direction dir() { return _msg->dir; }
void dir(PEP_msg_direction value) { _msg->dir = value; }
string id() { return str_attr(_msg->id); }
void id(string value) { str_attr(_msg->id, value); }
string shortmsg() { return str_attr(_msg->shortmsg); }
void shortmsg(string value) { str_attr(_msg->shortmsg, value); }
string longmsg() { return str_attr(_msg->longmsg); }
void longmsg(string value) { str_attr(_msg->longmsg, value); }
string longmsg_formatted() { return str_attr(_msg->longmsg_formatted); }
void longmsg_formatted(string value) { str_attr(_msg->longmsg_formatted, value); }
boost::python::tuple attachments();
void attachments(boost::python::list value);
time_t sent() { return timestamp_attr(_msg->sent); }
void sent(time_t value) { timestamp_attr(_msg->sent, value); }
time_t recv() { return timestamp_attr(_msg->recv); }
void recv(time_t value) { timestamp_attr(_msg->recv, value); }
Identity from() { return identity_attr(_msg->from); }
void from(object value) { identity_attr(_msg->from, value); }
boost::python::list to() { return identitylist_attr(_msg->to); }
void to(boost::python::list value) { identitylist_attr(_msg->to, value); }
Identity recv_by() { return identity_attr(_msg->recv_by); }
void recv_by(object value) { identity_attr(_msg->recv_by, value); }
boost::python::list cc() { return identitylist_attr(_msg->cc); }
void cc(boost::python::list value) { identitylist_attr(_msg->cc, value); }
boost::python::list bcc() { return identitylist_attr(_msg->bcc); }
void bcc(boost::python::list value) { identitylist_attr(_msg->bcc, value); }
boost::python::list reply_to() { return identitylist_attr(_msg->reply_to); }
void reply_to(boost::python::list value) { identitylist_attr(_msg->reply_to, value); }
boost::python::list in_reply_to() { return strlist_attr(_msg->in_reply_to); }
void in_reply_to(boost::python::list value) { strlist_attr(_msg->in_reply_to, value); }
boost::python::list references() { return strlist_attr(_msg->references); }
void references(boost::python::list value) { strlist_attr(_msg->references, value); }
boost::python::list keywords() { return strlist_attr(_msg->keywords); }
void keywords(boost::python::list value) { strlist_attr(_msg->keywords, value); }
string comments() { return str_attr(_msg->comments); }
void comments(string value) { str_attr(_msg->comments, value); }
dict opt_fields() { return strdict_attr(_msg->opt_fields); }
void opt_fields(dict value) { return strdict_attr(_msg->opt_fields, value); }
PEP_enc_format enc_format() { return _msg->enc_format; }
void enc_format(PEP_enc_format value) { _msg->enc_format = value; }
Message encrypt();
Message _encrypt(boost::python::list extra, int enc_format = 4, int flags = 0);
boost::python::tuple decrypt(int flags = 0);
PEP_rating outgoing_rating();
PEP_color outgoing_color();
Message deepcopy(dict &memo);
Message copy();
};
Message outgoing_message(Identity me);
Message incoming_message(string mime_text);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* MESSAGE_HH */

161
src/pEp/_pEp/message_api.cc

@ -0,0 +1,161 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// Engine
#include <pEp/pEpEngine.h>
#include <pEp/message_api.h>
#include <pEp/sync_api.h>
#include <pEp/sync_codec.h>
#include <pEp/distribution_codec.h>
// local
#include "message_api.hh"
#include "basic_api.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
Message encrypt_message(Message src, boost::python::list extra, int enc_format, int flags) {
Identity _from = src.from();
if (_from.address() == "")
throw invalid_argument("encrypt_message: src.from_.address empty");
if (_from.username() == "")
throw invalid_argument("encrypt_message: src.from_.username empty");
if (_from.user_id() == "")
src.from().user_id(_from.address());
stringlist_t *_extra = to_stringlist(extra);
PEP_enc_format _enc_format = (PEP_enc_format) enc_format;
PEP_encrypt_flags_t _flags = (PEP_encrypt_flags_t) flags;
message *_dst = NULL;
message *_src = src;
PEP_STATUS status = encrypt_message(Adapter::session(), _src, _extra, &_dst,
_enc_format, _flags);
free_stringlist(_extra);
_throw_status(status);
if (!_dst || _dst == _src)
return Message(_src);
return Message(_dst);
}
boost::python::tuple decrypt_message(Message src, int flags) {
message *_dst = NULL;
stringlist_t *_keylist = NULL;
PEP_rating _rating = PEP_rating_undefined;
PEP_decrypt_flags_t _flags = (PEP_decrypt_flags_t) flags;
message *_src = src;
PEP_STATUS status = ::decrypt_message(Adapter::session(), _src, &_dst, &_keylist,
&_rating, &_flags);
_throw_status(status);
boost::python::list keylist;
if (_keylist) {
keylist = from_stringlist(_keylist);
free_stringlist(_keylist);
}
Message dst = _dst ? Message(_dst) : Message(src);
return boost::python::make_tuple(dst, keylist, _rating, _flags);
}
PEP_color _color(int rating) {
return ::color_from_rating((PEP_rating) rating);
}
boost::python::tuple sync_decode(object buffer) {
Py_buffer src;
int result = PyObject_GetBuffer(buffer.ptr(), &src, PyBUF_CONTIG_RO);
if (result)
throw invalid_argument("need a contiguous buffer to read");
char *dst = NULL;
PEP_STATUS status = PER_to_XER_Sync_msg((char *) src.buf, src.len, &dst);
PyBuffer_Release(&src);
_throw_status(status);
string _dst(dst);
free(dst);
return boost::python::make_tuple(_dst, 0);
}
static boost::python::tuple sync_encode(string text) {
char *data = NULL;
size_t size = 0;
PEP_STATUS status = XER_to_PER_Sync_msg(text.c_str(), &data, &size);
_throw_status(status);
PyObject *ba = PyBytes_FromStringAndSize(data, size);
free(data);
if (!ba)
throw bad_alloc();
return boost::python::make_tuple(object(handle<>(ba)), 0);
}
boost::python::tuple Distribution_decode(object buffer) {
Py_buffer src;
int result = PyObject_GetBuffer(buffer.ptr(), &src, PyBUF_CONTIG_RO);
if (result)
throw invalid_argument("need a contiguous buffer to read");
char *dst = NULL;
PEP_STATUS status = PER_to_XER_Distribution_msg((char *) src.buf, src.len, &dst);
PyBuffer_Release(&src);
_throw_status(status);
string _dst(dst);
free(dst);
return boost::python::make_tuple(_dst, 0);
}
static boost::python::tuple Distribution_encode(string text) {
char *data = NULL;
size_t size = 0;
PEP_STATUS status = XER_to_PER_Distribution_msg(text.c_str(), &data, &size);
_throw_status(status);
PyObject *ba = PyBytes_FromStringAndSize(data, size);
free(data);
if (!ba)
throw bad_alloc();
return boost::python::make_tuple(object(handle<>(ba)), 0);
}
object sync_search(string name) {
if (name != "pep.sync") {
return object();
} else {
object codecs = import("codecs");
object CodecInfo = codecs.attr("CodecInfo");
object _sync_decode = make_function(sync_decode);
object _sync_encode = make_function(sync_encode);
return call<object>(CodecInfo.ptr(), _sync_encode, _sync_decode);
}
}
object distribution_search(string name) {
if (name != "pep.distribution") {
return object();
} else {
object codecs = import("codecs");
object CodecInfo = codecs.attr("CodecInfo");
object _distribution_decode = make_function(Distribution_decode);
object _distribution_encode = make_function(Distribution_encode);
return call<object>(CodecInfo.ptr(), _distribution_encode, _distribution_decode);
}
}
} // namespace PythonAdapter
} // namespace pEp {

30
src/pEp/_pEp/message_api.hh

@ -0,0 +1,30 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef MESSAGE_API_HH
#define MESSAGE_API_HH
#include "pEpmodule.hh"
namespace pEp {
namespace PythonAdapter {
Message encrypt_message(
Message src,
boost::python::list extra = boost::python::list(),
int enc_format = 4,
int flags = 0
);
boost::python::tuple decrypt_message(Message src, int flags = 0);
PEP_color _color(int rating);
object sync_search(string name);
object distribution_search(string name);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* MESSAGE_API_HH */

656
src/pEp/_pEp/pEpmodule.cc

@ -0,0 +1,656 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <boost/python.hpp>
#include <boost/locale.hpp>
#include <string>
#include <sstream>
#include <iomanip>
#include <mutex>
// Engine
#include <pEp/key_reset.h>
#include <pEp/message_api.h>
#include <pEp/sync_api.h>
#include <pEp/status_to_string.h>
// libpEpAdapter
#include <pEp/Adapter.hh>
#include <pEp/callback_dispatcher.hh>
#include <pEp/pEpLog.hh>
// local
#include "pEpmodule.hh"
#include "basic_api.hh"
#include "message_api.hh"
//#include "user_interface.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
static const char *version_string = "p≡p Python adapter version 0.3";
void init_before_main_module() {
pEpLog("called");
}
// hidden init function, wrapped by hello_world.init()
void _init_after_main_module() {
pEpLog("called");
callback_dispatcher.add(_messageToSend, notifyHandshake, nullptr, nullptr);
Adapter::_messageToSend = CallbackDispatcher::messageToSend;
}
void config_passive_mode(bool enable) {
::config_passive_mode(Adapter::session(), enable);
}
void config_unencrypted_subject(bool enable) {
::config_unencrypted_subject(Adapter::session(), enable);
}
void key_reset_user(string user_id, string fpr) {
if (user_id == "")
throw invalid_argument("user_id required");
PEP_STATUS status = ::key_reset_user(Adapter::session(),
user_id.c_str(), fpr != "" ? fpr.c_str() : nullptr);
_throw_status(status);
}
void key_reset_user2(string user_id) {
key_reset_user(user_id, "");
}
void key_reset_all_own_keys() {
PEP_STATUS status = ::key_reset_all_own_keys(Adapter::session());
_throw_status(status);
}
static string about() {
string version = string(version_string) + "\np≡p version "
+ PEP_VERSION + "\n";
return version;
}
void _throw_status(PEP_STATUS status) {
if (status == PEP_STATUS_OK)
return;
if (status >= 0x400 && status <= 0x4ff)
return;
if (status == PEP_OUT_OF_MEMORY)
throw bad_alloc();
if (status == PEP_ILLEGAL_VALUE)
throw invalid_argument("illegal value");
if (string(pEp_status_to_string(status)) == "unknown status code") {
stringstream build;
build << setfill('0') << "p≡p 0x" << setw(4) << hex << status;
throw runtime_error(build.str());
} else {
throw runtime_error(pEp_status_to_string(status));
}
}
PEP_STATUS _messageToSend(::message *msg) {
pEpLog("called");
try {
PyGILState_STATE gil = PyGILState_Ensure();
pEpLog("GIL Aquired");
object modref = import("pEp");
object funcref = modref.attr("message_to_send");
call<void>(funcref.ptr(), Message());
PyGILState_Release(gil);
pEpLog("GIL released");
} catch (exception &e) {}
return PEP_STATUS_OK;
}
PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handshake_signal signal) {
pEpLog("called");
try {
PyGILState_STATE gil = PyGILState_Ensure();
pEpLog("GIL Aquired");
object modref = import("pEp");
object funcref = modref.attr("notify_handshake");
call<void>(funcref.ptr(), me, partner, signal);
PyGILState_Release(gil);
pEpLog("GIL released");
} catch (exception &e) {}
return PEP_STATUS_OK;
}
void start_sync() {
CallbackDispatcher::start_sync();
}
void shutdown_sync() {
CallbackDispatcher::stop_sync();
}
void debug_color(int ansi_color) {
::set_debug_color(Adapter::session(), ansi_color);
}
void leave_device_group() {
::leave_device_group(Adapter::session());
}
bool is_sync_active() {
return Adapter::is_sync_running();
}
void testfunc() {
_messageToSend(NULL);
}
void deliverHandshakeResult(int result, object identities) {
identity_list *shared_identities = nullptr;
if (identities != boost::python::api::object() && boost::python::len(identities)) {
shared_identities = new_identity_list(nullptr);
if (!shared_identities)
throw bad_alloc();
try {
identity_list *si = shared_identities;
for (int i = 0; i < boost::python::len(identities); ++i) {
Identity ident = extract<Identity>(identities[i]);
si = identity_list_add(si, ident);
if (!si)
throw bad_alloc();
}
}
catch (exception &ex) {
free_identity_list(shared_identities);
throw ex;
}
}
PEP_STATUS status = ::deliverHandshakeResult(Adapter::session(), (sync_handshake_result) result, shared_identities);
free_identity_list(shared_identities);
_throw_status(status);
}
BOOST_PYTHON_MODULE(_pEp) {
init_before_main_module();
// Module init function called by pEp.init()
def("_init_after_main_module", _init_after_main_module);
def("testfunc", &testfunc);
docstring_options doc_options(true, false, false);
boost::locale::generator gen;
std::locale::global(gen(""));
scope().attr("about") = about();
scope().attr("per_user_directory") = per_user_directory();
scope().attr("per_machine_directory") = per_machine_directory();
scope().attr("engine_version") = get_engine_version();
scope().attr("protocol_version") = get_protocol_version();
def("passive_mode", config_passive_mode,
"do not attach pub keys to all messages");
def("unencrypted_subject", config_unencrypted_subject,
"do not encrypt the subject of messages");
def("key_reset", key_reset_user,
"reset the default database status for the user / keypair provided\n"
"This will effectively perform key_reset on each identity\n"
"associated with the key and user_id, if a key is provided, and for\n"
"each key (and all of their identities) if an fpr is not.");
def("key_reset", key_reset_user2,
"reset the default database status for the user / keypair provided\n"
"This will effectively perform key_reset on each identity\n"
"associated with the key and user_id, if a key is provided, and for\n"
"each key (and all of their identities) if an fpr is not.");
def("key_reset_all_own_keys", key_reset_all_own_keys,
"revoke and mistrust all own keys, generate new keys for all\n"
"own identities, and opportunistically communicate key reset\n"
"information to people we have recently contacted.");
auto identity_class = class_<Identity>("Identity",
"Identity(address, username, user_id='', fpr='', comm_type=0, lang='en')\n"
"\n"
"represents a p≡p identity\n"
"\n"
"an identity is a network address, under which a user is represented in\n"
"the network\n"
"\n"
" address network address, either an SMTP address or a URI\n"
" username real name or nickname for user\n"
" user_id ID this user is handled by the application\n"
" fpr full fingerprint of the key being used as key ID,\n"
" hex encoded\n"
" comm_type first rating level of this communication channel\n"
" lang ISO 639-1 language code for language being preferred\n"
" on this communication channel\n"
)
.def(boost::python::init<string>())
.def(boost::python::init<string, string>())
.def(boost::python::init<string, string, string>())
.def(boost::python::init<string, string, string, string>())
.def(boost::python::init<string, string, string, string, int>())
.def(boost::python::init<string, string, string, string, int, string>())
.def("__repr__", &Identity::_repr)
.def("__str__", &Identity::_str,
"string representation of this identity\n"
"following the pattern 'username < address >'\n"
)
.def("key_reset", &Identity::key_reset,
boost::python::arg("fpr")=object(""),
"reset the default database status for the identity / keypair provided. If this\n"
"corresponds to the own user and a private key, also revoke the key, generate a\n"
"new one, and communicate the reset to recently contacted pEp partners for this\n"
"identity. If it does not, remove the key from the keyring; the key's status is\n"
"completely fresh on next contact from the partner.")
.def("key_mistrusted", &Identity::key_mistrusted,
boost::python::arg("fpr")=object(""),
"If you want updated trust on the identity, you ll have"
"to call update_identity or myself respectively after this."
"N.B. If you are calling this on a key that is the identity or user default,"
"it will be removed as the default key for ANY identity and user for which"
"it is the default. Please keep in mind that the undo in undo_last_mistrust"
"will only undo the current identity's / it's user's default, not any"
"other identities which may be impacted (this will not affect most use cases)")
.def("enable_for_sync", &Identity::enable_for_sync,
"Enable own identity for p≡p sync.\n\n"
"Only use this on own identities, which are used as accounts.\n")
.def("disable_for_sync", &Identity::disable_for_sync,
"Disable own identity for p≡p sync.\n\n"
"Only use this on own identities, which are used as accounts.\n")
.add_property("address", (string(Identity::*)()) &Identity::address,
(void(Identity::*)(string)) &Identity::address,
"email address or URI")
.add_property("fpr", (string(Identity::*)()) &Identity::fpr,
(void(Identity::*)(string)) &Identity::fpr,
"key ID (full fingerprint, hex encoded)")
.add_property("user_id", (string(Identity::*)()) &Identity::user_id,
(void(Identity::*)(string)) &Identity::user_id,
"ID of person associated or 'pEp_own_userId' if own identity")
.add_property("username", (string(Identity::*)()) &Identity::username,
(void(Identity::*)(string)) &Identity::username,
"name in full of person associated")
.add_property("comm_type", (int(Identity::*)())
(PEP_comm_type(Identity::*)()) &Identity::comm_type,
(void(Identity::*)(int))
(void(Identity::*)(PEP_comm_type)) &Identity::comm_type,
"communication type, first rating level (p≡p internal)")
.add_property("lang", (string(Identity::*)()) &Identity::lang,
(void(Identity::*)(string)) &Identity::lang,
"ISO 639-1 language code")
.add_property("flags", (identity_flags_t(Identity::*)()) &Identity::flags,
(void(Identity::*)(identity_flags_t)) &Identity::flags,
"flags (p≡p internal)")
.add_property("rating", &Identity::rating, "rating of Identity")
.add_property("color", &Identity::color, "color of Identity as PEP_color")
.add_property("is_pEp_user", &Identity::is_pEp_user, "True if this is an identity of a pEp user")
.def("__deepcopy__", &Identity::deepcopy)
.def("update", &Identity::update, "update Identity")
.def("__copy__", &Identity::copy);
identity_class.attr("PEP_OWN_USERID") = "pEp_own_userId";
auto blob_class = class_<Message::Blob>("Blob",
"Blob(data, mime_type='', filename='')\n"
"\n"
"Binary large object\n"
"\n"
" data bytes-like object\n"
" mime_type MIME type for the data\n"
" filename filename to store the data\n",
boost::python::init< object, char const*, char const* >(args("data", "mime_type", "filename")))
.def(boost::python::init<object, string>())
.def(boost::python::init<object>())
.def("__repr__", &Message::Blob::_repr)
.def("__len__", &Message::Blob::size, "size of Blob data in bytes")
.def("decode", (string(Message::Blob::*)()) &Message::Blob::decode)
.def("decode", (string(Message::Blob::*)(string)) &Message::Blob::decode,
"text = blob.decode(encoding='')\n"
"\n"
"decode Blob data into string depending on MIME type if encoding=''\n"
"\n"
" mime_type='application/pEp.sync' decode as 'pEp.sync'\n"
" mime_type='application/pEp.keyreset' decode as 'pEp.keyreset'\n"
" other mime_type decode as 'ascii' by default\n"
)
.add_property("mime_type", (string(Message::Blob::*)()) &Message::Blob::mime_type,
(void(Message::Blob::*)(string)) &Message::Blob::mime_type,
"MIME type of object in Blob")
.add_property("filename", (string(Message::Blob::*)()) &Message::Blob::filename,
(void(Message::Blob::*)(string)) &Message::Blob::filename,
"filename of object in Blob");
((PyTypeObject *)(void *)blob_class.ptr())->tp_as_buffer = &Message::Blob::bp;
auto message_class = class_<Message>("Message",
"Message(dir=1, from=None)\n"
"\n"
"new p≡p message\n"
"\n"
" dir 1 for outgoing, 2 for incoming\n"
" from Identity() of sender\n"
"\n"
"Message(mime_text)\n"
"\n"
"new incoming p≡p message\n"
"\n"
" mime_text text in Multipurpose Internet Mail Extensions format\n"
)
.def(boost::python::init<int>())
.def(boost::python::init<int, Identity *>())
.def(boost::python::init<string>())
.def("__str__", &Message::_str,
"the string representation of a Message is it's MIME text"
)
.def("__repr__", &Message::_repr)
.add_property("dir", (int(Message::*)())
(PEP_msg_direction(Message::*)()) &Message::dir,
(void(Message::*)(int))
(void(Message::*)(PEP_msg_direction)) &Message::dir,
"0: incoming, 1: outgoing message")
.add_property("id", (string(Message::*)()) &Message::id,
(void(Message::*)(string)) &Message::id,
"message ID")
.add_property("shortmsg", (string(Message::*)()) &Message::shortmsg,
(void(Message::*)(string)) &Message::shortmsg,
"subject or short message")
.add_property("longmsg", (string(Message::*)()) &Message::longmsg,
(void(Message::*)(string)) &Message::longmsg,
"body or long version of message")
.add_property("longmsg_formatted", (string(Message::*)()) &Message::longmsg_formatted,
(void(Message::*)(string)) &Message::longmsg_formatted,
"HTML body or fromatted long version of message")
.add_property("attachments", (boost::python::tuple(Message::*)()) &Message::attachments,
(void(Message::*)(boost::python::list)) &Message::attachments,
"tuple of Blobs with attachments; setting moves Blobs to attachment tuple")
.add_property("sent", (time_t(Message::*)()) &Message::sent,
(void(Message::*)(time_t)) &Message::sent,
"time when message was sent in UTC seconds since epoch")
.add_property("recv", (time_t(Message::*)()) &Message::recv,
(void(Message::*)(time_t)) &Message::recv,
"time when message was received in UTC seconds since epoch")
.add_property("from_", (Identity(Message::*)()) &Message::from,
(void(Message::*)(object)) &Message::from,
"identity where message is from")
.add_property("to", (boost::python::list(Message::*)()) &Message::to,
(void(Message::*)(boost::python::list)) &Message::to,
"list of identities message is going to")
.add_property("recv_by", (Identity(Message::*)()) &Message::recv_by,
(void(Message::*)(object)) &Message::recv_by,
"identity where message was received by")
.add_property("cc", (boost::python::list(Message::*)()) &Message::cc,
(void(Message::*)(boost::python::list)) &Message::cc,
"list of identities message is going cc")
.add_property("bcc", (boost::python::list(Message::*)()) &Message::bcc,
(void(Message::*)(boost::python::list)) &Message::bcc,
"list of identities message is going bcc")
.add_property("reply_to", (boost::python::list(Message::*)()) &Message::reply_to,
(void(Message::*)(boost::python::list)) &Message::reply_to,
"list of identities where message will be replied to")
.add_property("in_reply_to", (boost::python::list(Message::*)()) &Message::in_reply_to,
(void(Message::*)(boost::python::list)) &Message::in_reply_to,
"in_reply_to list")
.add_property("references", (boost::python::list(Message::*)()) &Message::references,
(void(Message::*)(boost::python::list)) &Message::references,
"message IDs of messages this one is referring to")
.add_property("keywords", (boost::python::list(Message::*)()) &Message::keywords,
(void(Message::*)(boost::python::list)) &Message::keywords,
"keywords this message should be stored under")
.add_property("comments", (string(Message::*)()) &Message::comments,
(void(Message::*)(string)) &Message::comments,
"comments added to message")
.add_property("opt_fields", (dict(Message::*)()) &Message::opt_fields,
(void(Message::*)(dict)) &Message::opt_fields,
"opt_fields of message")
.add_property("enc_format", (int(Message::*)())
(PEP_enc_format(Message::*)()) &Message::enc_format,
(void(Message::*)(int))
(void(Message::*)(PEP_enc_format)) &Message::enc_format,
"0: unencrypted, 1: inline PGP, 2: S/MIME, 3: PGP/MIME, 4: p≡p format")
.def("encrypt", (Message(Message::*)())&Message::encrypt)
.def("encrypt", (Message(Message::*)(boost::python::list))&Message::_encrypt)
.def("encrypt", (Message(Message::*)(boost::python::list, int))&Message::_encrypt)
.def("encrypt", (Message(Message::*)(boost::python::list, int, int))&Message::_encrypt,
"msg2 = msg1.encrypt(extra_keys=[], enc_format='pEp', flags=0)\n"
"\n"
"encrypts a p≡p message and returns the encrypted message\n"
"\n"
" extra_keys list of strings with fingerprints for extra keys to use\n"
" for encryption\n"
" enc_format 0 for none, 1 for partitioned, 2 for S/MIME,\n"
" 3 for PGP/MIME, 4 for pEp\n"
" flags 1 is force encryption\n"
)
.def("decrypt", &Message::decrypt, boost::python::arg("flags")=0,
"msg2, keys, rating, flags = msg1.decrypt()\n"
"\n"
"decrypts a p≡p message and returns a tuple with data\n"
"\n"
" msg the decrypted p≡p message\n"
" keys a list of keys being used\n"
" rating the rating of the message as integer\n"
" flags flags set while decryption\n"
)
.add_property("outgoing_rating", &Message::outgoing_rating, "rating outgoing message will have")
.add_property("outgoing_color", &Message::outgoing_color, "color outgoing message will have as PEP_color")
.def("__deepcopy__", &Message::deepcopy)
.def("__copy__", &Message::copy);
// basic API and key management API
def("update_identity", &update_identity,
"update_identity(ident)\n"
"\n"
"update identity information\n"
"call this to complete identity information when you at least have an address\n"
);
def("myself", &myself,
"myself(ident)\n"
"\n"
"ensures that the own identity is being complete\n"
"supply ident.address and ident.username\n"
);
def("trust_personal_key", &trust_personal_key,
"trust_personal_key(ident)\n"
"\n"
"mark a key as trusted with a person\n"
);
enum_<identity_flags>("identity_flags")
.value("PEP_idf_not_for_sync", PEP_idf_not_for_sync)
.value("PEP_idf_list", PEP_idf_list)
.value("PEP_idf_devicegroup", PEP_idf_devicegroup);
def("set_identity_flags", &set_identity_flags,
"set_identity_flags(ident, flags)\n"
"\n"
"set identity flags\n"
);
def("unset_identity_flags", &unset_identity_flags,
"unset_identity_flags(ident, flags)\n"
"\n"
"unset identity flags\n"
);
def("key_reset_trust", &key_reset_trust,
"key_reset_trust(ident)\n"
"\n"
"reset trust bit or explicitly mistrusted status for an identity and "
"its accompanying key/user_id pair\n"
);
def("import_key", &import_key,
"private_key_list = import_key(key_data)\n"
"\n"
"import key(s) from key_data\n"
);
def("export_key", &export_key,
"key_data = export_key(identity)\n"
"\n"
"export key(s) of identity\n"
);
def("export_secret_key", &export_secret_key,
"key_data = export_seret_key(identity)\n"
"\n"
"export secret key(s) of identity\n"
);
def("set_own_key", &set_own_key,
"set_own_key(me, fpr)\n"
"\n"
"mark a key as an own key, and make it the default key\n"
"\n"
"me Own identity for which to add the existing key\n"
"fpr The fingerprint of the key to be added\n"
"\n"
"me->address, me->user_id and me->username must be set to valid data\n"
"myself() is called by set_own_key() without key generation\n"
"me->flags are ignored\n"
"me->address must not be an alias\n"
"me->fpr will be ignored and replaced by fpr\n"
);
// message API
enum_<PEP_rating>("rating")
.value("_undefined", PEP_rating_undefined)
.value("cannot_decrypt", PEP_rating_cannot_decrypt)
.value("have_no_key", PEP_rating_have_no_key)
.value("unencrypted", PEP_rating_unencrypted)
.value("unreliable", PEP_rating_unreliable)
.value("reliable", PEP_rating_reliable)
.value("trusted", PEP_rating_trusted)
.value("trusted_and_anonymized", PEP_rating_trusted_and_anonymized)
.value("fully_anonymous", PEP_rating_fully_anonymous)
.value("mistrust", PEP_rating_mistrust)
.value("b0rken", PEP_rating_b0rken)
.value("under_attack", PEP_rating_under_attack);
enum_<PEP_color>("colorvalue")
.value("no_color", PEP_color_no_color)
.value("yellow", PEP_color_yellow)
.value("green", PEP_color_green)
.value("red", PEP_color_red);
def("incoming_message", &incoming_message,
"msg = incoming_message(mime_text)\n"
"\n"
"create an incoming message from a MIME text"
);
def("outgoing_message", &outgoing_message,
"msg = outgoing_message(ident)\n"
"\n"
"create an outgoing message using an own identity"
);
def("color", &_color,
"c = color(rating)\n"
"\n"
"calculate color value out of rating. Returns PEP_color"
);
def("trustwords", &_trustwords,
"text = trustwords(ident_own, ident_partner)\n"
"\n"
"calculate trustwords for two Identities");
// Sync API
enum_<sync_handshake_signal>("sync_handshake_signal")
.value("SYNC_NOTIFY_UNDEFINED", SYNC_NOTIFY_UNDEFINED)
.value("SYNC_NOTIFY_INIT_ADD_OUR_DEVICE", SYNC_NOTIFY_INIT_ADD_OUR_DEVICE)
.value("SYNC_NOTIFY_INIT_ADD_OTHER_DEVICE", SYNC_NOTIFY_INIT_ADD_OTHER_DEVICE)
.value("SYNC_NOTIFY_INIT_FORM_GROUP", SYNC_NOTIFY_INIT_FORM_GROUP)
.value("SYNC_NOTIFY_TIMEOUT", SYNC_NOTIFY_TIMEOUT)
.value("SYNC_NOTIFY_ACCEPTED_DEVICE_ADDED", SYNC_NOTIFY_ACCEPTED_DEVICE_ADDED)
.value("SYNC_NOTIFY_ACCEPTED_GROUP_CREATED", SYNC_NOTIFY_ACCEPTED_GROUP_CREATED)
.value("SYNC_NOTIFY_ACCEPTED_DEVICE_ACCEPTED", SYNC_NOTIFY_ACCEPTED_DEVICE_ACCEPTED)
.value("SYNC_NOTIFY_SOLE", SYNC_NOTIFY_SOLE)
.value("SYNC_NOTIFY_IN_GROUP", SYNC_NOTIFY_IN_GROUP);
// auto user_interface_class = class_<UserInterface, UserInterface_callback, boost::noncopyable>(
// "UserInterface",
// "class MyUserInterface(UserInterface):\n"
// " def notifyHandshake(self, me, partner):\n"
// " ...\n"
// "\n"
// "p≡p User Interface class\n"
// "To be used as a mixin\n"
// )
// .def("notifyHandshake", &UserInterface::notifyHandshake,
// "notifyHandshake(self, me, partner)\n"
// "\n"
// " me own identity\n"
// " partner identity of communication partner\n"
// "\n"
// "overwrite this method with an implementation of a handshake dialog")
// .def("deliverHandshakeResult", &UserInterface::deliverHandshakeResult,
// boost::python::arg("identities")=object(),
// "deliverHandshakeResult(self, result, identities=None)\n"
// "\n"
// " result -1: cancel, 0: accepted, 1: rejected\n"
// " identities list of identities to share or None for all\n"
// "\n"
// "call to deliver the handshake result of the handshake dialog"
// );
def("deliver_handshake_result", &deliverHandshakeResult, boost::python::arg("identities")=object(),
"deliverHandshakeResult(self, result, identities=None)\n"
"\n"
" result -1: cancel, 0: accepted, 1: rejected\n"
" identities list of identities to share or None for all\n"
"\n"
"call to deliver the handshake result of the handshake dialog"
);
def("start_sync", &start_sync,
"start_sync()\n"
"\n"
"starts the sync thread"
);
def("shutdown_sync", &shutdown_sync,
"shutdown_sync()\n"
"\n"
"call this from another thread to shut down the sync thread\n"
);
def("debug_color", &debug_color,
"for debug builds set ANSI color value");
def("leave_device_group", &leave_device_group,
"leave_device_group()\n"
"\n"
"call this for a grouped device, which should leave\n"
);
def("is_sync_active", &is_sync_active,
"is_sync_active()\n"
"\n"
"True if sync is active, False otherwise\n"
);
// codecs
call< object >(((object)(import("codecs").attr("register"))).ptr(), make_function(sync_search));
call< object >(((object)(import("codecs").attr("register"))).ptr(), make_function(distribution_search));
}
} // namespace PythonAdapter
} // namespace pEp

35
src/pEp/_pEp/pEpmodule.hh

@ -0,0 +1,35 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef PEPMODULE_HH
#define PEPMODULE_HH
// Engine
#include <pEp/pEpEngine.h>
// local
#include "message.hh"
namespace pEp {
namespace PythonAdapter {
extern string device_name;
void config_passive_mode(bool enable);
void config_unencrypted_subject(bool enable);
void key_reset_user(string user_id, string fpr);
void key_reset_all_own_keys();
void _throw_status(PEP_STATUS status);
PEP_STATUS _messageToSend(::message *msg);
PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handshake_signal signal);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* PEPMODULE_HH */

172
src/pEp/_pEp/str_attr.cc

@ -0,0 +1,172 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <cstdlib>
#include <boost/python.hpp>
#include <boost/locale.hpp>
// local
#include "str_attr.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
using namespace boost::locale;
object repr(object s) {
return s.attr("__repr__")();
}
string repr(string s) {
str _s = s.c_str();
object _r = _s.attr("__repr__")();
string r = extract<string>(_r);
return r;
}
string str_attr(char *&str) {
if (!str)
return string("");
return string(str);
}
void str_attr(char *&str, string value) {
string normalized = normalize(value, norm_nfc);
free(str);
str = strdup(normalized.c_str());
if (!str)
throw bad_alloc();
}
time_t timestamp_attr(timestamp *&ts) {
if (!ts)
return 0;
return timegm(ts);
}
void timestamp_attr(timestamp *&ts, time_t value) {
free_timestamp(ts);
ts = new_timestamp(value);
}
boost::python::list strlist_attr(stringlist_t *&sl) {
boost::python::list result;
for (stringlist_t *_sl = sl; _sl && _sl->value; _sl = _sl->next) {
string s(_sl->value);
result.append(object(s));
}
return result;
}
void strlist_attr(stringlist_t *&sl, boost::python::list value) {
stringlist_t *_sl = new_stringlist(NULL);
if (!_sl)
throw bad_alloc();
stringlist_t *_s = _sl;
for (int i = 0; i < len(value); i++) {
extract <string> extract_string(value[i]);
if (!extract_string.check()) {
free_stringlist(_sl);
}
string s = extract_string();
s = normalize(s, norm_nfc);
_s = stringlist_add(_s, s.c_str());
if (!_s) {
free_stringlist(_sl);
throw bad_alloc();
}
}
free_stringlist(sl);
sl = _sl;
}
dict strdict_attr(stringpair_list_t *&spl) {
dict result;
for (stringpair_list_t *_spl = spl; _spl && _spl->value; _spl =
_spl->next) {
stringpair_t *p = _spl->value;
if (p->key && p->value) {
string key(p->key);
string value(p->value);
result[key] = value;
}
}
return result;
}
void strdict_attr(stringpair_list_t *&spl, dict value) {
stringpair_list_t *_spl = new_stringpair_list(NULL);
if (!_spl)
throw bad_alloc();
stringpair_list_t *_s = _spl;
for (int i = 0; i < len(value); i++) {
extract <string> extract_key(value.keys()[i]);
extract <string> extract_value(value.values()[i]);
if (!(extract_key.check() && extract_value.check()))
free_stringpair_list(_spl);
string key = extract_key();
key = normalize(key, norm_nfc);
string _value = extract_value();
_value = normalize(_value, norm_nfc);
stringpair_t *pair = new_stringpair(key.c_str(), _value.c_str());
if (!pair) {
free_stringpair_list(_spl);
throw bad_alloc();
}
_s = stringpair_list_add(_s, pair);
if (!_s) {
free_stringpair_list(_spl);
throw bad_alloc();
}
}
free_stringpair_list(spl);
spl = _spl;
}
stringlist_t *to_stringlist(boost::python::list l) {
stringlist_t *result = new_stringlist(NULL);
if (!result)
throw bad_alloc();
stringlist_t *_s = result;
for (int i = 0; i < len(l); i++) {
extract <string> extract_string(l[i]);
if (!extract_string.check())
free_stringlist(result);
string s = extract_string();
_s = stringlist_add(_s, s.c_str());
if (!_s) {
free_stringlist(result);
throw bad_alloc();
}
}
return result;
}
boost::python::list from_stringlist(const stringlist_t *sl) {
boost::python::list result;
for (const stringlist_t *_sl = sl; _sl && _sl->value; _sl = _sl->next) {
string s = _sl->value;
result.append(s);
}
return result;
}
} // namespace PythonAdapter
} // namespace pEp {

0
src/pEp/native_pEp/str_attr.hh → src/pEp/_pEp/str_attr.hh

123
src/pEp/_pEp/user_interface.cc

@ -0,0 +1,123 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <cassert>
// local
#include "user_interface.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
UserInterface *UserInterface::_ui = nullptr;
UserInterface::UserInterface() {
if (_ui)
throw runtime_error("only one UserInterface thread allowed");
_ui = this;
}
UserInterface::~UserInterface() {
_ui = nullptr;
}
UserInterface_callback::UserInterface_callback(PyObject *self) :
UserInterface(), _self(self) {
// adapter.ui_object(self);
// PEP_STATUS status = ::register_sync_callbacks(Adapter::session(),
// (void *) this, _notifyHandshake, retrieve_next_sync_event);
// assert(status == PEP_STATUS_OK);
// if (status)
// _throw_status(status);
}
UserInterface_callback::~UserInterface_callback() {
// ::unregister_sync_callbacks(Adapter::session());
}
PEP_STATUS UserInterface::_notifyHandshake(
pEp_identity *me, pEp_identity *partner,
sync_handshake_signal signal
) {
if (!(me && partner))
return PEP_ILLEGAL_VALUE;
auto that = dynamic_cast< UserInterface_callback * >(_ui);
that->notifyHandshake(Identity(me), Identity(partner), signal);
return PEP_STATUS_OK;
}
void UserInterface::deliverHandshakeResult(int result, object identities) {
identity_list *shared_identities = nullptr;
if (identities != boost::python::api::object() && boost::python::len(identities)) {
shared_identities = new_identity_list(nullptr);
if (!shared_identities)
throw bad_alloc();
try {
identity_list *si = shared_identities;
for (int i = 0; i < boost::python::len(identities); ++i) {
Identity ident = extract<Identity>(identities[i]);
si = identity_list_add(si, ident);
if (!si)
throw bad_alloc();
}
}
catch (exception &ex) {
free_identity_list(shared_identities);
throw ex;
}
}
PEP_STATUS status = ::deliverHandshakeResult(Adapter::session(),
(sync_handshake_result) result, shared_identities);
free_identity_list(shared_identities);
_throw_status(status);
}
//PEP_rating UserInterface::get_key_rating_for_user(string user_id, string fpr)
//{
// PEP_rating result;
// PEP_STATUS status =
// ::get_key_rating_for_user(Adapter::session(),
// user_id.c_str(), fpr.c_str(), &result);
// _throw_status(status);
// return result;
//}
//SYNC_EVENT UserInterface::retrieve_next_sync_event(void *management, unsigned threshold)
//{
// time_t started = time(nullptr);
// bool timeout = false;
//
// while (adapter.queue().empty()) {
// int i = 0;
// ++i;
// if (i > 10) {
// if (time(nullptr) > started + threshold) {
// timeout = true;
// break;
// }
// i = 0;
// }
// nanosleep((const struct timespec[]){{0, 100000000L}}, NULL);
// }
//
// if (timeout)
// return new_sync_timeout_event();
//
// return adapter.queue().pop_front();
//}
void UserInterface_callback::notifyHandshake(
Identity me, Identity partner, sync_handshake_signal signal) {
call_method<void>(_self, "notifyHandshake", me, partner, signal);
}
} // namespace PythonAdapter
} // namespace pEp {

61
src/pEp/_pEp/user_interface.hh

@ -0,0 +1,61 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef USER_INTERFACE_HH
#define USER_INTERFACE_HH
// System
#include <csetjmp>
// Engine
#include <pEp/sync_api.h>
#include <pEp/message_api.h>
// local
#include "pEpmodule.hh"
namespace pEp {
namespace PythonAdapter {
class UserInterface {
static UserInterface *_ui;
public:
UserInterface();
virtual ~UserInterface();
virtual void notifyHandshake(
Identity me,
Identity partner,
sync_handshake_signal signal) {
throw runtime_error("override this method");
}
virtual void deliverHandshakeResult(int result, object identities);
// PEP_rating get_key_rating_for_user(string user_id, string fpr);
protected:
static PEP_STATUS _notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handshake_signal signal);
};
class UserInterface_callback : public UserInterface {
PyObject *_self;
public:
UserInterface_callback(PyObject *self);
~UserInterface_callback();
void notifyHandshake(
Identity me,
Identity partner,
sync_handshake_signal signal
);
};
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* USER_INTERFACE_HH */

172
src/pEp/native_pEp/basic_api.cc

@ -1,172 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <sstream>
// Engine
#include <pEp/keymanagement.h>
#include <pEp/message_api.h>
#include <pEp/Adapter.hh>
// local
#include "basic_api.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
void update_identity(Identity& ident)
{
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.user_id() == PEP_OWN_USERID)
throw runtime_error("update_identity: '" PEP_OWN_USERID
"' may only be used for own identities");
PEP_STATUS status = update_identity(Adapter::session(), ident);
_throw_status(status);
}
void myself(Identity& ident)
{
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.username() == "")
throw invalid_argument("username needed");
if (ident.user_id() == "")
ident.user_id(ident.address());
PEP_STATUS status = myself(Adapter::session(), ident);
_throw_status(status);
}
string _trustwords(Identity me, Identity partner, string lang, bool full)
{
if (me.fpr() == "" || partner.fpr() == "")
throw invalid_argument("fingerprint needed in Identities");
if (lang == "" && me.lang() == partner.lang())
lang = me.lang();
char *words = NULL;
size_t size = 0;
PEP_STATUS status = get_trustwords(Adapter::session(), me, partner,
lang.c_str(),&words, &size, full);
_throw_status(status);
return words;
}
void trust_personal_key(Identity ident)
{
if (ident.fpr() == "")
throw invalid_argument("fingerprint needed in Identities");
if (ident.user_id() == "")
throw invalid_argument("user_id must be provided");
PEP_STATUS status = trust_personal_key(Adapter::session(), ident);
_throw_status(status);
}
void set_identity_flags(Identity ident, identity_flags_t flags)
{
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.user_id() == "")
throw invalid_argument("user_id needed");
PEP_STATUS status = set_identity_flags(Adapter::session(), ident, flags);
_throw_status(status);
}
void unset_identity_flags(Identity ident, identity_flags_t flags)
{
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.user_id() == "")
throw invalid_argument("user_id needed");
PEP_STATUS status = unset_identity_flags(Adapter::session(), ident, flags);
_throw_status(status);
}
void key_reset_trust(Identity ident)
{
if (ident.fpr() == "")
throw invalid_argument("fpr needed");
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.user_id() == "")
throw invalid_argument("user_id needed");
PEP_STATUS status = key_reset_trust(Adapter::session(), ident);
_throw_status(status);
}
boost::python::list import_key(string key_data)
{
::identity_list *private_keys = NULL;
PEP_STATUS status = ::import_key(Adapter::session(), key_data.c_str(), key_data.size(), &private_keys);
if (status && status != PEP_KEY_IMPORTED)
_throw_status(status);
auto result = boost::python::list();
for (::identity_list *il = private_keys; il && il->ident; il=il->next) {
::pEp_identity *ident = ::identity_dup(il->ident);
if (!ident) {
free_identity_list(private_keys);
throw bad_alloc();
}
result.append(Identity(ident));
}
free_identity_list(private_keys);
return result;
}
string export_key(Identity ident)
{
PEP_STATUS status = PEP_STATUS_OK;
char* key_data = NULL;
size_t size;
status = ::export_key(Adapter::session(), ident.fpr().c_str(), &key_data, &size);
_throw_status(status);
return key_data;
}
string export_secret_key(Identity ident)
{
PEP_STATUS status = PEP_STATUS_OK;
char* key_data = NULL;
size_t size;
status = ::export_secret_key(Adapter::session(), ident.fpr().c_str(), &key_data, &size);
_throw_status(status);
return key_data;
}
void set_own_key(Identity& ident, string fpr)
{
if (ident.address() == "")
throw invalid_argument("address needed");
if (ident.username() == "")
throw invalid_argument("username needed");
if (ident.user_id() == "")
throw invalid_argument("user_id needed");
if (fpr == "")
throw invalid_argument("fpr needed");
const char* fpr_c = fpr.c_str();
PEP_STATUS status = set_own_key(Adapter::session(), ident, fpr_c);
_throw_status(status);
}
} // namespace PythonAdapter
} // namespace pEp {

30
src/pEp/native_pEp/basic_api.hh

@ -1,30 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef BASIC_API_HH
#define BASIC_API_HH
#include "pEpmodule.hh"
namespace pEp {
namespace PythonAdapter {
void update_identity(Identity& ident);
void myself(Identity& ident);
string _trustwords(Identity me, Identity partner, string lang, bool full);
void trust_personal_key(Identity ident);
void set_identity_flags(Identity ident, identity_flags_t flags);
void unset_identity_flags(Identity ident, identity_flags_t flags);
void key_reset_trust(Identity ident);
boost::python::list import_key(string key_data);
string export_key(Identity ident);
string export_secret_key(Identity ident);
void set_own_key(Identity& ident, string fpr);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* BASIC_API_HH */

285
src/pEp/native_pEp/identity.cc

@ -1,285 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <typeinfo>
#include <sstream>
// Engine
#include <pEp/identity_list.h>
#include <pEp/keymanagement.h>
#include <pEp/key_reset.h>
// local
#include "identity.hh"
#include "pEpmodule.hh"
#include "basic_api.hh"
#include "message_api.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
Identity::Identity(string address, string username, string user_id,
string fpr, int comm_type, string lang, identity_flags_t flags)
: _ident(new_identity(address.c_str(), fpr.c_str(), user_id.c_str(),
username.c_str()), &::free_identity)
{
if (!_ident)
throw bad_alloc();
_ident->comm_type = (PEP_comm_type) comm_type;
_ident->flags = (identity_flags_t) flags;
this->lang(lang);
}
Identity::Identity(const Identity& second)
: _ident(second._ident)
{
}
Identity::Identity(pEp_identity *ident)
: _ident(ident, &::free_identity)
{
}
Identity::~Identity()
{
}
Identity::operator pEp_identity *()
{
return _ident.get();
}
Identity::operator const pEp_identity *() const
{
return _ident.get();
}
string Identity::_repr()
{
stringstream build;
build << "Identity(";
string address;
if (_ident->address)
address = string(_ident->address);
build << repr(address) << ", ";
string username;
if (_ident->username)
username = string(_ident->username);
build << repr(username) << ", ";
string user_id;
if (_ident->user_id)
user_id = string(_ident->user_id);
build << repr(user_id) << ", ";
string fpr;
if (_ident->fpr)
fpr = string(_ident->fpr);
build << repr(fpr) << ", ";
build << (int) _ident->comm_type << ", ";
string lang = _ident->lang;
build << repr(lang) << ")";
return build.str();
}
string Identity::_str()
{
if (!(_ident->address && _ident->address[0]))
return "";
if (!(_ident->username && _ident->username[0]))
return _ident->address;
return string(_ident->username) + " <" + _ident->address + ">";
}
void Identity::username(string value)
{
if (value.length() && value.length() < 5)
throw length_error("username must be at least 5 characters");
str_attr(_ident->username, value);
}
void Identity::lang(string value)
{
if (value == "")
memset(_ident->lang, 0, 3);
else if (value.length() != 2)
throw length_error("length of lang must be 2");
else
memcpy(_ident->lang, value.c_str(), 3);
}
string Identity::lang()
{
return _ident->lang;
}
int Identity::rating()
{
if (!(_ident->address))
throw invalid_argument("address must be given");
PEP_rating rating = PEP_rating_undefined;
PEP_STATUS status = ::identity_rating(Adapter::session(), _ident.get(), &rating);
_throw_status(status);
return (int) rating;
}
PEP_color Identity::color()
{
return _color(rating());
}
Identity Identity::copy()
{
pEp_identity *dup = ::identity_dup(*this);
if (!dup)
throw bad_alloc();
return Identity(dup);
}
Identity Identity::deepcopy(dict&)
{
return copy();
}
void Identity::update()
{
update_identity(*this);
}
void Identity::key_reset(string fpr)
{
PEP_STATUS status = ::key_reset_identity(Adapter::session(), *this,
fpr != "" ? fpr.c_str() : nullptr);
_throw_status(status);
}
void Identity::key_mistrusted()
{
PEP_STATUS status = ::key_mistrusted(Adapter::session(), *this);
_throw_status(status);
}
bool Identity::is_pEp_user()
{
bool result;
PEP_STATUS status = ::is_pEp_user(Adapter::session(), *this, &result);
_throw_status(status);
return result;
}
void Identity::enable_for_sync()
{
PEP_STATUS status = ::enable_identity_for_sync(Adapter::session(), *this);
_throw_status(status);
}
void Identity::disable_for_sync()
{
PEP_STATUS status = ::disable_identity_for_sync(Adapter::session(), *this);
_throw_status(status);
}
Myself::Myself(string address, string username, string user_id, string lang)
: Identity(address, username, user_id, "", 0, lang)
{
if (!(address.length() && username.length()))
throw invalid_argument("address and username must be set");
if (lang.length() && lang.length() != 2)
throw length_error("lang must be an ISO 639-1 language code or empty");
// FIXME: should set .me
// _ident->me = true;
if (user_id.length())
throw runtime_error("user_id feature not yet implemented for Myself");
}
void Myself::update()
{
pEp::PythonAdapter::myself(*this);
}
Identity identity_attr(pEp_identity *&ident)
{
if (!ident)
throw out_of_range("no identity assigned");
pEp_identity *_dup = identity_dup(ident);
if (!_dup)
throw bad_alloc();
Identity _ident(_dup);
return _ident;
}
void identity_attr(pEp_identity *&ident, object value)
{
Identity& _ident = extract< Identity& >(value);
pEp_identity *_dup = ::identity_dup(_ident);
if (!_dup)
throw bad_alloc();
PEP_STATUS status = update_identity(Adapter::session(), _dup);
_throw_status(status);
free_identity(ident);
ident = _dup;
}
boost::python::list identitylist_attr(identity_list *&il)
{
boost::python::list result;
for (identity_list *_il = il; _il && _il->ident; _il = _il->next) {
pEp_identity *ident = ::identity_dup(_il->ident);
if (!ident)
throw bad_alloc();
result.append(object(Identity(ident)));
}
return result;
}
void identitylist_attr(identity_list *&il, boost::python::list value)
{
identity_list *_il = new_identity_list(NULL);
if (!_il)
throw bad_alloc();
identity_list *_i = _il;
for (int i=0; i<len(value); i++) {
extract< Identity& > extract_identity(value[i]);
if (!extract_identity.check()) {
free_identity_list(_il);
}
pEp_identity *_ident = extract_identity();
pEp_identity *_dup = ::identity_dup(_ident);
if (!_dup) {
free_identity_list(_il);
throw bad_alloc();
}
PEP_STATUS status = update_identity(Adapter::session(), _dup);
if (status != PEP_STATUS_OK) {
free_identity_list(_il);
_throw_status(status);
}
_i = identity_list_add(_i, _dup);
if (!_i) {
free_identity_list(_il);
throw bad_alloc();
}
}
free_identity_list(il);
il = _il;
}
} // namespace PythonAdapter
} // namespace pEp {

102
src/pEp/native_pEp/identity.hh

@ -1,102 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef IDENTITY_HH
#define IDENTITY_HH
// System
#include <boost/python.hpp>
#include <string>
#include <memory>
#include <cstddef>
// Engine
#include <pEp/pEpEngine.h>
#include <pEp/message_api.h>
//libpEpAdapter
#include "pEp/Adapter.hh"
// local
#include "str_attr.hh"
namespace pEp {
namespace PythonAdapter {
using std::string;
using std::shared_ptr;
// Identity is owning a pEp_identity
class Identity {
protected:
shared_ptr< pEp_identity > _ident;
public:
Identity(string address = "", string username = "",
string user_id = "", string fpr = "", int comm_type = 0,
string lang = "", identity_flags_t flags = 0);
Identity(const Identity& second);
Identity(pEp_identity *ident);
virtual ~Identity();
operator pEp_identity *();
operator const pEp_identity *() const;
string _repr();
string _str();
string address() { return str_attr(_ident->address); }
void address(string value) { str_attr(_ident->address, value); }
string fpr() { return str_attr(_ident->fpr); }
void fpr(string value) { str_attr(_ident->fpr, value); }
string user_id() { return str_attr(_ident->user_id); }
void user_id(string value) { str_attr(_ident->user_id, value); }
string username() { return str_attr(_ident->username); }
void username(string value);
PEP_comm_type comm_type() { return _ident->comm_type; }
void comm_type(PEP_comm_type value) { _ident->comm_type = value; };
std::string lang();
void lang(std::string value);
identity_flags_t flags() { return _ident->flags; }
void flags(identity_flags_t flags) { _ident->flags = flags; }
int rating();
PEP_color color();
Identity copy();
Identity deepcopy(dict& memo);
virtual void update();
void key_reset(string fpr="");
void key_mistrusted();
bool is_pEp_user();
void enable_for_sync();
void disable_for_sync();
};
class Myself : public Identity {
public:
Myself(string address, string username, string user_id="", string lang="");
virtual void update();
};
Identity identity_attr(pEp_identity *&ident);
void identity_attr(pEp_identity *&ident, object value);
boost::python::list identitylist_attr(identity_list *&il);
void identitylist_attr(identity_list *&il, boost::python::list value);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* IDENTITY_HH */

413
src/pEp/native_pEp/message.cc

@ -1,413 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <sstream>
#include <vector>
#include <Python.h>
// Engine
#include <pEp/mime.h>
#include <pEp/keymanagement.h>
#include <pEp/message_api.h>
// local
#include "message.hh"
#include "message_api.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
Message::Blob::Blob(bloblist_t *bl, bool chained) :
_bl(bl), part_of_chain(chained)
{
if (!_bl)
throw bad_alloc();
}
Message::Blob::Blob(object data, string mime_type, string filename) :
_bl(new_bloblist(NULL, 0, NULL, NULL)), part_of_chain(false)
{
if (!_bl)
throw bad_alloc();
Py_buffer src;
int result = PyObject_GetBuffer(data.ptr(), &src, PyBUF_CONTIG_RO);
if (result)
throw invalid_argument("need a contiguous buffer to read");
char *mem = (char *)malloc(src.len);
if (!mem) {
PyBuffer_Release(&src);
throw bad_alloc();
}
memcpy(mem, src.buf, src.len);
free(_bl->value);
_bl->size = src.len;
_bl->value = mem;
PyBuffer_Release(&src);
this->mime_type(mime_type);
this->filename(filename);
}
Message::Blob::Blob(const Message::Blob& second) :
_bl(second._bl), part_of_chain(true)
{
}
Message::Blob::~Blob()
{
if (!part_of_chain) {
free(_bl->value);
free(_bl);
}
}
string Message::Blob::_repr()
{
stringstream build;
build << "Blob(";
if (!_bl) {
build << "b'', '', ''";
}
else {
build << "bytes(" << _bl->size << "), ";
string mime_type;
if (_bl->mime_type)
mime_type = string(_bl->mime_type);
string filename;
if (_bl->filename)
filename = string(_bl->filename);
build << repr(mime_type) << ", ";
build << repr(filename);
}
build << ")";
return build.str();
}
int Message::Blob::getbuffer(PyObject *self, Py_buffer *view, int flags) {
bloblist_t *bl = NULL;
try {
Message::Blob& blob = extract< Message::Blob& >(self);
bl = blob._bl;
}
catch (exception& e) {
PyErr_SetString(PyExc_RuntimeError, "extract not possible");
view->obj = NULL;
return -1;
}
if (!(bl && bl->value)) {
PyErr_SetString(PyExc_RuntimeError, "no data available");
view->obj = NULL;
return -1;
}
return PyBuffer_FillInfo(view, self, bl->value, bl->size, 0, flags);
}
string Message::Blob::decode(string encoding)
{
if (encoding == "") {
string _mime_type = _bl->mime_type ? _bl->mime_type : "";
encoding = "ascii";
if (_mime_type == "application/pEp.sync")
encoding = "pep.sync";
if (_mime_type == "application/pEp.keyreset")
encoding = "pep.distribution";
}
object codecs = import("codecs");
object _decode = codecs.attr("decode");
return call< string >(_decode.ptr(), this, encoding);
}
PyBufferProcs Message::Blob::bp = { getbuffer, NULL };
Message::Message(int dir, Identity *from)
: _msg(new_message((PEP_msg_direction) dir), &free_message)
{
if (!_msg)
throw bad_alloc();
if (from) {
_msg->from = ::identity_dup(*from);
if (!_msg->from)
throw bad_alloc();
_msg->dir = (PEP_msg_direction) dir;
}
}
Message::Message(string mimetext)
: _msg(NULL, &free_message)
{
message *_cpy;
PEP_STATUS status = mime_decode_message(mimetext.c_str(),
mimetext.size(), &_cpy, NULL);
switch (status) {
case PEP_STATUS_OK:
if (_cpy)
_cpy->dir = PEP_dir_outgoing;
else
_cpy = new_message(PEP_dir_outgoing);
if (!_cpy)
throw bad_alloc();
_msg = shared_ptr< message >(_cpy);
break;
case PEP_BUFFER_TOO_SMALL:
throw runtime_error("mime_decode_message: buffer too small");
case PEP_CANNOT_CREATE_TEMP_FILE:
throw runtime_error("mime_decode_message: cannot create temp file");
case PEP_OUT_OF_MEMORY:
throw bad_alloc();
default:
stringstream build;
build << "mime_decode_message: unknown error (" << (int) status << ")";
throw runtime_error(build.str());
}
}
Message::Message(const Message& second)
: _msg(second._msg)
{
if (!_msg.get())
throw bad_alloc();
}
Message::Message(message *msg)
: _msg(::message_dup(msg), &free_message)
{
}
Message::~Message()
{
}
Message::operator message *()
{
return _msg.get();
}
Message::operator const message *() const
{
return _msg.get();
}
string Message::_str()
{
if (!(_msg->from && _msg->from->address && _msg->from->address[0]))
throw out_of_range(".from_.address missing");
char *mimetext;
string result;
PEP_STATUS status = mime_encode_message(*this, false, &mimetext, false);
switch (status) {
case PEP_STATUS_OK:
result = mimetext;
free(mimetext);
break;
case PEP_BUFFER_TOO_SMALL:
throw runtime_error("mime_encode_message: buffer too small");
case PEP_CANNOT_CREATE_TEMP_FILE:
throw runtime_error("mime_encode_message: cannot create temp file");
case PEP_OUT_OF_MEMORY:
throw bad_alloc();
default:
stringstream build;
build << "mime_encode_message: unknown error (" << (int) status << ")";
throw runtime_error(build.str());
}
return result;
}
string Message::_repr()
{
stringstream build;
build << "Message(" << repr(_str()) << ")";
return build.str();
}
boost::python::tuple Message::attachments()
{
boost::python::list l;
for (bloblist_t *bl = _msg->attachments; bl && bl->value; bl =
bl->next) {
l.append(Blob(bl, true));
}
return boost::python::tuple(l);
}
void Message::attachments(boost::python::list value)
{
bloblist_t *bl = new_bloblist(NULL, 0, NULL, NULL);
if (!bl)
throw bad_alloc();
bloblist_t *_l = bl;
for (int i=0; i<len(value); i++) {
Message::Blob& blob = extract< Message::Blob& >(value[i]);
_l = bloblist_add(_l, blob._bl->value, blob._bl->size,
blob._bl->mime_type, blob._bl->filename);
if (!_l) {
for (_l = bl; _l && _l->value; ) {
free(_l->mime_type);
free(_l->filename);
bloblist_t *_ll = _l;
_l = _l->next;
free(_ll);
}
throw bad_alloc();
}
}
for (int i=0; i<len(value); i++) {
Message::Blob& blob = extract< Message::Blob& >(value[i]);
blob._bl->value = NULL;
blob._bl->size = 0;
free(blob._bl->mime_type);
blob._bl->mime_type = NULL;
free(blob._bl->filename);
blob._bl->filename = NULL;
}
free_bloblist(_msg->attachments);
_msg->attachments = bl;
}
Message Message::encrypt()
{
boost::python::list extra;
return encrypt_message(*this, extra, PEP_enc_PGP_MIME, 0);
}
Message Message::_encrypt(boost::python::list extra, int enc_format, int flags)
{
if (!enc_format)
enc_format = PEP_enc_PGP_MIME;
return encrypt_message(*this, extra, enc_format, flags);
}
boost::python::tuple Message::decrypt(int flags) {
return pEp::PythonAdapter::decrypt_message(*this, flags);
}
PEP_rating Message::outgoing_rating()
{
if (_msg->dir != PEP_dir_outgoing)
throw invalid_argument("Message.dir must be outgoing");
if (from().address() == "")
throw invalid_argument("from.address needed");
if (from().username() == "")
throw invalid_argument("from.username needed");
if (len(to()) + len(cc()) == 0)
throw invalid_argument("either to or cc needed");
PEP_STATUS status = myself(Adapter::session(), _msg->from);
_throw_status(status);
PEP_rating rating = PEP_rating_undefined;
status = outgoing_message_rating(Adapter::session(), *this, &rating);
_throw_status(status);
return rating;
}
PEP_color Message::outgoing_color()
{
return _color(outgoing_rating());
}
Message Message::copy()
{
message *dup = message_dup(*this);
if (!dup)
throw bad_alloc();
return Message(dup);
}
Message Message::deepcopy(dict&)
{
return copy();
}
Message outgoing_message(Identity me)
{
if (me.address().empty() || me.user_id().empty())
throw runtime_error("at least address and user_id of own user needed");
::myself(Adapter::session(), me);
auto m = Message(PEP_dir_outgoing, &me);
return m;
}
static object update(Identity ident)
{
if (ident.address().empty())
throw runtime_error("at least address needed");
update_identity(Adapter::session(), ident);
return object(ident);
}
static boost::python::list update(boost::python::list il)
{
for (int i=0; i<len(il); i++) {
update(extract< Identity >(il[i]));
}
return il;
}
Message incoming_message(string mime_text)
{
auto m = Message(mime_text);
m.dir(PEP_dir_incoming);
try {
m.from(update(m.from()));
}
catch (out_of_range&) { }
try {
m.recv_by(update(m.recv_by()));
}
catch (out_of_range&) { }
m.to(update(m.to()));
m.cc(update(m.cc()));
m.reply_to(update(m.reply_to()));
return m;
}
} // namespace PythonAdapter
} // namespace pEp {

154
src/pEp/native_pEp/message.hh

@ -1,154 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef MESSAGE_HH
#define MESSAGE_HH
// System
#include <string>
#include <boost/python.hpp>
#include <boost/lexical_cast.hpp>
// Engine
#include <pEp/message.h>
#include <pEp/message_api.h>
// local
#include "str_attr.hh"
#include "identity.hh"
namespace pEp {
namespace PythonAdapter {
using std::string;
using std::runtime_error;
using std::invalid_argument;
using boost::lexical_cast;
// Message is owning a message struct
class Message {
shared_ptr< ::message > _msg;
public:
// Blob is owning a bloblist_t struct - or not and just managing
// one depending on part_of_chain
class Blob {
bloblist_t *_bl;
bool part_of_chain;
public:
Blob(bloblist_t *bl = new_bloblist(NULL, 0, NULL, NULL),
bool chained = false);
Blob(object data, string mime_type = "", string filename = "");
Blob(const Blob& second);
~Blob();
string _repr();
string mime_type() { return _bl ? str_attr(_bl->mime_type) : ""; }
void mime_type(string value) { str_attr(_bl->mime_type, value); }
string filename() { return str_attr(_bl->filename); }
void filename(string value) { str_attr(_bl->filename, value); }
size_t size() { return _bl->size; }
string decode(string encoding);
string decode() { return decode(""); }
static PyBufferProcs bp;
friend class Message;
protected:
static int getbuffer(PyObject *self, Py_buffer *view, int flags);
};
Message(int dir = PEP_dir_outgoing, Identity *from = NULL);
Message(string mimetext);
Message(const Message& second);
Message(message *msg);
~Message();
operator message *();
operator const message *() const;
string _str();
string _repr();
PEP_msg_direction dir() { return _msg->dir; }
void dir(PEP_msg_direction value) { _msg->dir = value; }
string id() { return str_attr(_msg->id); }
void id(string value) { str_attr(_msg->id, value); }
string shortmsg() { return str_attr(_msg->shortmsg); }
void shortmsg(string value) { str_attr(_msg->shortmsg, value); }
string longmsg() { return str_attr(_msg->longmsg); }
void longmsg(string value) { str_attr(_msg->longmsg, value); }
string longmsg_formatted() { return str_attr(_msg->longmsg_formatted); }
void longmsg_formatted(string value) { str_attr(_msg->longmsg_formatted, value); }
boost::python::tuple attachments();
void attachments(boost::python::list value);
time_t sent() { return timestamp_attr(_msg->sent); }
void sent(time_t value) { timestamp_attr(_msg->sent, value); }
time_t recv() { return timestamp_attr(_msg->recv); }
void recv(time_t value) { timestamp_attr(_msg->recv, value); }
Identity from() { return identity_attr(_msg->from); }
void from(object value) { identity_attr(_msg->from, value); }
boost::python::list to() { return identitylist_attr(_msg->to); }
void to(boost::python::list value) { identitylist_attr(_msg->to, value); }
Identity recv_by() { return identity_attr(_msg->recv_by); }
void recv_by(object value) { identity_attr(_msg->recv_by, value); }
boost::python::list cc() { return identitylist_attr(_msg->cc); }
void cc(boost::python::list value) { identitylist_attr(_msg->cc, value); }
boost::python::list bcc() { return identitylist_attr(_msg->bcc); }
void bcc(boost::python::list value) { identitylist_attr(_msg->bcc, value); }
boost::python::list reply_to() { return identitylist_attr(_msg->reply_to); }
void reply_to(boost::python::list value) { identitylist_attr(_msg->reply_to, value); }
boost::python::list in_reply_to() { return strlist_attr(_msg->in_reply_to); }
void in_reply_to(boost::python::list value) { strlist_attr(_msg->in_reply_to, value); }
boost::python::list references() { return strlist_attr(_msg->references); }
void references(boost::python::list value) { strlist_attr(_msg->references, value); }
boost::python::list keywords() { return strlist_attr(_msg->keywords); }
void keywords(boost::python::list value) { strlist_attr(_msg->keywords, value); }
string comments() { return str_attr(_msg->comments); }
void comments(string value) { str_attr(_msg->comments, value); }
dict opt_fields() { return strdict_attr(_msg->opt_fields); }
void opt_fields(dict value) { return strdict_attr(_msg->opt_fields, value); }
PEP_enc_format enc_format() { return _msg->enc_format; }
void enc_format(PEP_enc_format value) { _msg->enc_format = value; }
Message encrypt();
Message _encrypt(boost::python::list extra, int enc_format=4, int flags=0);
boost::python::tuple decrypt(int flags=0);
PEP_rating outgoing_rating();
PEP_color outgoing_color();
Message deepcopy(dict& memo);
Message copy();
};
Message outgoing_message(Identity me);
Message incoming_message(string mime_text);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* MESSAGE_HH */

172
src/pEp/native_pEp/message_api.cc

@ -1,172 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// Engine
#include <pEp/pEpEngine.h>
#include <pEp/message_api.h>
#include <pEp/sync_api.h>
#include <pEp/sync_codec.h>
#include <pEp/distribution_codec.h>
// local
#include "message_api.hh"
#include "basic_api.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
Message encrypt_message(Message src, boost::python::list extra, int enc_format, int flags)
{
Identity _from = src.from();
if (_from.address() == "")
throw invalid_argument("encrypt_message: src.from_.address empty");
if (_from.username() == "")
throw invalid_argument("encrypt_message: src.from_.username empty");
if (_from.user_id() == "")
src.from().user_id(_from.address());
stringlist_t *_extra = to_stringlist(extra);
PEP_enc_format _enc_format = (PEP_enc_format) enc_format;
PEP_encrypt_flags_t _flags = (PEP_encrypt_flags_t) flags;
message *_dst = NULL;
message *_src = src;
PEP_STATUS status = encrypt_message(Adapter::session(), _src, _extra, &_dst,
_enc_format, _flags);
free_stringlist(_extra);
_throw_status(status);
if (!_dst || _dst == _src)
return Message(_src);
return Message(_dst);
}
boost::python::tuple decrypt_message(Message src, int flags)
{
message *_dst = NULL;
stringlist_t *_keylist = NULL;
PEP_rating _rating = PEP_rating_undefined;
PEP_decrypt_flags_t _flags = (PEP_decrypt_flags_t) flags;
message *_src = src;
PEP_STATUS status = ::decrypt_message(Adapter::session(), _src, &_dst, &_keylist,
&_rating, &_flags);
_throw_status(status);
boost::python::list keylist;
if (_keylist) {
keylist = from_stringlist(_keylist);
free_stringlist(_keylist);
}
Message dst = _dst ? Message(_dst) : Message(src);
return boost::python::make_tuple(dst, keylist, _rating, _flags);
}
PEP_color _color(int rating)
{
return ::color_from_rating((PEP_rating) rating);
}
boost::python::tuple sync_decode(object buffer)
{
Py_buffer src;
int result = PyObject_GetBuffer(buffer.ptr(), &src, PyBUF_CONTIG_RO);
if (result)
throw invalid_argument("need a contiguous buffer to read");
char *dst = NULL;
PEP_STATUS status = PER_to_XER_Sync_msg((char *) src.buf, src.len, &dst);
PyBuffer_Release(&src);
_throw_status(status);
string _dst(dst);
free(dst);
return boost::python::make_tuple(_dst, 0);
}
static boost::python::tuple sync_encode(string text)
{
char *data = NULL;
size_t size = 0;
PEP_STATUS status = XER_to_PER_Sync_msg(text.c_str(), &data, &size);
_throw_status(status);
PyObject *ba = PyBytes_FromStringAndSize(data, size);
free(data);
if (!ba)
throw bad_alloc();
return boost::python::make_tuple(object(handle<>(ba)), 0);
}
boost::python::tuple Distribution_decode(object buffer)
{
Py_buffer src;
int result = PyObject_GetBuffer(buffer.ptr(), &src, PyBUF_CONTIG_RO);
if (result)
throw invalid_argument("need a contiguous buffer to read");
char *dst = NULL;
PEP_STATUS status = PER_to_XER_Distribution_msg((char *) src.buf, src.len, &dst);
PyBuffer_Release(&src);
_throw_status(status);
string _dst(dst);
free(dst);
return boost::python::make_tuple(_dst, 0);
}
static boost::python::tuple Distribution_encode(string text)
{
char *data = NULL;
size_t size = 0;
PEP_STATUS status = XER_to_PER_Distribution_msg(text.c_str(), &data, &size);
_throw_status(status);
PyObject *ba = PyBytes_FromStringAndSize(data, size);
free(data);
if (!ba)
throw bad_alloc();
return boost::python::make_tuple(object(handle<>(ba)), 0);
}
object sync_search(string name)
{
if (name != "pep.sync") {
return object();
}
else {
object codecs = import("codecs");
object CodecInfo = codecs.attr("CodecInfo");
object _sync_decode = make_function(sync_decode);
object _sync_encode = make_function(sync_encode);
return call< object >(CodecInfo.ptr(), _sync_encode, _sync_decode);
}
}
object distribution_search(string name)
{
if (name != "pep.distribution") {
return object();
}
else {
object codecs = import("codecs");
object CodecInfo = codecs.attr("CodecInfo");
object _distribution_decode = make_function(Distribution_decode);
object _distribution_encode = make_function(Distribution_encode);
return call< object >(CodecInfo.ptr(), _distribution_encode, _distribution_decode);
}
}
} // namespace PythonAdapter
} // namespace pEp {

27
src/pEp/native_pEp/message_api.hh

@ -1,27 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef MESSAGE_API_HH
#define MESSAGE_API_HH
#include "pEpmodule.hh"
namespace pEp {
namespace PythonAdapter {
Message encrypt_message(
Message src,
boost::python::list extra = boost::python::list(),
int enc_format = 4,
int flags = 0
);
boost::python::tuple decrypt_message(Message src, int flags=0);
PEP_color _color(int rating);
object sync_search(string name);
object distribution_search(string name);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* MESSAGE_API_HH */

673
src/pEp/native_pEp/pEpmodule.cc

@ -1,673 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <boost/python.hpp>
#include <boost/locale.hpp>
#include <string>
#include <sstream>
#include <iomanip>
#include <mutex>
// Engine
#include <pEp/key_reset.h>
#include <pEp/message_api.h>
#include <pEp/sync_api.h>
#include <pEp/status_to_string.h>
// libpEpAdapter
#include <pEp/Adapter.hh>
#include <pEp/callback_dispatcher.hh>
#include <pEp/pEpLog.hh>
// local
#include "pEpmodule.hh"
#include "basic_api.hh"
#include "message_api.hh"
//#include "user_interface.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
static const char *version_string = "p≡p Python adapter version 0.3";
void init_before_main_module() {
pEpLog("called");
}
// hidden init function, wrapped by hello_world.init()
void _init_after_main_module() {
pEpLog("called");
callback_dispatcher.add(_messageToSend, notifyHandshake, nullptr, nullptr);
Adapter::_messageToSend = CallbackDispatcher::messageToSend;
}
void config_passive_mode(bool enable)
{
::config_passive_mode(Adapter::session(), enable);
}
void config_unencrypted_subject(bool enable)
{
::config_unencrypted_subject(Adapter::session(), enable);
}
void key_reset_user(string user_id, string fpr)
{
if (user_id == "")
throw invalid_argument("user_id required");
PEP_STATUS status = ::key_reset_user(Adapter::session(),
user_id.c_str(), fpr != "" ? fpr.c_str() : nullptr);
_throw_status(status);
}
void key_reset_user2(string user_id)
{
key_reset_user(user_id, "");
}
void key_reset_all_own_keys()
{
PEP_STATUS status = ::key_reset_all_own_keys(Adapter::session());
_throw_status(status);
}
static string about()
{
string version = string(version_string) + "\np≡p version "
+ PEP_VERSION + "\n";
return version;
}
void _throw_status(PEP_STATUS status)
{
if (status == PEP_STATUS_OK)
return;
if (status >= 0x400 && status <= 0x4ff)
return;
if (status == PEP_OUT_OF_MEMORY)
throw bad_alloc();
if (status == PEP_ILLEGAL_VALUE)
throw invalid_argument("illegal value");
if (string(pEp_status_to_string(status)) == "unknown status code") {
stringstream build;
build << setfill('0') << "p≡p 0x" << setw(4) << hex << status;
throw runtime_error(build.str());
}
else {
throw runtime_error(pEp_status_to_string(status));
}
}
PEP_STATUS _messageToSend(::message *msg)
{
pEpLog("called");
try {
PyGILState_STATE gil = PyGILState_Ensure();
pEpLog("GIL Aquired");
object modref = import("pEp");
object funcref = modref.attr("message_to_send");
call<void>(funcref.ptr(), Message());
PyGILState_Release(gil);
pEpLog("GIL released");
} catch (exception& e) { }
return PEP_STATUS_OK;
}
PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handshake_signal signal)
{
pEpLog("called");
try {
PyGILState_STATE gil = PyGILState_Ensure();
pEpLog("GIL Aquired");
object modref = import("pEp");
object funcref = modref.attr("notify_handshake");
call<void>(funcref.ptr(), me, partner, signal);
PyGILState_Release(gil);
pEpLog("GIL released");
} catch (exception& e) { }
return PEP_STATUS_OK;
}
void start_sync()
{
CallbackDispatcher::start_sync();
}
void shutdown_sync()
{
CallbackDispatcher::stop_sync();
}
void debug_color(int ansi_color)
{
::set_debug_color(Adapter::session(), ansi_color);
}
void leave_device_group()
{
::leave_device_group(Adapter::session());
}
bool is_sync_active()
{
return Adapter::is_sync_running();
}
void testfunc() {
_messageToSend(NULL);
}
void deliverHandshakeResult(int result, object identities)
{
identity_list *shared_identities = nullptr;
if (identities != boost::python::api::object() && boost::python::len(identities)) {
shared_identities = new_identity_list(nullptr);
if (!shared_identities)
throw bad_alloc();
try {
identity_list *si = shared_identities;
for (int i=0; i < boost::python::len(identities); ++i) {
Identity ident = extract< Identity >(identities[i]);
si = identity_list_add(si, ident);
if (!si)
throw bad_alloc();
}
}
catch (exception& ex) {
free_identity_list(shared_identities);
throw ex;
}
}
PEP_STATUS status = ::deliverHandshakeResult(Adapter::session(), (sync_handshake_result) result, shared_identities);
free_identity_list(shared_identities);
_throw_status(status);
}
BOOST_PYTHON_MODULE(native_pEp)
{
init_before_main_module();
// Module init function called by pEp.init()
def("_init_after_main_module", _init_after_main_module);
def("testfunc", &testfunc);
docstring_options doc_options(true, false, false);
boost::locale::generator gen;
std::locale::global(gen(""));
// _scope = new scope();
scope().attr("about") = about();
scope().attr("per_user_directory") = per_user_directory();
scope().attr("per_machine_directory") = per_machine_directory();
scope().attr("engine_version") = get_engine_version();
scope().attr("protocol_version") = get_protocol_version();
def("passive_mode", config_passive_mode,
"do not attach pub keys to all messages");
def("unencrypted_subject", config_unencrypted_subject,
"do not encrypt the subject of messages");
def("key_reset", key_reset_user,
"reset the default database status for the user / keypair provided\n"
"This will effectively perform key_reset on each identity\n"
"associated with the key and user_id, if a key is provided, and for\n"
"each key (and all of their identities) if an fpr is not.");
def("key_reset", key_reset_user2,
"reset the default database status for the user / keypair provided\n"
"This will effectively perform key_reset on each identity\n"
"associated with the key and user_id, if a key is provided, and for\n"
"each key (and all of their identities) if an fpr is not.");
def("key_reset_all_own_keys", key_reset_all_own_keys,
"revoke and mistrust all own keys, generate new keys for all\n"
"own identities, and opportunistically communicate key reset\n"
"information to people we have recently contacted.");
auto identity_class = class_<Identity>("Identity",
"Identity(address, username, user_id='', fpr='', comm_type=0, lang='en')\n"
"\n"
"represents a p≡p identity\n"
"\n"
"an identity is a network address, under which a user is represented in\n"
"the network\n"
"\n"
" address network address, either an SMTP address or a URI\n"
" username real name or nickname for user\n"
" user_id ID this user is handled by the application\n"
" fpr full fingerprint of the key being used as key ID,\n"
" hex encoded\n"
" comm_type first rating level of this communication channel\n"
" lang ISO 639-1 language code for language being preferred\n"
" on this communication channel\n"
)
.def(boost::python::init<string>())
.def(boost::python::init<string, string>())
.def(boost::python::init<string, string, string>())
.def(boost::python::init<string, string, string, string>())
.def(boost::python::init<string, string, string, string, int>())
.def(boost::python::init<string, string, string, string, int, string>())
.def("__repr__", &Identity::_repr)
.def("__str__", &Identity::_str,
"string representation of this identity\n"
"following the pattern 'username < address >'\n"
)
.def("key_reset", &Identity::key_reset,
boost::python::arg("fpr")=object(""),
"reset the default database status for the identity / keypair provided. If this\n"
"corresponds to the own user and a private key, also revoke the key, generate a\n"
"new one, and communicate the reset to recently contacted pEp partners for this\n"
"identity. If it does not, remove the key from the keyring; the key's status is\n"
"completely fresh on next contact from the partner.")
.def("key_mistrusted", &Identity::key_mistrusted,
boost::python::arg("fpr")=object(""),
"If you want updated trust on the identity, you ll have"
"to call update_identity or myself respectively after this."
"N.B. If you are calling this on a key that is the identity or user default,"
"it will be removed as the default key for ANY identity and user for which"
"it is the default. Please keep in mind that the undo in undo_last_mistrust"
"will only undo the current identity's / it's user's default, not any"
"other identities which may be impacted (this will not affect most use cases)")
.def("enable_for_sync", &Identity::enable_for_sync,
"Enable own identity for p≡p sync.\n\n"
"Only use this on own identities, which are used as accounts.\n")
.def("disable_for_sync", &Identity::disable_for_sync,
"Disable own identity for p≡p sync.\n\n"
"Only use this on own identities, which are used as accounts.\n")
.add_property("address", (string(Identity::*)()) &Identity::address,
(void(Identity::*)(string)) &Identity::address,
"email address or URI")
.add_property("fpr", (string(Identity::*)()) &Identity::fpr,
(void(Identity::*)(string)) &Identity::fpr,
"key ID (full fingerprint, hex encoded)")
.add_property("user_id", (string(Identity::*)()) &Identity::user_id,
(void(Identity::*)(string)) &Identity::user_id,
"ID of person associated or 'pEp_own_userId' if own identity")
.add_property("username", (string(Identity::*)()) &Identity::username,
(void(Identity::*)(string)) &Identity::username,
"name in full of person associated")
.add_property("comm_type", (int(Identity::*)())
(PEP_comm_type(Identity::*)()) &Identity::comm_type,
(void(Identity::*)(int))
(void(Identity::*)(PEP_comm_type)) &Identity::comm_type,
"communication type, first rating level (p≡p internal)")
.add_property("lang", (string(Identity::*)()) &Identity::lang,
(void(Identity::*)(string)) &Identity::lang,
"ISO 639-1 language code")
.add_property("flags", (identity_flags_t(Identity::*)()) &Identity::flags,
(void(Identity::*)(identity_flags_t)) &Identity::flags,
"flags (p≡p internal)")
.add_property("rating", &Identity::rating, "rating of Identity")
.add_property("color", &Identity::color, "color of Identity as PEP_color")
.add_property("is_pEp_user", &Identity::is_pEp_user, "True if this is an identity of a pEp user")
.def("__deepcopy__", &Identity::deepcopy)
.def("update", &Identity::update, "update Identity")
.def("__copy__", &Identity::copy);
identity_class.attr("PEP_OWN_USERID") = "pEp_own_userId";
auto blob_class = class_<Message::Blob>("Blob",
"Blob(data, mime_type='', filename='')\n"
"\n"
"Binary large object\n"
"\n"
" data bytes-like object\n"
" mime_type MIME type for the data\n"
" filename filename to store the data\n" ,
boost::python::init< object, char const*, char const* >(args("data", "mime_type", "filename")))
.def(boost::python::init<object, string>())
.def(boost::python::init<object>())
.def("__repr__", &Message::Blob::_repr)
.def("__len__", &Message::Blob::size, "size of Blob data in bytes")
.def("decode", (string(Message::Blob::*)()) &Message::Blob::decode)
.def("decode", (string(Message::Blob::*)(string)) &Message::Blob::decode,
"text = blob.decode(encoding='')\n"
"\n"
"decode Blob data into string depending on MIME type if encoding=''\n"
"\n"
" mime_type='application/pEp.sync' decode as 'pEp.sync'\n"
" mime_type='application/pEp.keyreset' decode as 'pEp.keyreset'\n"
" other mime_type decode as 'ascii' by default\n"
)
.add_property("mime_type", (string(Message::Blob::*)()) &Message::Blob::mime_type,
(void(Message::Blob::*)(string)) &Message::Blob::mime_type,
"MIME type of object in Blob")
.add_property("filename", (string(Message::Blob::*)()) &Message::Blob::filename,
(void(Message::Blob::*)(string)) &Message::Blob::filename,
"filename of object in Blob");
((PyTypeObject *)(void *)blob_class.ptr())->tp_as_buffer = &Message::Blob::bp;
auto message_class = class_<Message>("Message",
"Message(dir=1, from=None)\n"
"\n"
"new p≡p message\n"
"\n"
" dir 1 for outgoing, 2 for incoming\n"
" from Identity() of sender\n"
"\n"
"Message(mime_text)\n"
"\n"
"new incoming p≡p message\n"
"\n"
" mime_text text in Multipurpose Internet Mail Extensions format\n"
)
.def(boost::python::init<int>())
.def(boost::python::init<int, Identity *>())
.def(boost::python::init<string>())
.def("__str__", &Message::_str,
"the string representation of a Message is it's MIME text"
)
.def("__repr__", &Message::_repr)
.add_property("dir", (int(Message::*)())
(PEP_msg_direction(Message::*)()) &Message::dir,
(void(Message::*)(int))
(void(Message::*)(PEP_msg_direction)) &Message::dir,
"0: incoming, 1: outgoing message")
.add_property("id", (string(Message::*)()) &Message::id,
(void(Message::*)(string)) &Message::id,
"message ID")
.add_property("shortmsg", (string(Message::*)()) &Message::shortmsg,
(void(Message::*)(string)) &Message::shortmsg,
"subject or short message")
.add_property("longmsg", (string(Message::*)()) &Message::longmsg,
(void(Message::*)(string)) &Message::longmsg,
"body or long version of message")
.add_property("longmsg_formatted", (string(Message::*)()) &Message::longmsg_formatted,
(void(Message::*)(string)) &Message::longmsg_formatted,
"HTML body or fromatted long version of message")
.add_property("attachments", (boost::python::tuple(Message::*)()) &Message::attachments,
(void(Message::*)(boost::python::list)) &Message::attachments,
"tuple of Blobs with attachments; setting moves Blobs to attachment tuple")
.add_property("sent", (time_t(Message::*)()) &Message::sent,
(void(Message::*)(time_t)) &Message::sent,
"time when message was sent in UTC seconds since epoch")
.add_property("recv", (time_t(Message::*)()) &Message::recv,
(void(Message::*)(time_t)) &Message::recv,
"time when message was received in UTC seconds since epoch")
.add_property("from_", (Identity(Message::*)()) &Message::from,
(void(Message::*)(object)) &Message::from,
"identity where message is from")
.add_property("to", (boost::python::list(Message::*)()) &Message::to,
(void(Message::*)(boost::python::list)) &Message::to,
"list of identities message is going to")
.add_property("recv_by", (Identity(Message::*)()) &Message::recv_by,
(void(Message::*)(object)) &Message::recv_by,
"identity where message was received by")
.add_property("cc", (boost::python::list(Message::*)()) &Message::cc,
(void(Message::*)(boost::python::list)) &Message::cc,
"list of identities message is going cc")
.add_property("bcc", (boost::python::list(Message::*)()) &Message::bcc,
(void(Message::*)(boost::python::list)) &Message::bcc,
"list of identities message is going bcc")
.add_property("reply_to", (boost::python::list(Message::*)()) &Message::reply_to,
(void(Message::*)(boost::python::list)) &Message::reply_to,
"list of identities where message will be replied to")
.add_property("in_reply_to", (boost::python::list(Message::*)()) &Message::in_reply_to,
(void(Message::*)(boost::python::list)) &Message::in_reply_to,
"in_reply_to list")
.add_property("references", (boost::python::list(Message::*)()) &Message::references,
(void(Message::*)(boost::python::list)) &Message::references,
"message IDs of messages this one is referring to")
.add_property("keywords", (boost::python::list(Message::*)()) &Message::keywords,
(void(Message::*)(boost::python::list)) &Message::keywords,
"keywords this message should be stored under")
.add_property("comments", (string(Message::*)()) &Message::comments,
(void(Message::*)(string)) &Message::comments,
"comments added to message")
.add_property("opt_fields", (dict(Message::*)()) &Message::opt_fields,
(void(Message::*)(dict)) &Message::opt_fields,
"opt_fields of message")
.add_property("enc_format", (int(Message::*)())
(PEP_enc_format(Message::*)()) &Message::enc_format,
(void(Message::*)(int))
(void(Message::*)(PEP_enc_format)) &Message::enc_format,
"0: unencrypted, 1: inline PGP, 2: S/MIME, 3: PGP/MIME, 4: p≡p format")
.def("encrypt", (Message(Message::*)())&Message::encrypt)
.def("encrypt", (Message(Message::*)(boost::python::list))&Message::_encrypt)
.def("encrypt", (Message(Message::*)(boost::python::list,int))&Message::_encrypt)
.def("encrypt", (Message(Message::*)(boost::python::list,int,int))&Message::_encrypt,
"msg2 = msg1.encrypt(extra_keys=[], enc_format='pEp', flags=0)\n"
"\n"
"encrypts a p≡p message and returns the encrypted message\n"
"\n"
" extra_keys list of strings with fingerprints for extra keys to use\n"
" for encryption\n"
" enc_format 0 for none, 1 for partitioned, 2 for S/MIME,\n"
" 3 for PGP/MIME, 4 for pEp\n"
" flags 1 is force encryption\n"
)
.def("decrypt", &Message::decrypt, boost::python::arg("flags")=0,
"msg2, keys, rating, flags = msg1.decrypt()\n"
"\n"
"decrypts a p≡p message and returns a tuple with data\n"
"\n"
" msg the decrypted p≡p message\n"
" keys a list of keys being used\n"
" rating the rating of the message as integer\n"
" flags flags set while decryption\n"
)
.add_property("outgoing_rating", &Message::outgoing_rating, "rating outgoing message will have")
.add_property("outgoing_color", &Message::outgoing_color, "color outgoing message will have as PEP_color")
.def("__deepcopy__", &Message::deepcopy)
.def("__copy__", &Message::copy);
// basic API and key management API
def("update_identity", &update_identity,
"update_identity(ident)\n"
"\n"
"update identity information\n"
"call this to complete identity information when you at least have an address\n"
);
def("myself", &myself,
"myself(ident)\n"
"\n"
"ensures that the own identity is being complete\n"
"supply ident.address and ident.username\n"
);
def("trust_personal_key", &trust_personal_key,
"trust_personal_key(ident)\n"
"\n"
"mark a key as trusted with a person\n"
);
enum_<identity_flags>("identity_flags")
.value("PEP_idf_not_for_sync", PEP_idf_not_for_sync)
.value("PEP_idf_list", PEP_idf_list)
.value("PEP_idf_devicegroup", PEP_idf_devicegroup);
def("set_identity_flags", &set_identity_flags,
"set_identity_flags(ident, flags)\n"
"\n"
"set identity flags\n"
);
def("unset_identity_flags", &unset_identity_flags,
"unset_identity_flags(ident, flags)\n"
"\n"
"unset identity flags\n"
);
def("key_reset_trust", &key_reset_trust,
"key_reset_trust(ident)\n"
"\n"
"reset trust bit or explicitly mistrusted status for an identity and "
"its accompanying key/user_id pair\n"
);
def("import_key", &import_key,
"private_key_list = import_key(key_data)\n"
"\n"
"import key(s) from key_data\n"
);
def("export_key", &export_key,
"key_data = export_key(identity)\n"
"\n"
"export key(s) of identity\n"
);
def("export_secret_key", &export_secret_key,
"key_data = export_seret_key(identity)\n"
"\n"
"export secret key(s) of identity\n"
);
def("set_own_key", &set_own_key,
"set_own_key(me, fpr)\n"
"\n"
"mark a key as an own key, and make it the default key\n"
"\n"
"me Own identity for which to add the existing key\n"
"fpr The fingerprint of the key to be added\n"
"\n"
"me->address, me->user_id and me->username must be set to valid data\n"
"myself() is called by set_own_key() without key generation\n"
"me->flags are ignored\n"
"me->address must not be an alias\n"
"me->fpr will be ignored and replaced by fpr\n"
);
// message API
enum_<PEP_rating>("rating")
.value("_undefined", PEP_rating_undefined)
.value("cannot_decrypt", PEP_rating_cannot_decrypt)
.value("have_no_key", PEP_rating_have_no_key)
.value("unencrypted", PEP_rating_unencrypted)
.value("unreliable", PEP_rating_unreliable)
.value("reliable", PEP_rating_reliable)
.value("trusted", PEP_rating_trusted)
.value("trusted_and_anonymized", PEP_rating_trusted_and_anonymized)
.value("fully_anonymous", PEP_rating_fully_anonymous)
.value("mistrust", PEP_rating_mistrust)
.value("b0rken", PEP_rating_b0rken)
.value("under_attack", PEP_rating_under_attack);
enum_<PEP_color>("colorvalue")
.value("no_color", PEP_color_no_color)
.value("yellow", PEP_color_yellow)
.value("green", PEP_color_green)
.value("red", PEP_color_red);
def("incoming_message", &incoming_message,
"msg = incoming_message(mime_text)\n"
"\n"
"create an incoming message from a MIME text"
);
def("outgoing_message", &outgoing_message,
"msg = outgoing_message(ident)\n"
"\n"
"create an outgoing message using an own identity"
);
def("color", &_color,
"c = color(rating)\n"
"\n"
"calculate color value out of rating. Returns PEP_color"
);
def("trustwords", &_trustwords,
"text = trustwords(ident_own, ident_partner)\n"
"\n"
"calculate trustwords for two Identities");
// Sync API
enum_<sync_handshake_signal>("sync_handshake_signal")
.value("SYNC_NOTIFY_UNDEFINED" , SYNC_NOTIFY_UNDEFINED)
.value("SYNC_NOTIFY_INIT_ADD_OUR_DEVICE" , SYNC_NOTIFY_INIT_ADD_OUR_DEVICE)
.value("SYNC_NOTIFY_INIT_ADD_OTHER_DEVICE" , SYNC_NOTIFY_INIT_ADD_OTHER_DEVICE)
.value("SYNC_NOTIFY_INIT_FORM_GROUP" , SYNC_NOTIFY_INIT_FORM_GROUP)
.value("SYNC_NOTIFY_TIMEOUT" , SYNC_NOTIFY_TIMEOUT)
.value("SYNC_NOTIFY_ACCEPTED_DEVICE_ADDED" , SYNC_NOTIFY_ACCEPTED_DEVICE_ADDED)
.value("SYNC_NOTIFY_ACCEPTED_GROUP_CREATED", SYNC_NOTIFY_ACCEPTED_GROUP_CREATED)
.value("SYNC_NOTIFY_ACCEPTED_DEVICE_ACCEPTED", SYNC_NOTIFY_ACCEPTED_DEVICE_ACCEPTED)
.value("SYNC_NOTIFY_SOLE" , SYNC_NOTIFY_SOLE)
.value("SYNC_NOTIFY_IN_GROUP" , SYNC_NOTIFY_IN_GROUP);
// auto user_interface_class = class_<UserInterface, UserInterface_callback, boost::noncopyable>(
// "UserInterface",
// "class MyUserInterface(UserInterface):\n"
// " def notifyHandshake(self, me, partner):\n"
// " ...\n"
// "\n"
// "p≡p User Interface class\n"
// "To be used as a mixin\n"
// )
// .def("notifyHandshake", &UserInterface::notifyHandshake,
// "notifyHandshake(self, me, partner)\n"
// "\n"
// " me own identity\n"
// " partner identity of communication partner\n"
// "\n"
// "overwrite this method with an implementation of a handshake dialog")
// .def("deliverHandshakeResult", &UserInterface::deliverHandshakeResult,
// boost::python::arg("identities")=object(),
// "deliverHandshakeResult(self, result, identities=None)\n"
// "\n"
// " result -1: cancel, 0: accepted, 1: rejected\n"
// " identities list of identities to share or None for all\n"
// "\n"
// "call to deliver the handshake result of the handshake dialog"
// );
def("deliver_handshake_result", &deliverHandshakeResult, boost::python::arg("identities")=object(),
"deliverHandshakeResult(self, result, identities=None)\n"
"\n"
" result -1: cancel, 0: accepted, 1: rejected\n"
" identities list of identities to share or None for all\n"
"\n"
"call to deliver the handshake result of the handshake dialog"
);
def("start_sync", &start_sync,
"start_sync()\n"
"\n"
"starts the sync thread"
);
def("shutdown_sync", &shutdown_sync,
"shutdown_sync()\n"
"\n"
"call this from another thread to shut down the sync thread\n"
);
def("debug_color", &debug_color,
"for debug builds set ANSI color value");
def("leave_device_group", &leave_device_group,
"leave_device_group()\n"
"\n"
"call this for a grouped device, which should leave\n"
);
def("is_sync_active", &is_sync_active,
"is_sync_active()\n"
"\n"
"True if sync is active, False otherwise\n"
);
// codecs
call< object >(((object)(import("codecs").attr("register"))).ptr(), make_function(sync_search));
call< object >(((object)(import("codecs").attr("register"))).ptr(), make_function(distribution_search));
}
} // namespace PythonAdapter
} // namespace pEp

28
src/pEp/native_pEp/pEpmodule.hh

@ -1,28 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef PEPMODULE_HH
#define PEPMODULE_HH
// Engine
#include <pEp/pEpEngine.h>
// local
#include "message.hh"
namespace pEp {
namespace PythonAdapter {
extern string device_name;
void config_passive_mode(bool enable);
void config_unencrypted_subject(bool enable);
void key_reset_user(string user_id, string fpr);
void key_reset_all_own_keys();
void _throw_status(PEP_STATUS status);
PEP_STATUS _messageToSend(::message *msg);
PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handshake_signal signal);
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* PEPMODULE_HH */

184
src/pEp/native_pEp/str_attr.cc

@ -1,184 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <cstdlib>
#include <boost/python.hpp>
#include <boost/locale.hpp>
// local
#include "str_attr.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
using namespace boost::locale;
object repr(object s)
{
return s.attr("__repr__")();
}
string repr(string s)
{
str _s = s.c_str();
object _r = _s.attr("__repr__")();
string r = extract< string >(_r);
return r;
}
string str_attr(char *&str)
{
if (!str)
return string("");
return string(str);
}
void str_attr(char *&str, string value)
{
string normalized = normalize(value, norm_nfc);
free(str);
str = strdup(normalized.c_str());
if (!str)
throw bad_alloc();
}
time_t timestamp_attr(timestamp *&ts)
{
if (!ts)
return 0;
return timegm(ts);
}
void timestamp_attr(timestamp *&ts, time_t value)
{
free_timestamp(ts);
ts = new_timestamp(value);
}
boost::python::list strlist_attr(stringlist_t *&sl)
{
boost::python::list result;
for (stringlist_t *_sl = sl; _sl && _sl->value; _sl = _sl->next) {
string s(_sl->value);
result.append(object(s));
}
return result;
}
void strlist_attr(stringlist_t *&sl, boost::python::list value)
{
stringlist_t *_sl = new_stringlist(NULL);
if (!_sl)
throw bad_alloc();
stringlist_t *_s = _sl;
for (int i=0; i<len(value); i++) {
extract< string > extract_string(value[i]);
if (!extract_string.check()) {
free_stringlist(_sl);
}
string s = extract_string();
s = normalize(s, norm_nfc);
_s = stringlist_add(_s, s.c_str());
if (!_s) {
free_stringlist(_sl);
throw bad_alloc();
}
}
free_stringlist(sl);
sl = _sl;
}
dict strdict_attr(stringpair_list_t *&spl)
{
dict result;
for (stringpair_list_t *_spl = spl; _spl && _spl->value; _spl =
_spl->next) {
stringpair_t *p = _spl->value;
if (p->key && p->value) {
string key(p->key);
string value(p->value);
result[key] = value;
}
}
return result;
}
void strdict_attr(stringpair_list_t *&spl, dict value)
{
stringpair_list_t *_spl = new_stringpair_list(NULL);
if (!_spl)
throw bad_alloc();
stringpair_list_t *_s = _spl;
for (int i=0; i<len(value); i++) {
extract< string > extract_key(value.keys()[i]);
extract< string > extract_value(value.values()[i]);
if (!(extract_key.check() && extract_value.check()))
free_stringpair_list(_spl);
string key = extract_key();
key = normalize(key, norm_nfc);
string _value = extract_value();
_value = normalize(_value, norm_nfc);
stringpair_t *pair = new_stringpair(key.c_str(), _value.c_str());
if (!pair) {
free_stringpair_list(_spl);
throw bad_alloc();
}
_s = stringpair_list_add(_s, pair);
if (!_s) {
free_stringpair_list(_spl);
throw bad_alloc();
}
}
free_stringpair_list(spl);
spl = _spl;
}
stringlist_t *to_stringlist(boost::python::list l)
{
stringlist_t *result = new_stringlist(NULL);
if (!result)
throw bad_alloc();
stringlist_t *_s = result;
for (int i=0; i<len(l); i++) {
extract< string > extract_string(l[i]);
if (!extract_string.check())
free_stringlist(result);
string s = extract_string();
_s = stringlist_add(_s, s.c_str());
if (!_s) {
free_stringlist(result);
throw bad_alloc();
}
}
return result;
}
boost::python::list from_stringlist(const stringlist_t *sl)
{
boost::python::list result;
for (const stringlist_t *_sl = sl; _sl && _sl->value; _sl = _sl->next) {
string s = _sl->value;
result.append(s);
}
return result;
}
} // namespace PythonAdapter
} // namespace pEp {

130
src/pEp/native_pEp/user_interface.cc

@ -1,130 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
// System
#include <cassert>
// local
#include "user_interface.hh"
namespace pEp {
namespace PythonAdapter {
using namespace std;
using namespace boost::python;
UserInterface *UserInterface::_ui = nullptr;
UserInterface::UserInterface()
{
if (_ui)
throw runtime_error("only one UserInterface thread allowed");
_ui = this;
}
UserInterface::~UserInterface()
{
_ui = nullptr;
}
UserInterface_callback::UserInterface_callback(PyObject *self) :
UserInterface(), _self(self)
{
// adapter.ui_object(self);
// PEP_STATUS status = ::register_sync_callbacks(Adapter::session(),
// (void *) this, _notifyHandshake, retrieve_next_sync_event);
// assert(status == PEP_STATUS_OK);
// if (status)
// _throw_status(status);
}
UserInterface_callback::~UserInterface_callback()
{
// ::unregister_sync_callbacks(Adapter::session());
}
PEP_STATUS UserInterface::_notifyHandshake(
pEp_identity *me, pEp_identity *partner,
sync_handshake_signal signal
)
{
if (!(me && partner))
return PEP_ILLEGAL_VALUE;
auto that = dynamic_cast< UserInterface_callback * >(_ui);
that->notifyHandshake(Identity(me), Identity(partner), signal);
return PEP_STATUS_OK;
}
void UserInterface::deliverHandshakeResult(int result, object identities)
{
identity_list *shared_identities = nullptr;
if (identities != boost::python::api::object() && boost::python::len(identities)) {
shared_identities = new_identity_list(nullptr);
if (!shared_identities)
throw bad_alloc();
try {
identity_list *si = shared_identities;
for (int i=0; i < boost::python::len(identities); ++i) {
Identity ident = extract< Identity >(identities[i]);
si = identity_list_add(si, ident);
if (!si)
throw bad_alloc();
}
}
catch (exception& ex) {
free_identity_list(shared_identities);
throw ex;
}
}
PEP_STATUS status = ::deliverHandshakeResult(Adapter::session(),
(sync_handshake_result) result, shared_identities);
free_identity_list(shared_identities);
_throw_status(status);
}
//PEP_rating UserInterface::get_key_rating_for_user(string user_id, string fpr)
//{
// PEP_rating result;
// PEP_STATUS status =
// ::get_key_rating_for_user(Adapter::session(),
// user_id.c_str(), fpr.c_str(), &result);
// _throw_status(status);
// return result;
//}
//SYNC_EVENT UserInterface::retrieve_next_sync_event(void *management, unsigned threshold)
//{
// time_t started = time(nullptr);
// bool timeout = false;
//
// while (adapter.queue().empty()) {
// int i = 0;
// ++i;
// if (i > 10) {
// if (time(nullptr) > started + threshold) {
// timeout = true;
// break;
// }
// i = 0;
// }
// nanosleep((const struct timespec[]){{0, 100000000L}}, NULL);
// }
//
// if (timeout)
// return new_sync_timeout_event();
//
// return adapter.queue().pop_front();
//}
void UserInterface_callback::notifyHandshake(
Identity me, Identity partner, sync_handshake_signal signal)
{
call_method< void >(_self, "notifyHandshake", me, partner, signal);
}
} // namespace PythonAdapter
} // namespace pEp {

60
src/pEp/native_pEp/user_interface.hh

@ -1,60 +0,0 @@
// This file is under GNU Affero General Public License 3.0
// see LICENSE.txt
#ifndef USER_INTERFACE_HH
#define USER_INTERFACE_HH
// System
#include <csetjmp>
// Engine
#include <pEp/sync_api.h>
#include <pEp/message_api.h>
// local
#include "pEpmodule.hh"
namespace pEp {
namespace PythonAdapter {
class UserInterface {
static UserInterface *_ui;
public:
UserInterface();
virtual ~UserInterface();
virtual void notifyHandshake(
Identity me,
Identity partner,
sync_handshake_signal signal)
{
throw runtime_error("override this method");
}
virtual void deliverHandshakeResult(int result, object identities);
// PEP_rating get_key_rating_for_user(string user_id, string fpr);
protected:
static PEP_STATUS _notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handshake_signal signal);
};
class UserInterface_callback : public UserInterface {
PyObject *_self;
public:
UserInterface_callback(PyObject *self);
~UserInterface_callback();
void notifyHandshake(
Identity me,
Identity partner,
sync_handshake_signal signal
);
};
} /* namespace PythonAdapter */
} /* namespace pEp */
#endif /* USER_INTERFACE_HH */

27
test/basic_doctest.py

@ -1,27 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
>>> import pEp
>>> me = pEp.Identity("alice.smith@peptest.ch", "Alice Smith", "23")
>>> me.username
'Alice Smith'
>>> print(me)
Alice Smith <alice.smith@peptest.ch>
>>> you = pEp.Identity("bob.bourne@peptest.ch", "Bob Bourne", "42")
>>> print(you)
Bob Bourne <bob.bourne@peptest.ch>
>>> m = pEp.outgoing_message(me)
>>> m.to = [you]
>>> m.shortmsg = "let's meet next week"
>>> m.longmsg = "Please call me back"
>>> m2 = m.encrypt()
>>> print(m2)
>>> m3, keys, rating, flags = m2.decrypt()
>>> rating
pEp.rating.reliable
"""
if __name__ == "__main__":
import doctest
doctest.testmod()

3
tests/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt

64
tests/conftest.py

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt
"""pytest configuration for the unit tests."""
from .model import *
# Init
@pytest.fixture()
def env_init(tmpdir_factory, request):
"""Create a tmp dir for the tests"""
base = str(abs(hash(request.node.nodeid)))[:3]
bn = tmpdir_factory.mktemp(base)
print(bn)
import os
os.environ["PEP_HOME"] = str(bn)
os.environ["HOME"] = str(bn)
@pytest.fixture()
def pEp(env_init):
import pEp
return pEp
@pytest.fixture()
def alice_myself(pEp, model):
alice = pEp.Identity(
model.alice.addr,
model.alice.name,
model.alice.user_id
)
pEp.myself(alice)
return alice
@pytest.fixture()
def alice_imported(pEp, model):
pEp.import_key(model.alice.key_sec)
alice = pEp.Identity(
model.alice.addr,
model.alice.name,
model.alice.user_id
)
return alice
@pytest.fixture()
def import_ident_alice_as_own_ident(pEp, model, alice_imported):
alice = alice_imported
pEp.set_own_key(alice, model.alice.fpr)
return alice
@pytest.fixture()
def import_ident_bob(pEp, model):
pEp.import_key(model.bob.key_pub)
bob = pEp.Identity(
model.bob.addr,
model.bob.name,
)
bob.update()
return bob

12
tests/constants.py

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt
"""Constants for unit tests."""
DATADIR = "data"
OUTGOING_MSG = 1
INCOMING_MSG = 2
SUBJECT = "This is a subject"
BODY = "Hi world!\n"

24
tests/data/1A97F263D8319D6885F638C5AA81E1B5457A2B40.pub.asc

@ -0,0 +1,24 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: 1A97 F263 D831 9D68 85F6 38C5 AA81 E1B5 457A 2B40
Comment: bob_work@peptest.org
xjMEXyp4RRYJKwYBBAHaRw8BAQdAdA0qiUaazZJrnWAtqQ+V/XevsZkNHyspsD8M
jWv69i3ChAQfFgoAFQWCXyp4RQWJBaSPvQIVCgKbAQIeAQAhCRCqgeG1RXorQBYh
BBqX8mPYMZ1ohfY4xaqB4bVFeitA1j0BAONtcJp4KI4CTjVJps7udMFPRqAPDXU6
0YJzHWXK0mspAQCA2kZ2mJKFvMiKl3mY79N08eF2QE4zOVHYQYR+GZrtAc0UYm9i
X3dvcmtAcGVwdGVzdC5vcmfChAQTFgoAFQWCXyp4RQWJBaSPvQIVCgKbAQIeAQAh
CRCqgeG1RXorQBYhBBqX8mPYMZ1ohfY4xaqB4bVFeitA0NsA/i+jZZ8jVJVjTN9d
OPlCSg2rgMoW4CMudbvCeJ05HBGgAQDfpHv0mceDpw0Xb7JvSCZGpcnx7JvnrijO
o7s0km1fB84zBF8qeEUWCSsGAQQB2kcPAQEHQCqxykiuw5mJk7ulNeeS9ZRmNMh+
A5Za3/Mqwr8neCElwsA7BBgWCgAVBYJfKnhFBYkFpI+9AhUKApsCAh4BAJgJEKqB
4bVFeitAdqAEGRYKAAYFgl8qeEUAIQkQZIW7NIu2E2oWIQQjjCvzjFveMgb5MvZk
hbs0i7YTav2zAQCDuIDQu3vO31HUiGvSc2/ZYRKUAHJsVfnCovZSFiBaHwD+MNgr
QYydV0jkx2hyaQV7GGml132sKCVmHW6t+8+B8AYWIQQal/Jj2DGdaIX2OMWqgeG1
RXorQI6IAP0RuRRGJV8zg9ax3HSpBYsqQCTjq/Kswiskrzlda77RrwD9EnohAaPF
0yd5MrZ0H01+RJseLhF20mznjYX4ucKckQrOOARfKnhFEgorBgEEAZdVAQUBAQdA
/QOYQ28riHwdqlzL1SS89+sK52RUSGJ+z2jMEGZXrg4DAQgJwoQEGBYKABUFgl8q
eEUFiQWkj70CCwkCmwwCHgEAIQkQqoHhtUV6K0AWIQQal/Jj2DGdaIX2OMWqgeG1
RXorQAI/AQCVdZqaCPSHI8KQ75SmjCHOrizFJ5OvRQIxLcgFYnbf4QD/RK8/na7X
t3H5zFD9yjCeNtTKuBubz8wnlV48V6F8NQk=
=71Mh
-----END PGP PUBLIC KEY BLOCK-----

26
tests/data/1A97F263D8319D6885F638C5AA81E1B5457A2B40.sec.asc

@ -0,0 +1,26 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: 1A97 F263 D831 9D68 85F6 38C5 AA81 E1B5 457A 2B40
Comment: bob_work@peptest.org
xVgEXyp4RRYJKwYBBAHaRw8BAQdAdA0qiUaazZJrnWAtqQ+V/XevsZkNHyspsD8M
jWv69i0AAP9vlqqd0FjnC219hFfUH3NWDyNkE+qLncgL8iauLJJW2xApwoQEHxYK
ABUFgl8qeEUFiQWkj70CFQoCmwECHgEAIQkQqoHhtUV6K0AWIQQal/Jj2DGdaIX2
OMWqgeG1RXorQNY9AQDjbXCaeCiOAk41SabO7nTBT0agDw11OtGCcx1lytJrKQEA
gNpGdpiShbzIipd5mO/TdPHhdkBOMzlR2EGEfhma7QHNFGJvYl93b3JrQHBlcHRl
c3Qub3JnwoQEExYKABUFgl8qeEUFiQWkj70CFQoCmwECHgEAIQkQqoHhtUV6K0AW
IQQal/Jj2DGdaIX2OMWqgeG1RXorQNDbAP4vo2WfI1SVY0zfXTj5QkoNq4DKFuAj
LnW7wnidORwRoAEA36R79JnHg6cNF2+yb0gmRqXJ8eyb564ozqO7NJJtXwfHWARf
KnhFFgkrBgEEAdpHDwEBB0AqscpIrsOZiZO7pTXnkvWUZjTIfgOWWt/zKsK/J3gh
JQABAJW6BesKSs2CgM+fx+SKkv+di6b1aTLirOmVdfbse0R9E5TCwDsEGBYKABUF
gl8qeEUFiQWkj70CFQoCmwICHgEAmAkQqoHhtUV6K0B2oAQZFgoABgWCXyp4RQAh
CRBkhbs0i7YTahYhBCOMK/OMW94yBvky9mSFuzSLthNq/bMBAIO4gNC7e87fUdSI
a9Jzb9lhEpQAcmxV+cKi9lIWIFofAP4w2CtBjJ1XSOTHaHJpBXsYaaXXfawoJWYd
bq37z4HwBhYhBBqX8mPYMZ1ohfY4xaqB4bVFeitAjogA/RG5FEYlXzOD1rHcdKkF
iypAJOOr8qzCKySvOV1rvtGvAP0SeiEBo8XTJ3kytnQfTX5Emx4uEXbSbOeNhfi5
wpyRCsddBF8qeEUSCisGAQQBl1UBBQEBB0D9A5hDbyuIfB2qXMvVJLz36wrnZFRI
Yn7PaMwQZleuDgMBCAkAAP99a9Hrl/wNGP8ohfOaTkMYE9zuBEo8FHsCpLT+/Yz9
8BIBwoQEGBYKABUFgl8qeEUFiQWkj70CCwkCmwwCHgEAIQkQqoHhtUV6K0AWIQQa
l/Jj2DGdaIX2OMWqgeG1RXorQAI/AQCVdZqaCPSHI8KQ75SmjCHOrizFJ5OvRQIx
LcgFYnbf4QD/RK8/na7Xt3H5zFD9yjCeNtTKuBubz8wnlV48V6F8NQk=
=Yzdu
-----END PGP PRIVATE KEY BLOCK-----

24
tests/data/2D35731B9C754564CBAD15D2D18F7444594F2283.pub.asc

@ -0,0 +1,24 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: 2D35 731B 9C75 4564 CBAD 15D2 D18F 7444 594F 2283
Comment: alice@peptest.org
xjMEXxFT2hYJKwYBBAHaRw8BAQdA/q3RICIqQ4FHd6RfqP6QY1gd2trvJn0DBg/1
jIfdpKfChAQfFgoAFQWCXxFT2gWJBaSPvQIVCgKbAQIeAQAhCRDRj3REWU8igxYh
BC01cxucdUVky60V0tGPdERZTyKDUnYBAMT6z+rJ6HvoAhE531BWhsSU9X0QKLC7
UYLQ3/ZGBVAbAQC14aJX1sODCKCPK6eivyhfd8l6W69vCBpPa55TgQ3GD80RYWxp
Y2VAcGVwdGVzdC5vcmfChAQTFgoAFQWCXxFT2gWJBaSPvQIVCgKbAQIeAQAhCRDR
j3REWU8igxYhBC01cxucdUVky60V0tGPdERZTyKDQlMBAPhingJLnqAjx+DnEWdu
nPRwnhxWSgY31GRAD8LG36zlAP94BN2pRvv6sPSCSla+yUvVcYQt927MvI1B3mOI
2jnlAM4zBF8RU9oWCSsGAQQB2kcPAQEHQCxlXfe9mHyk7Z9Cd+/12i5kYm6ljyKV
Jsst1oxMj/CnwsA7BBgWCgAVBYJfEVPaBYkFpI+9AhUKApsCAh4BAJgJENGPdERZ
TyKDdqAEGRYKAAYFgl8RU9oAIQkQYvLo9HBcHrAWIQTnCIjW7qTg/Ju3h6Ni8uj0
cFwesAy3AP4uHp91sH/DWv5Ac7jppfEXpg+CW/RoKdJ32N5S6BnWZQEAn5jtnve2
cBx2jgUQEKkGZ6AprrrdCnPgH8AOxNOWbAYWIQQtNXMbnHVFZMutFdLRj3REWU8i
g9JRAQCHR0PJVrsyy91jLA0oAYaEyf743XlrJY8yhM3H4HKKzQEAishD4vi9cpCj
tx4EXIwoqxy2wfLbwfkGulENJS38DAbOOARfEVPaEgorBgEEAZdVAQUBAQdAo7WR
Up2ZLQbkUhTB9Aqw3Z6uKGNUUiOp7GsVu3fpAScDAQgJwoQEGBYKABUFgl8RU9oF
iQWkj70CCwkCmwwCHgEAIQkQ0Y90RFlPIoMWIQQtNXMbnHVFZMutFdLRj3REWU8i
g1QJAP9l9Xyw8j22fFUa6iKzD68SP7zLb4LbGpHdKXcBd86kigD+O0zwJp7S/Ogq
LpOoJOD6xX50+0JnlPnIxZWwhgYPrAs=
=S7O4
-----END PGP PUBLIC KEY BLOCK-----

26
tests/data/2D35731B9C754564CBAD15D2D18F7444594F2283.sec.asc

@ -0,0 +1,26 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: 2D35 731B 9C75 4564 CBAD 15D2 D18F 7444 594F 2283
Comment: alice@peptest.org
xVgEXxFT2hYJKwYBBAHaRw8BAQdA/q3RICIqQ4FHd6RfqP6QY1gd2trvJn0DBg/1
jIfdpKcAAP0W8bWDwzy2kpwqa5gx9T/DWt4kDCoa7tGzMU+dv0FBnBCMwoQEHxYK
ABUFgl8RU9oFiQWkj70CFQoCmwECHgEAIQkQ0Y90RFlPIoMWIQQtNXMbnHVFZMut
FdLRj3REWU8ig1J2AQDE+s/qyeh76AIROd9QVobElPV9ECiwu1GC0N/2RgVQGwEA
teGiV9bDgwigjyunor8oX3fJeluvbwgaT2ueU4ENxg/NEWFsaWNlQHBlcHRlc3Qu
b3JnwoQEExYKABUFgl8RU9oFiQWkj70CFQoCmwECHgEAIQkQ0Y90RFlPIoMWIQQt
NXMbnHVFZMutFdLRj3REWU8ig0JTAQD4Yp4CS56gI8fg5xFnbpz0cJ4cVkoGN9Rk
QA/Cxt+s5QD/eATdqUb7+rD0gkpWvslL1XGELfduzLyNQd5jiNo55QDHWARfEVPa
FgkrBgEEAdpHDwEBB0AsZV33vZh8pO2fQnfv9douZGJupY8ilSbLLdaMTI/wpwAA
/RU5lcZzjrlWdEh95e12IjqpCKt1VpwQhT617pSZyYpqELbCwDsEGBYKABUFgl8R
U9oFiQWkj70CFQoCmwICHgEAmAkQ0Y90RFlPIoN2oAQZFgoABgWCXxFT2gAhCRBi
8uj0cFwesBYhBOcIiNbupOD8m7eHo2Ly6PRwXB6wDLcA/i4en3Wwf8Na/kBzuOml
8RemD4Jb9Ggp0nfY3lLoGdZlAQCfmO2e97ZwHHaOBRAQqQZnoCmuut0Kc+AfwA7E
05ZsBhYhBC01cxucdUVky60V0tGPdERZTyKD0lEBAIdHQ8lWuzLL3WMsDSgBhoTJ
/vjdeWsljzKEzcfgcorNAQCKyEPi+L1ykKO3HgRcjCirHLbB8tvB+Qa6UQ0lLfwM
BsddBF8RU9oSCisGAQQBl1UBBQEBB0CjtZFSnZktBuRSFMH0CrDdnq4oY1RSI6ns
axW7d+kBJwMBCAkAAP9kY8sKTIAbQiR7PVRDPfI49ccZmoDSjNG6nPBeT7JwEA/h
woQEGBYKABUFgl8RU9oFiQWkj70CCwkCmwwCHgEAIQkQ0Y90RFlPIoMWIQQtNXMb
nHVFZMutFdLRj3REWU8ig1QJAP9l9Xyw8j22fFUa6iKzD68SP7zLb4LbGpHdKXcB
d86kigD+O0zwJp7S/OgqLpOoJOD6xX50+0JnlPnIxZWwhgYPrAs=
=uIGP
-----END PGP PRIVATE KEY BLOCK-----

24
tests/data/3E45175EE953EBBEB948F11A6A03DB2A17FB9D15.pub.asc

@ -0,0 +1,24 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: 3E45 175E E953 EBBE B948 F11A 6A03 DB2A 17FB 9D15
Comment: alice_work@peptest.org
xjMEXxFT7BYJKwYBBAHaRw8BAQdAToI/CRX5M3LjPpSCfmJtOngHTos502W02e4e
I1CyNE/ChAQfFgoAFQWCXxFT7AWJBaSPvQIVCgKbAQIeAQAhCRBqA9sqF/udFRYh
BD5FF17pU+u+uUjxGmoD2yoX+50VV9cA/1HmBlEd6SVkhAK7dP46yGQtEJLix5x+
Z14COnk/EKETAP9Roz7sJakpqC/VejESHekpt5DterDWAXc4hjf1ADutDc0WYWxp
Y2Vfd29ya0BwZXB0ZXN0Lm9yZ8KEBBMWCgAVBYJfEVPsBYkFpI+9AhUKApsBAh4B
ACEJEGoD2yoX+50VFiEEPkUXXulT6765SPEaagPbKhf7nRVc2wD/Q7ISeG3cDbji
W+17gSnlYEYtG5EjIzGavYMr2tPhAlcA/RpuaaYkB+9FGYapXpKEZfOa9gRfg4sY
l2jIK5QNWYcDzjMEXxFT7BYJKwYBBAHaRw8BAQdAnljLgkOQD0jx8eoIbV0rUN1o
m5mux0+pLHZ6sIt+yD7CwDsEGBYKABUFgl8RU+wFiQWkj70CFQoCmwICHgEAmAkQ
agPbKhf7nRV2oAQZFgoABgWCXxFT7AAhCRBLlptQp2yWJxYhBMqF7w4SDl18JRD6
akuWm1CnbJYnUBEBAM55LvWxQ7Mi65AJEGhRlqGzCexUDheuXgVZYbqboc2wAQDc
SUxqP5Jbq66pAWSpHBTgT1rrZW9RbsA+yOA9B6DpBxYhBD5FF17pU+u+uUjxGmoD
2yoX+50VAjgBANay4I3jqeDBCkUlxPQZr0wlcxhiZW4m6NSamTqQvsLCAPwPivrv
YXaMc4zGSAesvk0Mwo5+2eX984EiHzsW+1mUAM44BF8RU+wSCisGAQQBl1UBBQEB
B0C0Ix+Z8q+NmQ7W64bAZwf1lYbDIykmtmh4pOCu9/XGdQMBCAnChAQYFgoAFQWC
XxFT7AWJBaSPvQILCQKbDAIeAQAhCRBqA9sqF/udFRYhBD5FF17pU+u+uUjxGmoD
2yoX+50VvKMBAOHyhqD60oJJpA1G116lL7LjKjnFuJG/6tG4xfvFi2fPAQCxPM+N
Pm5OZkyuDqsmY1RkrsbPCMOer3nrCNaKWYBcCg==
=FGLu
-----END PGP PUBLIC KEY BLOCK-----

26
tests/data/3E45175EE953EBBEB948F11A6A03DB2A17FB9D15.sec.asc

@ -0,0 +1,26 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: 3E45 175E E953 EBBE B948 F11A 6A03 DB2A 17FB 9D15
Comment: alice_work@peptest.org
xVgEXxFT7BYJKwYBBAHaRw8BAQdAToI/CRX5M3LjPpSCfmJtOngHTos502W02e4e
I1CyNE8AAP9nsNGRiCaR0Rd9A0zQ8olYfdApaEGAtlI7Ke5AjnRGvRBMwoQEHxYK
ABUFgl8RU+wFiQWkj70CFQoCmwECHgEAIQkQagPbKhf7nRUWIQQ+RRde6VPrvrlI
8RpqA9sqF/udFVfXAP9R5gZRHeklZIQCu3T+OshkLRCS4secfmdeAjp5PxChEwD/
UaM+7CWpKagv1XoxEh3pKbeQ7Xqw1gF3OIY39QA7rQ3NFmFsaWNlX3dvcmtAcGVw
dGVzdC5vcmfChAQTFgoAFQWCXxFT7AWJBaSPvQIVCgKbAQIeAQAhCRBqA9sqF/ud
FRYhBD5FF17pU+u+uUjxGmoD2yoX+50VXNsA/0OyEnht3A244lvte4Ep5WBGLRuR
IyMxmr2DK9rT4QJXAP0abmmmJAfvRRmGqV6ShGXzmvYEX4OLGJdoyCuUDVmHA8dY
BF8RU+wWCSsGAQQB2kcPAQEHQJ5Yy4JDkA9I8fHqCG1dK1DdaJuZrsdPqSx2erCL
fsg+AAEA9dzB32gcqp1IQ3GXFZ7gu6ay46pdfrlUNNvnyzUxW8wSOcLAOwQYFgoA
FQWCXxFT7AWJBaSPvQIVCgKbAgIeAQCYCRBqA9sqF/udFXagBBkWCgAGBYJfEVPs
ACEJEEuWm1CnbJYnFiEEyoXvDhIOXXwlEPpqS5abUKdslidQEQEAznku9bFDsyLr
kAkQaFGWobMJ7FQOF65eBVlhupuhzbABANxJTGo/klurrqkBZKkcFOBPWutlb1Fu
wD7I4D0HoOkHFiEEPkUXXulT6765SPEaagPbKhf7nRUCOAEA1rLgjeOp4MEKRSXE
9BmvTCVzGGJlbibo1JqZOpC+wsIA/A+K+u9hdoxzjMZIB6y+TQzCjn7Z5f3zgSIf
Oxb7WZQAx10EXxFT7BIKKwYBBAGXVQEFAQEHQLQjH5nyr42ZDtbrhsBnB/WVhsMj
KSa2aHik4K739cZ1AwEICQAA/2iRtG/26b2MEAaxszNxbwdaeE39GU5HvdLmnuVQ
kTwgENbChAQYFgoAFQWCXxFT7AWJBaSPvQILCQKbDAIeAQAhCRBqA9sqF/udFRYh
BD5FF17pU+u+uUjxGmoD2yoX+50VvKMBAOHyhqD60oJJpA1G116lL7LjKjnFuJG/
6tG4xfvFi2fPAQCxPM+NPm5OZkyuDqsmY1RkrsbPCMOer3nrCNaKWYBcCg==
=Vrqy
-----END PGP PRIVATE KEY BLOCK-----

24
tests/data/6A9835699EF1215F1558A496D9C1D4B0984094E5.pub.asc

@ -0,0 +1,24 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: 6A98 3569 9EF1 215F 1558 A496 D9C1 D4B0 9840 94E5
Comment: bob@peptest.org
xjMEXxFT+xYJKwYBBAHaRw8BAQdAAYt8QUG+i4GQyfwaUQL+cmGKUcWHkcWX87IC
qmNpm//ChAQfFgoAFQWCXxFT+wWJBaSPvQIVCgKbAQIeAQAhCRDZwdSwmECU5RYh
BGqYNWme8SFfFVikltnB1LCYQJTlle4A/3Xl+WA2cHvE5pYsuhxZGqwABxaOmIx8
AMxj0TBpuObXAP9y/flFpkK15Et/wl/T7iwAn2HAbQWRaPMoX694iQGjB80PYm9i
QHBlcHRlc3Qub3JnwoQEExYKABUFgl8RU/sFiQWkj70CFQoCmwECHgEAIQkQ2cHU
sJhAlOUWIQRqmDVpnvEhXxVYpJbZwdSwmECU5cmFAQDNarwFonE0e3rmjt1eI6CK
e8VjWgMpOYRa05xPdONVsQD/cejxjE+vsMlw3xVprNbKeJeXN+ZPJjNI4LIcw1B/
igrOMwRfEVP7FgkrBgEEAdpHDwEBB0DdURvGONC+G8dZa0Q1vFGp5WVnrtahfJ0T
PRx5LPdvC8LAOwQYFgoAFQWCXxFT+wWJBaSPvQIVCgKbAgIeAQCYCRDZwdSwmECU
5XagBBkWCgAGBYJfEVP7ACEJENwMujbE/mf1FiEEvRHaDz+iRkPLimpN3Ay6NsT+
Z/VX8QD/eKyJ/O2yF4jaNDDjzNrEzOZg7pvqiKZolV2WMlA4msUBANdN8PkKCH5G
HPo/T3mWl+xYkOVqZuw90Ay6npS6++wJFiEEapg1aZ7xIV8VWKSW2cHUsJhAlOUQ
UwEAhWLV69mEyxA1FrixumNan7vulofahcc7ox5cfZAANbEA/jBNmTPtROEaSeuU
IPyQ0lO+DS+Gmh9FYIfa35uAaQYKzjgEXxFT+xIKKwYBBAGXVQEFAQEHQL3nDAFV
lgBfUmw5C9OEv0txcP98NUgtADUmr5p9MdswAwEICcKEBBgWCgAVBYJfEVP7BYkF
pI+9AgsJApsMAh4BACEJENnB1LCYQJTlFiEEapg1aZ7xIV8VWKSW2cHUsJhAlOWl
CQD+O3xgfil6wkuenJEEWOWRHm0ft2u50DeP3/mJiRRdefoBAOwurevk+Ky1ZGqo
gpTmsS/1vAf/fz2DLWWN61hIyOEM
=/DS+
-----END PGP PUBLIC KEY BLOCK-----

26
tests/data/6A9835699EF1215F1558A496D9C1D4B0984094E5.sec.asc

@ -0,0 +1,26 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: 6A98 3569 9EF1 215F 1558 A496 D9C1 D4B0 9840 94E5
Comment: bob@peptest.org
xVgEXxFT+xYJKwYBBAHaRw8BAQdAAYt8QUG+i4GQyfwaUQL+cmGKUcWHkcWX87IC
qmNpm/8AAQCrGsb0A0+Vfxn13bwcs9cotQzf4f8gP4AN6oX3wiMgQxB1woQEHxYK
ABUFgl8RU/sFiQWkj70CFQoCmwECHgEAIQkQ2cHUsJhAlOUWIQRqmDVpnvEhXxVY
pJbZwdSwmECU5ZXuAP915flgNnB7xOaWLLocWRqsAAcWjpiMfADMY9Ewabjm1wD/
cv35RaZCteRLf8Jf0+4sAJ9hwG0FkWjzKF+veIkBowfND2JvYkBwZXB0ZXN0Lm9y
Z8KEBBMWCgAVBYJfEVP7BYkFpI+9AhUKApsBAh4BACEJENnB1LCYQJTlFiEEapg1
aZ7xIV8VWKSW2cHUsJhAlOXJhQEAzWq8BaJxNHt65o7dXiOginvFY1oDKTmEWtOc
T3TjVbEA/3Ho8YxPr7DJcN8VaazWyniXlzfmTyYzSOCyHMNQf4oKx1gEXxFT+xYJ
KwYBBAHaRw8BAQdA3VEbxjjQvhvHWWtENbxRqeVlZ67WoXydEz0ceSz3bwsAAPsF
YmCVyg8ega1rLrOGVRhphlUGpGwiAvUd8C56L/ZGlg3kwsA7BBgWCgAVBYJfEVP7
BYkFpI+9AhUKApsCAh4BAJgJENnB1LCYQJTldqAEGRYKAAYFgl8RU/sAIQkQ3Ay6
NsT+Z/UWIQS9EdoPP6JGQ8uKak3cDLo2xP5n9VfxAP94rIn87bIXiNo0MOPM2sTM
5mDum+qIpmiVXZYyUDiaxQEA103w+QoIfkYc+j9PeZaX7FiQ5Wpm7D3QDLqelLr7
7AkWIQRqmDVpnvEhXxVYpJbZwdSwmECU5RBTAQCFYtXr2YTLEDUWuLG6Y1qfu+6W
h9qFxzujHlx9kAA1sQD+ME2ZM+1E4RpJ65Qg/JDSU74NL4aaH0Vgh9rfm4BpBgrH
XQRfEVP7EgorBgEEAZdVAQUBAQdAvecMAVWWAF9SbDkL04S/S3Fw/3w1SC0ANSav
mn0x2zADAQgJAAD/cLLc4Gss5eq6v0PO/sCLyyA69hsz5FAyDFX7F3nMBygRzMKE
BBgWCgAVBYJfEVP7BYkFpI+9AgsJApsMAh4BACEJENnB1LCYQJTlFiEEapg1aZ7x
IV8VWKSW2cHUsJhAlOWlCQD+O3xgfil6wkuenJEEWOWRHm0ft2u50DeP3/mJiRRd
efoBAOwurevk+Ky1ZGqogpTmsS/1vAf/fz2DLWWN61hIyOEM
=4nOw
-----END PGP PRIVATE KEY BLOCK-----

126
tests/model.py

@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt
from . import utils
import pytest
@pytest.fixture()
def model():
"Returns the whole data model"
return Model()
identities = \
{
"alice": {
"name": "alice",
"user_id": "23",
"accounts":
{
"work": {
"addr": "alice_work@peptest.org",
"fpr": "3E45175EE953EBBEB948F11A6A03DB2A17FB9D15"
},
"home": {
"addr": "alice@peptest.org",
"fpr": "2D35731B9C754564CBAD15D2D18F7444594F2283"
}
}
},
"bob": {
"name": "bob",
"user_id": "uuid:1-2-3-4",
"accounts": \
{
"work": {
"addr": "bob_work@peptest.org",
"fpr": "1A97F263D8319D6885F638C5AA81E1B5457A2B40"
},
"home": {
"addr": "bob@peptest.org",
"fpr": "6A9835699EF1215F1558A496D9C1D4B0984094E5"
}
}
}
}
class Identity:
"""
An Identity class that is:
- can represent pEp.Identity
- is read-only (const)
"""
def __init__(self, name="", user_id="", addr="", fpr="", key_sec="", key_pub=""):
self.__name = name
self.__user_id = user_id
self.__addr = addr
self.__fpr = fpr
self.__key_sec = key_sec
self.__key_pub = key_pub
@property
def name(self):
return self.__name
@property
def user_id(self):
return self.__user_id
@property
def addr(self):
return self.__addr
@property
def fpr(self):
return self.__fpr
@property
def key_sec(self):
return self.__key_sec
@property
def key_pub(self):
return self.__key_pub
def debug(self) -> str:
ret = "name:" + self.__name
ret +="user_id:" + self.__user_id
ret +="addr:" + self.__addr
ret +="fpr:" + self.__fpr
ret +="key_sec:" + self.__key_sec[0:255]
ret +="key_pub:" + self.__key_pub[0:255]
return ret
def __str__(self):
return "{} {}".format(self.name, "<{}>".format(self.addr))
# The Data Model
class Model:
alice = None
alice_work = None
bob = None
bob_work = None
def getIdentity(self, name, account) -> Identity:
# fetch keys for ident from data folder
key_sec = utils.data_file_contents(identities[name]['accounts'][account]['fpr'] + ".sec.asc")
key_pub = utils.data_file_contents(identities[name]['accounts'][account]['fpr'] + ".pub.asc")
ident = Identity(name=identities[name]['name'],
user_id=identities[name]['user_id'],
addr=identities[name]['accounts'][account]['addr'],
fpr=identities[name]['accounts'][account]['fpr'],
key_pub=key_pub,
key_sec=key_sec
)
return ident
def __init__(self):
self.alice = self.getIdentity("alice", "home")
self.alice_work = self.getIdentity("alice", "work")
self.bob = self.getIdentity("bob", "home")
self.bob_work = self.getIdentity("bob", "work")

55
tests/test_basic.py

@ -0,0 +1,55 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from . import constants
import pytest
# """
# >>> import pEp
# >>> me = pEp.Identity("alice.smith@peptest.ch", "Alice Smith", "23")
# >>> me.username
# 'Alice Smith'
# >>> print(me)
# Alice Smith <alice.smith@peptest.ch>
# >>> you = pEp.Identity("bob.bourne@peptest.ch", "Bob Bourne", "42")
# >>> print(you)
# Bob Bourne <bob.bourne@peptest.ch>
# >>> m = pEp.outgoing_message(me)
# >>> m.to = [you]
# >>> m.shortmsg = "let's meet next week"
# >>> m.longmsg = "Please call me back"
# >>> m2 = m.encrypt()
# >>> print(m2)
# >>> m3, keys, rating, flags = m2.decrypt()
# >>> rating
# pEp.rating.reliable
# """
#
# if __name__ == "__main__":
# import doctest
# doctest.testmod()
def test_basic(pEp, model):
me = pEp.Identity(
model.alice.addr,
model.alice.name,
model.alice.user_id
)
assert me.username == model.alice.name
assert str(me) == str(model.alice)
you = pEp.Identity(
model.bob.addr,
model.bob.name,
model.bob.user_id
)
assert str(you) == str(model.bob)
#TODO: pEp.outgoing_message() needs to return type pEp.Message not None
m = pEp.outgoing_message(me)
m.to = [you]
m.shortmsg = constants.SUBJECT
m.longmsg = constants.BODY
#TODO: encrypt needs to return message type
m2 = m.encrypt()
m3, keys, rating, flags = m2.decrypt()
#TODO: fix pEp.rating
# assert rating == pEp.

107
tests/test_identity.py

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt
"""Identity unit tests."""
import pytest
from . import constants
from . import model
# TODO: test_get_identity_by_{name,addr,etc..}
# def test_create_ident_myself(pEp, alice_myself):
# def test_create_ident_import(pEp, alice_myself):
# def test_create_ident_nokey(pEp, alice_myself):
def test_identity_constructor(pEp, model):
alice = pEp.Identity(
model.alice.addr,
model.alice.name,
model.alice.user_id
)
assert alice.address == model.alice.addr
assert alice.username == model.alice.name
assert alice.user_id == model.alice.user_id
assert str(alice) == str(model.alice)
# TODO:
# These here are actually plenty of individual tests
# Identity.update
# key_import
# set_own_key
@pytest.mark.skip(reason="needs to be decomposed and migrated to new data model")
def test_two_identities_succeed(pEp, model):
alice = pEp.Identity(
constants.ALICE_ADDRESS, constants.ALICE_USERNAME, '',
constants.ALICE_FPR, 0, ''
)
assert alice.address == constants.ALICE_ADDRESS
assert alice.username == constants.ALICE_USERNAME
assert alice.fpr == constants.ALICE_FPR
assert alice.user_id == ""
assert alice.comm_type == 0
assert alice.flags == 0
pEp.import_key(model.bob.key_pub)
bob = pEp.Identity()
bob.address = constants.BOB_ADDRESS
bob.username = constants.BOB_USERNAME
bob.fpr = constants.BOB_FPR
expected_bob = pEp.Identity(
constants.BOB_ADDRESS, constants.BOB_USERNAME, '',
constants.BOB_FPR, 56, ''
)
assert str(bob) == constants.BOB_NAME_ADDR
assert bob.address == expected_bob.address
assert bob.username == expected_bob.username
assert bob.fpr == expected_bob.fpr
assert bob.user_id == ""
assert bob.comm_type == 0
assert bob.flags == 0
# Test that data after updating.
bob.update()
assert str(bob) == constants.BOB_NAME_ADDR
assert bob.address == expected_bob.address
assert bob.username == expected_bob.username
assert bob.fpr == expected_bob.fpr
assert bob.user_id == "TOFU_bob@openpgp.example"
assert bob.comm_type == 56
assert bob.flags == 0
@pytest.mark.skip(reason="needs to be decomposed and migrated to new data model")
def test_set_own_key(pEp, alice_key_sec):
pEp.import_key(alice_key_sec)
alice = pEp.Identity()
alice.address = constants.ALICE_ADDRESS
alice.username = constants.ALICE_USERNAME
alice.fpr = constants.ALICE_FPR
alice.user_id = constants.ALICE_NAME_ADDR
expected_alice = pEp.Identity(
constants.ALICE_ADDRESS, constants.ALICE_USERNAME, '',
constants.ALICE_FPR, 0, ''
)
pEp.set_own_key(alice, alice.fpr)
# assert str(alice) == constants.ALICE_NAME_ADDR
assert str(alice) == str(expected_alice)
assert alice.address == expected_alice.address
assert alice.username == expected_alice.username
# assert alice.user_id == constants.ALICE_NAME_ADDR
assert alice.user_id == str(expected_alice)
assert alice.fpr == expected_alice.fpr
assert alice.comm_type == 255
assert alice.flags == 0
# After setting own key this would give ValueError: illegal value
# alice.update()

169
tests/test_message.py

@ -0,0 +1,169 @@
# -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt
"""Message unit tests."""
from . import constants
def test_msg_enc_dec_roundtrip(pEp, model, import_ident_alice_as_own_ident, import_ident_bob):
alice = import_ident_alice_as_own_ident
bob = import_ident_bob
msg = pEp.Message(constants.OUTGOING_MSG, alice)
msg.to = [bob]
msg.shortmsg = constants.SUBJECT
msg.longmsg = constants.BODY
assert msg.enc_format == 0
# XXX: No way to check MIME so far.
# Test that creating the `Message` with `outgoing_message` is the same.
msg2 = pEp.outgoing_message(alice)
msg2.to = [bob]
msg2.shortmsg = constants.SUBJECT
msg2.longmsg = constants.BODY
assert str(msg2) == str(msg)
# Encrypt Message
enc_msg = msg.encrypt()
assert enc_msg.enc_format == 3
assert str(enc_msg.from_) == str(model.alice)
assert str(enc_msg.to[0]) == str(model.bob)
assert enc_msg.shortmsg == "p≡p"
assert enc_msg.longmsg == "this message was encrypted with p≡p https://pEp-project.org"
# Decrypt message.
dec_msg, key_list, rating, r = enc_msg.decrypt()
assert r == 0
# pEp version 2.2 throws this error:
# AttributeError: module 'pEp' has no attribute 'PEP_rating'
# assert rating == pEp.PEP_rating.PEP_rating_reliable
# It seems to have changed to the following.
assert rating == pEp._pEp.rating.reliable
# The first 2 keys are Alice's ones, the last is Bob's one.
assert key_list[0] == key_list[1] == model.alice.fpr
assert key_list[-1] == model.bob.fpr
assert dec_msg.shortmsg == constants.SUBJECT
assert dec_msg.longmsg.replace("\r", "") == msg.longmsg
dec_lines = str(dec_msg).replace("\r", "").split("\n")
# pEp version 2.2 seems to have fixed some of the replaced characters.
# and changed also:
# Content-Type: doesn't pring `; charset="utf-8"` anymore.
# Content-Transfer-Encoding: doesn't print `quoted-printable` anymore.
# Content-Disposition: is not present anymore.
# `!` is not replaced by `=21` anymore.
expected_dec_lines = \
"""From: alice <alice@peptest.org>
To: bob <bob@peptest.org>
Subject: This is a subject
X-pEp-Version: 2.1
X-EncStatus: reliable
X-KeyList:
X,X,6A9835699EF1215F1558A496D9C1D4B0984094E5
MIME-Version: 1.0
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
Hi world!
""".split("\n")
assert dec_lines[:5] == expected_dec_lines[:5]
assert dec_lines[7:] == expected_dec_lines[7:]
def test_msg_len_changes(pEp, import_ident_alice_as_own_ident, import_ident_bob):
"""Test that the original message is modified after encryption.
Headers are added and therefore the modified unencrypted message length
is different to the original.
XXX: The original message should be left unchanged.
There could be another method previous to `encrypt` that adds the
extra headers and modify the subject returning a new message.
"""
alice = import_ident_alice_as_own_ident
bob = import_ident_bob
msg = pEp.outgoing_message(alice)
msg.to = [bob]
msg.shortmsg = constants.SUBJECT
msg.longmsg = constants.BODY
msg_len = len(str(msg))
# Encrypt Message
msg.encrypt()
# After encryption, the original message is modified!!
# It contains one more header and the alice's public key, if it's the first
# msg to bob.
# XXX: if/when this is fixed, change the following `!=` to `==`
msg_after_encrypt_len = len(str(msg))
assert msg.shortmsg != constants.SUBJECT
assert msg.longmsg == constants.BODY
assert msg_after_encrypt_len != msg_len
def test_dec_msg_len(pEp, import_ident_alice_as_own_ident, import_ident_bob):
"""
Test that the decrypted message length is different from the original.
Because it adds extra headers.
"""
alice = import_ident_alice_as_own_ident
bob = import_ident_bob
msg = pEp.outgoing_message(alice)
msg.to = [bob]
msg.shortmsg = constants.SUBJECT
msg.longmsg = constants.BODY
msg_len = len(str(msg))
# Encrypt Message
enc_msg = msg.encrypt()
# Decrypt message.
dec_msg, _key_list, _rating, _r = enc_msg.decrypt()
dec_msg_len = len(str(dec_msg))
assert dec_msg.longmsg.replace("\r", "") == constants.BODY # msg.longmsg
expected_dec_msg = \
"""From: alice <alice@peptest.org>\r
To: bob <bob@peptest.org>\r
Subject: This is a subject\r
X-pEp-Version: 2.1\r
X-EncStatus: reliable\r
X-KeyList: \r
2D35731B9C754564CBAD15D2D18F7444594F2283,2D35731B9C754564CBAD15D2D18F7444594F2283,6A9835699EF1215F1558A496D9C1D4B0984094E5\r
MIME-Version: 1.0\r
Content-Type: text/plain\r
Content-Transfer-Encoding: 7bit\r
\r
Hi world!\r
"""
assert expected_dec_msg == str(dec_msg)
# The decrypted message length should then be equal to the original message
# minus the extra headers added.
dec_lines = str(dec_msg).split("\n")
extra_headers_lines = dec_lines[3:7]
extra_headers = "\n".join(extra_headers_lines) + "\n"
len_extra_headers = len(extra_headers)
print("len_extra_headers", len_extra_headers)
assert dec_msg_len - len_extra_headers == msg_len
#@pytest.mark.skip(reason="PYADAPT-91")
def test_null_char_rmed(pEp, import_ident_alice_as_own_ident, import_ident_bob):
"""Test that null characters and anything after them are not removed."""
alice = import_ident_alice_as_own_ident
bob = import_ident_bob
msg = pEp.outgoing_message(alice)
msg.to = [bob]
msg.shortmsg = constants.SUBJECT
# Message with null chars, potentially for padding.
body = "Hi Bob,\n" + "\0" * 255 + "\nBye,\nAlice."
msg.longmsg = body
# PYADAPT-91: The null characters and anything after them is removed.
# If/when this is fixed, change the following assertion.
assert msg.longmsg != body

15
tests/test_pep.py

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt
"""Unit test for pEp package, not for subpackages or modules."""
def test_pep_version():
""" Test that __version__ is not None or empty and is not 0.0.0."""
from pEp import __version__
# We could also test that match the regex, but that is already a test in
# setuptools_scm itself.
assert __version__
assert __version__ != "0.0.0"

13
tests/utils.py

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt
from . import constants
import pathlib
def data_file_contents(name) -> str:
"""grab the contents of a file in the data folder"""
path = pathlib.Path(__file__).parent / constants.DATADIR / name
with path.open(mode='r') as fid:
return fid.read()

2
utils/pEp

@ -1,5 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# This file is under GNU Affero General Public License 3.0
# see LICENSE.txt
""" """

Loading…
Cancel
Save