forked from r4sas/PBinCLI
Compare commits
25 Commits
dependabot
...
master
Author | SHA1 | Date | |
---|---|---|---|
ab7b2c060c | |||
b35fd9f4b9 | |||
d8f3c44371 | |||
7117b2bcd7 | |||
c860fec339 | |||
7ecb501627 | |||
7b90b6a772 | |||
a9a4079855 | |||
3965efef89 | |||
51170975c7 | |||
cc791da03c | |||
906c14fddf | |||
0e61d05c9f | |||
05c1938aa6 | |||
369738ea50 | |||
|
7bd3ef22b9 | ||
|
f05f65ea61 | ||
3c2cdb10ac | |||
28f7c09f83 | |||
b4ffc1a06b | |||
58645e1d32 | |||
82ca95f01a | |||
5589ba0437 | |||
6c9a5c95b6 | |||
|
505a0104ca |
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,3 +1,4 @@
|
|||||||
|
.github/** export-ignore
|
||||||
.gitattributes export-ignore
|
.gitattributes export-ignore
|
||||||
.gitignore export-ignore
|
.gitignore export-ignore
|
||||||
README.md export-ignore
|
README.md export-ignore
|
||||||
|
8
.github/dependabot.yml
vendored
Normal file
8
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: pip
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "17:00"
|
||||||
|
open-pull-requests-limit: 10
|
24
.github/workflows/binary.yml
vendored
Normal file
24
.github/workflows/binary.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
name: Package Application with Pyinstaller
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Binary for windows
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup python 3.8
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
- name: Build executable with pyinstaller
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install .
|
||||||
|
pip install pyinstaller pywin32-ctypes
|
||||||
|
pyinstaller pbincli.spec
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: pbincli-windows
|
||||||
|
path: dist/*
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -54,6 +54,7 @@ MANIFEST
|
|||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
*.manifest
|
*.manifest
|
||||||
*.spec
|
*.spec
|
||||||
|
-pbincli.spec
|
||||||
|
|
||||||
# Installer logs
|
# Installer logs
|
||||||
pip-log.txt
|
pip-log.txt
|
||||||
|
142
README.md
142
README.md
@ -1,73 +1,139 @@
|
|||||||
[![GitHub license](https://img.shields.io/github/license/r4sas/PBinCLI.svg)](https://github.com/r4sas/PBinCLI/blob/master/LICENSE)
|
[![GitHub license](https://img.shields.io/github/license/r4sas/PBinCLI.svg)](https://github.com/r4sas/PBinCLI/blob/master/LICENSE)
|
||||||
[![GitHub tag](https://img.shields.io/github/tag/r4sas/PBinCLI.svg)](https://github.com/r4sas/PBinCLI/tags/)
|
[![GitHub tag](https://img.shields.io/github/tag/r4sas/PBinCLI.svg)](https://github.com/r4sas/PBinCLI/tags/)
|
||||||
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/4f24f43356a84621bbd9078c4b3f1b70)](https://www.codacy.com/app/r4sas/PBinCLI?utm_source=github.com&utm_medium=referral&utm_content=r4sas/PBinCLI&utm_campaign=Badge_Grade)
|
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/4f24f43356a84621bbd9078c4b3f1b70)](https://www.codacy.com/gh/r4sas/PBinCLI/dashboard?utm_source=github.com&utm_medium=referral&utm_content=r4sas/PBinCLI&utm_campaign=Badge_Grade)
|
||||||
|
|
||||||
PBinCLI
|
# PBinCLI
|
||||||
=====
|
|
||||||
|
|
||||||
PBinCLI is command line client for [PrivateBin](https://github.com/PrivateBin/PrivateBin/) written on Python 3.
|
PBinCLI is a command line client for [PrivateBin](https://github.com/PrivateBin/PrivateBin/) written in Python 3.
|
||||||
|
|
||||||
Installing
|
# Installation
|
||||||
-----
|
|
||||||
|
Installing globally using pip3:
|
||||||
```bash
|
```bash
|
||||||
virtualenv --python=python3 venv
|
pip3 install pbincli
|
||||||
|
```
|
||||||
|
|
||||||
|
Installing with `virtualenv`:
|
||||||
|
```bash
|
||||||
|
python3 -m virtualenv --python=python3 venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pip install pbincli
|
pip install pbincli
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage
|
*Note*: if you used `virtualenv` installation method, don't forget to activate your virtual environment before running the tool: call `. /path/to/venv/bin/activate` in terminal
|
||||||
-----
|
|
||||||
By default pbincli configured to use `https://paste.i2pd.xyz/` for sending and receiving pastes. No proxy used by default.
|
|
||||||
|
|
||||||
You can create config file with variables `server` and `proxy` in `~/.config/pbincli/pbincli.conf` to use different settings.
|
# Configuration
|
||||||
|
|
||||||
Example contents:
|
By default PBinCLI is configured to use `https://paste.i2pd.xyz/` for sending and receiving pastes. No proxy is used by default.
|
||||||
|
|
||||||
|
You can always create a config file to use different settings.
|
||||||
|
|
||||||
|
Configuration file is expected to be found in `~/.config/pbincli/pbincli.conf`, `%APPDATA%/pbincli/pbincli.conf` (Windows) and `~/Library/Application Support/pbincli/pbincli.conf` (MacOS)
|
||||||
|
|
||||||
|
## Example of config file content
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
server=https://paste.i2pd.xyz/
|
server=https://paste.i2pd.xyz/
|
||||||
proxy=http://127.0.0.1:3128
|
proxy=http://127.0.0.1:3128
|
||||||
```
|
```
|
||||||
|
|
||||||
Run inside `venv` command:
|
## List of OPTIONS available
|
||||||
|
|
||||||
|
| Option | Default | Possible value |
|
||||||
|
|----------------------|-------------------------|----------------|
|
||||||
|
| server | https://paste.i2pd.xyz/ | Domain ending with slash |
|
||||||
|
| mirrors | None | Domains separated with comma, like `http://privatebin.ygg/,http://privatebin.i2p/` |
|
||||||
|
| proxy | None | Proxy address starting with scheme `http://` or `socks5://` |
|
||||||
|
| expire | 1day | 5min / 10min / 1hour / 1day / 1week / 1month / 1year / never |
|
||||||
|
| burn | False | True / False |
|
||||||
|
| discus | False | True / False |
|
||||||
|
| format | plaintext | plaintext / syntaxhighlighting / markdown |
|
||||||
|
| short | False | True / False |
|
||||||
|
| short_api | None | `tinyurl`, `clckru`, `isgd`, `vgd`, `cuttly`, `yourls`, `custom` |
|
||||||
|
| short_url | None | Domain name of shortener service for `yourls`, or URL (with required parameters) for `custom` |
|
||||||
|
| short_user | None | Used only in `yourls` |
|
||||||
|
| short_pass | None | Used only in `yourls` |
|
||||||
|
| short_token | None | Used only in `yourls` |
|
||||||
|
| no_check_certificate | False | True / False |
|
||||||
|
| no_insecure_warning | False | True / False |
|
||||||
|
| compression | zlib | zlib / none |
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
PBinCLI tool is started with `pbincli` command. Detailed help on command usage is provided with `-h` option:
|
||||||
```bash
|
```bash
|
||||||
pbincli send --text "Hello!"
|
pbincli {send|get|delete} -h
|
||||||
```
|
```
|
||||||
|
|
||||||
Or use stdin input to read text for paste:
|
## Sending
|
||||||
|
|
||||||
|
* Sending text:
|
||||||
|
```bash
|
||||||
|
pbincli send -t "Hello! This is a test paste!"
|
||||||
|
```
|
||||||
|
|
||||||
|
* Using stdin input to read text into a paste:
|
||||||
```bash
|
```bash
|
||||||
pbincli send - <<EOF
|
pbincli send - <<EOF
|
||||||
Hello! This is test paste!
|
Hello! This is a test paste!
|
||||||
EOF
|
EOF
|
||||||
```
|
```
|
||||||
|
|
||||||
It will send string `Hello! This is test paste!` to PrivateBin.
|
* Sending a file with text attached into a paste:
|
||||||
|
|
||||||
To send file use `--file` or `-f` with filename. Example:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pbincli send -c "My document" -f info.pdf
|
pbincli send -f info.pdf -t "I'm sending my document."
|
||||||
```
|
```
|
||||||
|
|
||||||
To retrieve paste from server, use `get` command with paste info.
|
* Sending a file only with no text attached:
|
||||||
|
|
||||||
It must be formated like `pasteID#passphrase`. Example:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pbincli get 49eeb1326cfa9491#vfeortoVWaYeJlviDdhxQBtj5e0I2kArpynrtu/tnGs=
|
pbincli send -q -f info.pdf
|
||||||
```
|
|
||||||
More info you can find by typing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pbincli [-h] {send, get, delete}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
TODO
|
### Other options
|
||||||
----
|
|
||||||
Write a more complete usage documentation.
|
|
||||||
|
|
||||||
License
|
It is also possible to set-up paste parameters such as "burn after reading", expiritaion time, formatting, enabling discussions and changing compression algorithm. Please refer to `pbincli send -h` output for more information.
|
||||||
-------
|
|
||||||
This project is licensed under the MIT license, which can be found in the file
|
## Receiving
|
||||||
[LICENSE](https://github.com/r4sas/PBinCLI/blob/master/LICENSE) in the root of the project source code.
|
|
||||||
|
To retrieve a paste from a server, you need to use `get` command with the paste info.
|
||||||
|
|
||||||
|
Paste info must be formated as `pasteID#Passphrase` or just use full URL to a paste. Example:
|
||||||
|
```bash
|
||||||
|
pbincli get "xxx#yyy" ### receive paste xxx from https://paste.i2pd.xyz/ by default
|
||||||
|
pbincli get "https://example.com/?xxx#yyy" ### receive paste xxx from https://example.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deletion
|
||||||
|
|
||||||
|
To delete a paste from a server, use `delete` command with paste info:
|
||||||
|
```bash
|
||||||
|
pbincli delete "pasteid=xxx&deletetoken=yyy" ### delete paste xxx from https://paste.i2pd.xyz/ by default
|
||||||
|
pbincli delete "https://example.com/?pasteid=xxx&deletetoken=yyy" ### delete paste xxx from https://example.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to delete a paste on different server than the configured one, use `-s` option together with the instance URL.
|
||||||
|
|
||||||
|
# Additional examples
|
||||||
|
|
||||||
|
Here you can find additional examples.
|
||||||
|
|
||||||
|
## Usage with I2P enabled services
|
||||||
|
|
||||||
|
Change settings to set server to `http://privatebin.i2p/` and proxy to `http://127.0.0.1:4444`. Configuration file for this example is:
|
||||||
|
```ini
|
||||||
|
server=http://privatebin.i2p/
|
||||||
|
proxy=http://127.0.0.1:4444
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using aliases
|
||||||
|
|
||||||
|
Example of alias to send a paste from `stdin` direclty to I2P service:
|
||||||
|
```bash
|
||||||
|
alias pastei2p="echo 'paste the text to stdin' && pbincli send -s http://privatebin.i2p/ -x http://127.0.0.1:4444 -"
|
||||||
|
```
|
||||||
|
|
||||||
|
Call it by running `pastei2p` in terminal.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
This project is licensed under the MIT license, which can be found in the file [LICENSE](https://github.com/r4sas/PBinCLI/blob/master/LICENSE) in the root of the project source code.
|
||||||
|
190
README.rst
190
README.rst
@ -10,82 +10,210 @@
|
|||||||
:alt: GitHub tag
|
:alt: GitHub tag
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://api.codacy.com/project/badge/Grade/4f24f43356a84621bbd9078c4b3f1b70
|
.. image:: https://app.codacy.com/project/badge/Grade/4f24f43356a84621bbd9078c4b3f1b70
|
||||||
:target: https://www.codacy.com/app/r4sas/PBinCLI?utm_source=github.com&utm_medium=referral&utm_content=r4sas/PBinCLI&utm_campaign=Badge_Grade
|
:target: https://www.codacy.com/gh/r4sas/PBinCLI/dashboard?utm_source=github.com&utm_medium=referral&utm_content=r4sas/PBinCLI&utm_campaign=Badge_Grade
|
||||||
:alt: Codacy Badge
|
:alt: Codacy Badge
|
||||||
|
|
||||||
|
|
||||||
PBinCLI
|
PBinCLI
|
||||||
=======
|
=======
|
||||||
|
|
||||||
PBinCLI is command line client for `PrivateBin <https://github.com/PrivateBin/PrivateBin/>`_ written on Python 3.
|
PBinCLI is a command line client for `PrivateBin <https://github.com/PrivateBin/PrivateBin/>`_ written in Python 3.
|
||||||
|
|
||||||
Installing
|
Installation
|
||||||
----------
|
============
|
||||||
|
|
||||||
|
Installing globally using pip3:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
virtualenv --python=python3 venv
|
pip3 install pbincli
|
||||||
|
|
||||||
|
Installing with ``virtualenv``\ :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
python3 -m virtualenv --python=python3 venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pip install pbincli
|
pip install pbincli
|
||||||
|
|
||||||
Usage
|
*Note*\ : if you used ``virtualenv`` installation method, don't forget to activate your virtual environment before running the tool: call ``. /path/to/venv/bin/activate`` in terminal
|
||||||
-----
|
|
||||||
|
|
||||||
By default pbincli configured to use ``https://paste.i2pd.xyz/`` for sending and receiving pastes. No proxy used by default.
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
You can create config file with variables ``server`` and ``proxy`` in ``~/.config/pbincli/pbincli.conf`` to use different settings.
|
By default PBinCLI is configured to use ``https://paste.i2pd.xyz/`` for sending and receiving pastes. No proxy is used by default.
|
||||||
|
|
||||||
Example contents:
|
You can always create a config file to use different settings.
|
||||||
|
|
||||||
|
Configuration file is expected to be found in ``~/.config/pbincli/pbincli.conf``\ , ``%APPDATA%/pbincli/pbincli.conf`` (Windows) and ``~/Library/Application Support/pbincli/pbincli.conf`` (MacOS)
|
||||||
|
|
||||||
|
Example of config file content
|
||||||
|
------------------------------
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
server=https://paste.i2pd.xyz/
|
server=https://paste.i2pd.xyz/
|
||||||
proxy=http://127.0.0.1:3128
|
proxy=http://127.0.0.1:3128
|
||||||
|
|
||||||
Run inside ``venv`` command:
|
List of OPTIONS available
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Option
|
||||||
|
- Default
|
||||||
|
- Possible value
|
||||||
|
* - server
|
||||||
|
- https://paste.i2pd.xyz/
|
||||||
|
- Domain ending with slash
|
||||||
|
* - mirrors
|
||||||
|
- None
|
||||||
|
- Domains separated with comma, like ``http://privatebin.ygg/,http://privatebin.i2p/``
|
||||||
|
* - proxy
|
||||||
|
- None
|
||||||
|
- Proxy address starting with scheme ``http://`` or ``socks5://``
|
||||||
|
* - expire
|
||||||
|
- 1day
|
||||||
|
- 5min / 10min / 1hour / 1day / 1week / 1month / 1year / never
|
||||||
|
* - burn
|
||||||
|
- False
|
||||||
|
- True / False
|
||||||
|
* - discus
|
||||||
|
- False
|
||||||
|
- True / False
|
||||||
|
* - format
|
||||||
|
- plaintext
|
||||||
|
- plaintext / syntaxhighlighting / markdown
|
||||||
|
* - short
|
||||||
|
- False
|
||||||
|
- True / False
|
||||||
|
* - short_api
|
||||||
|
- None
|
||||||
|
- ``tinyurl``\ , ``clckru``\ , ``isgd``\ , ``vgd``\ , ``cuttly``\ , ``yourls``\ , ``custom``
|
||||||
|
* - short_url
|
||||||
|
- None
|
||||||
|
- Domain name of shortener service for ``yourls``\ , or URL (with required parameters) for ``custom``
|
||||||
|
* - short_user
|
||||||
|
- None
|
||||||
|
- Used only in ``yourls``
|
||||||
|
* - short_pass
|
||||||
|
- None
|
||||||
|
- Used only in ``yourls``
|
||||||
|
* - short_token
|
||||||
|
- None
|
||||||
|
- Used only in ``yourls``
|
||||||
|
* - no_check_certificate
|
||||||
|
- False
|
||||||
|
- True / False
|
||||||
|
* - no_insecure_warning
|
||||||
|
- False
|
||||||
|
- True / False
|
||||||
|
* - compression
|
||||||
|
- zlib
|
||||||
|
- zlib / none
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
PBinCLI tool is started with ``pbincli`` command. Detailed help on command usage is provided with ``-h`` option:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pbincli send --text "Hello!"
|
pbincli {send|get|delete} -h
|
||||||
|
|
||||||
Or use stdin input to read text for paste:
|
Sending
|
||||||
|
-------
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
Sending text:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
pbincli send -t "Hello! This is a test paste!"
|
||||||
|
|
||||||
|
*
|
||||||
|
Using stdin input to read text into a paste:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pbincli send - <<EOF
|
pbincli send - <<EOF
|
||||||
Hello! This is test paste!
|
Hello! This is a test paste!
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
It will send string ``Hello! This is test paste!`` to PrivateBin.
|
*
|
||||||
|
Sending a file with text attached into a paste:
|
||||||
To send file use ``--file`` or ``-f`` with filename. Example:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pbincli send -c "My document" -f info.pdf
|
pbincli send -f info.pdf -t "I'm sending my document."
|
||||||
|
|
||||||
To retrieve paste from server, use ``get`` command with paste info.
|
*
|
||||||
|
Sending a file only with no text attached:
|
||||||
It must be formated like ``pasteID#passphrase``. Example:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pbincli get 49eeb1326cfa9491#vfeortoVWaYeJlviDdhxQBtj5e0I2kArpynrtu/tnGs=
|
pbincli send -q -f info.pdf
|
||||||
|
|
||||||
More info you can find by typing
|
Other options
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
It is also possible to set-up paste parameters such as "burn after reading", expiritaion time, formatting, enabling discussions and changing compression algorithm. Please refer to ``pbincli send -h`` output for more information.
|
||||||
|
|
||||||
|
Receiving
|
||||||
|
---------
|
||||||
|
|
||||||
|
To retrieve a paste from a server, you need to use ``get`` command with the paste info.
|
||||||
|
|
||||||
|
Paste info must be formated as ``pasteID#Passphrase`` or just use full URL to a paste. Example:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pbincli [-h] {send, get, delete}
|
pbincli get "xxx#yyy" ### receive paste xxx from https://paste.i2pd.xyz/ by default
|
||||||
|
pbincli get "https://example.com/?xxx#yyy" ### receive paste xxx from https://example.com/
|
||||||
|
|
||||||
TODO
|
Deletion
|
||||||
----
|
--------
|
||||||
|
|
||||||
Write a more complete usage documentation.
|
To delete a paste from a server, use ``delete`` command with paste info:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
pbincli delete "pasteid=xxx&deletetoken=yyy" ### delete paste xxx from https://paste.i2pd.xyz/ by default
|
||||||
|
pbincli delete "https://example.com/?pasteid=xxx&deletetoken=yyy" ### delete paste xxx from https://example.com/
|
||||||
|
|
||||||
|
If you need to delete a paste on different server than the configured one, use ``-s`` option together with the instance URL.
|
||||||
|
|
||||||
|
Additional examples
|
||||||
|
===================
|
||||||
|
|
||||||
|
Here you can find additional examples.
|
||||||
|
|
||||||
|
Usage with I2P enabled services
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Change settings to set server to ``http://privatebin.i2p/`` and proxy to ``http://127.0.0.1:4444``. Configuration file for this example is:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
server=http://privatebin.i2p/
|
||||||
|
proxy=http://127.0.0.1:4444
|
||||||
|
|
||||||
|
Using aliases
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Example of alias to send a paste from ``stdin`` direclty to I2P service:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
alias pastei2p="echo 'paste the text to stdin' && pbincli send -s http://privatebin.i2p/ -x http://127.0.0.1:4444 -"
|
||||||
|
|
||||||
|
Call it by running ``pastei2p`` in terminal.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
=======
|
||||||
|
|
||||||
This project is licensed under the MIT license, which can be found in the file
|
This project is licensed under the MIT license, which can be found in the file `LICENSE <https://github.com/r4sas/PBinCLI/blob/master/LICENSE>`_ in the root of the project source code.
|
||||||
`LICENSE <https://github.com/r4sas/PBinCLI/blob/master/LICENSE>`_ in the root of the project source code.
|
|
||||||
|
BIN
contrib/privatebin.ico
Normal file
BIN
contrib/privatebin.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
67
pbincli.spec
Normal file
67
pbincli.spec
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
|
||||||
|
from pkg_resources import parse_version
|
||||||
|
from PyInstaller.utils.win32.versioninfo import VSVersionInfo, FixedFileInfo, StringFileInfo, StringTable, StringStruct, VarFileInfo, VarStruct
|
||||||
|
from pbincli.__init__ import __version__ as pbincli_version, __copyright__ as pbincli_copyright
|
||||||
|
|
||||||
|
pbincli_ver = parse_version(pbincli_version)
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
a = Analysis(['pbincli\\cli.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher)
|
||||||
|
exe = EXE(pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='pbincli-' + pbincli_version,
|
||||||
|
version=VSVersionInfo(
|
||||||
|
ffi=FixedFileInfo(
|
||||||
|
filevers=(pbincli_ver.major, pbincli_ver.minor, pbincli_ver.micro, 0),
|
||||||
|
prodvers=(pbincli_ver.major, pbincli_ver.minor, pbincli_ver.micro, 0),
|
||||||
|
mask=0x3f,
|
||||||
|
flags=0x0,
|
||||||
|
OS=0x40004,
|
||||||
|
fileType=0x1,
|
||||||
|
subtype=0x0,
|
||||||
|
date=(0, 0)
|
||||||
|
),
|
||||||
|
kids=[
|
||||||
|
StringFileInfo([
|
||||||
|
StringTable(
|
||||||
|
u'040904B0',
|
||||||
|
[
|
||||||
|
StringStruct(u'FileDescription', u'PrivateBin CLI'),
|
||||||
|
StringStruct(u'FileVersion', pbincli_version),
|
||||||
|
StringStruct(u'InternalName', u'pbincli'),
|
||||||
|
StringStruct(u'LegalCopyright', pbincli_copyright),
|
||||||
|
StringStruct(u'OriginalFilename', u'pbincli-' + pbincli_version + u'.exe'),
|
||||||
|
StringStruct(u'ProductName', u'PBinCLI'),
|
||||||
|
StringStruct(u'ProductVersion', pbincli_version)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
|
||||||
|
]
|
||||||
|
),
|
||||||
|
icon=['contrib\\privatebin.ico'],
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True)
|
@ -2,6 +2,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
__author__ = "R4SAS <r4sas@i2pmail.org>"
|
__author__ = "R4SAS <r4sas@i2pmail.org>"
|
||||||
__version__ = "0.3.1"
|
__version__ = "0.3.4"
|
||||||
__copyright__ = "Copyright (c) R4SAS"
|
__copyright__ = "Copyright (c) R4SAS"
|
||||||
__license__ = "MIT"
|
__license__ = "MIT"
|
||||||
|
@ -1,38 +1,47 @@
|
|||||||
|
import signal, sys
|
||||||
|
from urllib.parse import parse_qsl
|
||||||
|
|
||||||
|
from pbincli.api import Shortener
|
||||||
from pbincli.format import Paste
|
from pbincli.format import Paste
|
||||||
from pbincli.utils import PBinCLIError
|
from pbincli.utils import PBinCLIError, check_writable, json_encode, uri_validator, validate_url_ending
|
||||||
import signal
|
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
def signal_handler(sig, frame):
|
||||||
print('Keyboard interrupt received, terminating...')
|
print('Keyboard interrupt received, terminating…')
|
||||||
exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
|
||||||
def send(args, api_client, settings=None):
|
def send(args, api_client, settings=None):
|
||||||
from pbincli.api import Shortener
|
if settings['short']:
|
||||||
if args.short:
|
|
||||||
shortener = Shortener(settings)
|
shortener = Shortener(settings)
|
||||||
|
|
||||||
if not args.notext:
|
if not args.notext:
|
||||||
if args.text:
|
if args.text:
|
||||||
text = args.text
|
text = args.text
|
||||||
elif args.stdin:
|
elif args.stdin:
|
||||||
|
print("Reading text from stdin…")
|
||||||
text = args.stdin.read()
|
text = args.stdin.read()
|
||||||
elif not args.file:
|
elif not args.file:
|
||||||
PBinCLIError("Nothing to send!")
|
PBinCLIError("Nothing to send!")
|
||||||
else:
|
else:
|
||||||
text = ""
|
text = ""
|
||||||
|
|
||||||
|
print("Preparing paste…")
|
||||||
paste = Paste(args.debug)
|
paste = Paste(args.debug)
|
||||||
|
|
||||||
|
if args.verbose: print("Used server: {}".format(api_client.getServer()))
|
||||||
|
|
||||||
# get from server supported paste format version and update object
|
# get from server supported paste format version and update object
|
||||||
|
if args.verbose: print("Getting supported paste format version from server…")
|
||||||
version = api_client.getVersion()
|
version = api_client.getVersion()
|
||||||
paste.setVersion(version)
|
paste.setVersion(version)
|
||||||
|
|
||||||
|
if args.verbose: print("Filling paste with data…")
|
||||||
# set compression type, works only on v2 pastes
|
# set compression type, works only on v2 pastes
|
||||||
if version == 2:
|
if version == 2:
|
||||||
paste.setCompression(args.compression)
|
paste.setCompression(settings['compression'])
|
||||||
|
|
||||||
# add text in paste (if it provided)
|
# add text in paste (if it provided)
|
||||||
paste.setText(text)
|
paste.setText(text)
|
||||||
@ -45,21 +54,22 @@ def send(args, api_client, settings=None):
|
|||||||
if args.file:
|
if args.file:
|
||||||
paste.setAttachment(args.file)
|
paste.setAttachment(args.file)
|
||||||
|
|
||||||
|
if args.verbose: print("Encrypting paste…")
|
||||||
paste.encrypt(
|
paste.encrypt(
|
||||||
formatter = args.format,
|
formatter = settings['format'],
|
||||||
burnafterreading = args.burn,
|
burnafterreading = settings['burn'],
|
||||||
discussion = args.discus,
|
discussion = settings['discus'],
|
||||||
expiration = args.expire)
|
expiration = settings['expire'])
|
||||||
|
|
||||||
|
if args.verbose: print("Sending request to server…")
|
||||||
request = paste.getJSON()
|
request = paste.getJSON()
|
||||||
|
|
||||||
if args.debug:
|
if args.debug: print("Passphrase:\t{}\nRequest:\t{}".format(paste.getHash(), request))
|
||||||
print("Passphrase:\t{}".format(paste.getHash()))
|
|
||||||
print("Request:\t{}".format(request))
|
|
||||||
|
|
||||||
# If we use dry option, exit now
|
# If we use dry option, exit now
|
||||||
if args.dry: exit(0)
|
if args.dry: sys.exit(0)
|
||||||
|
|
||||||
|
print("Uploading paste…")
|
||||||
result = api_client.post(request)
|
result = api_client.post(request)
|
||||||
|
|
||||||
if args.debug: print("Response:\t{}\n".format(result))
|
if args.debug: print("Response:\t{}\n".format(result))
|
||||||
@ -68,20 +78,41 @@ def send(args, api_client, settings=None):
|
|||||||
if not result['status']: # return code is zero
|
if not result['status']: # return code is zero
|
||||||
passphrase = paste.getHash()
|
passphrase = paste.getHash()
|
||||||
|
|
||||||
print("Paste uploaded!\nPasteID:\t{}\nPassword:\t{}\nDelete token:\t{}\n\nLink:\t\t{}?{}#{}".format(
|
# Paste information
|
||||||
|
print("Paste uploaded!\nPasteID:\t{}\nPassword:\t{}\nDelete token:\t{}".format(
|
||||||
result['id'],
|
result['id'],
|
||||||
passphrase,
|
passphrase,
|
||||||
result['deletetoken'],
|
result['deletetoken']))
|
||||||
|
|
||||||
|
# Paste link
|
||||||
|
print("\nLink:\t\t{}?{}#{}".format(
|
||||||
settings['server'],
|
settings['server'],
|
||||||
result['id'],
|
result['id'],
|
||||||
passphrase))
|
passphrase))
|
||||||
elif result['status']: # return code is other then zero
|
|
||||||
PBinCLIError("Something went wrong...\nError:\t\t{}".format(result['message']))
|
|
||||||
else: # or here no status field in response or it is empty
|
|
||||||
PBinCLIError("Something went wrong...\nError: Empty response.")
|
|
||||||
|
|
||||||
if args.short:
|
# Paste deletion link
|
||||||
print("\nQuerying URL shortening service...")
|
print("Delete Link:\t{}?pasteid={}&deletetoken={}".format(
|
||||||
|
settings['server'],
|
||||||
|
result['id'],
|
||||||
|
result['deletetoken']))
|
||||||
|
|
||||||
|
# Print links to mirrors if present
|
||||||
|
if settings['mirrors']:
|
||||||
|
print("\nMirrors:")
|
||||||
|
urls = settings['mirrors'].split(',')
|
||||||
|
for x in urls:
|
||||||
|
print("\t\t{}?{}#{}".format(
|
||||||
|
validate_url_ending(x),
|
||||||
|
result['id'],
|
||||||
|
passphrase))
|
||||||
|
|
||||||
|
elif result['status']: # return code is other then zero
|
||||||
|
PBinCLIError("Something went wrong…\nError:\t\t{}".format(result['message']))
|
||||||
|
else: # or here no status field in response or it is empty
|
||||||
|
PBinCLIError("Something went wrong…\nError: Empty response.")
|
||||||
|
|
||||||
|
if settings['short']:
|
||||||
|
print("\nQuerying URL shortening service…")
|
||||||
shortener.getlink("{}?{}#{}".format(
|
shortener.getlink("{}?{}#{}".format(
|
||||||
settings['server'],
|
settings['server'],
|
||||||
result['id'],
|
result['id'],
|
||||||
@ -89,16 +120,19 @@ def send(args, api_client, settings=None):
|
|||||||
|
|
||||||
|
|
||||||
def get(args, api_client, settings=None):
|
def get(args, api_client, settings=None):
|
||||||
from pbincli.utils import check_writable, json_encode
|
parseduri, isuri = uri_validator(args.pasteinfo)
|
||||||
|
|
||||||
try:
|
if isuri and parseduri.query and parseduri.fragment:
|
||||||
pasteid, passphrase = args.pasteinfo.split("#")
|
api_client.server = args.pasteinfo.split("?")[0]
|
||||||
except ValueError:
|
pasteid = parseduri.query
|
||||||
PBinCLIError("Provided info hasn't contain valid PasteID#Passphrase string")
|
passphrase = parseduri.fragment
|
||||||
|
elif parseduri.path and parseduri.path != "/" and parseduri.fragment:
|
||||||
if not (pasteid and passphrase):
|
pasteid = parseduri.path
|
||||||
PBinCLIError("Incorrect request")
|
passphrase = parseduri.fragment
|
||||||
|
else:
|
||||||
|
PBinCLIError("Provided info hasn't contain valid URL or PasteID#Passphrase string")
|
||||||
|
|
||||||
|
if args.verbose: print("Used server: {}".format(api_client.getServer()))
|
||||||
if args.debug: print("PasteID:\t{}\nPassphrase:\t{}".format(pasteid, passphrase))
|
if args.debug: print("PasteID:\t{}\nPassphrase:\t{}".format(pasteid, passphrase))
|
||||||
|
|
||||||
paste = Paste(args.debug)
|
paste = Paste(args.debug)
|
||||||
@ -107,13 +141,14 @@ def get(args, api_client, settings=None):
|
|||||||
paste.setPassword(args.password)
|
paste.setPassword(args.password)
|
||||||
if args.debug: print("Password:\t{}".format(args.password))
|
if args.debug: print("Password:\t{}".format(args.password))
|
||||||
|
|
||||||
|
if args.verbose: print("Requesting paste from server…")
|
||||||
result = api_client.get(pasteid)
|
result = api_client.get(pasteid)
|
||||||
|
|
||||||
if args.debug: print("Response:\t{}\n".format(result))
|
if args.debug: print("Response:\t{}\n".format(result))
|
||||||
|
|
||||||
# Paste was received. Checking received status code
|
# Paste was received. Checking received status code
|
||||||
if not result['status']: # return code is zero
|
if not result['status']: # return code is zero
|
||||||
print("Paste received!")
|
print("Paste received! Decoding…")
|
||||||
|
|
||||||
version = result['v'] if 'v' in result else 1
|
version = result['v'] if 'v' in result else 1
|
||||||
paste.setVersion(version)
|
paste.setVersion(version)
|
||||||
@ -150,21 +185,32 @@ def get(args, api_client, settings=None):
|
|||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
if version == 1 and 'meta' in result and 'burnafterreading' in result['meta'] and result['meta']['burnafterreading']:
|
if version == 1 and 'meta' in result and 'burnafterreading' in result['meta'] and result['meta']['burnafterreading']:
|
||||||
print("Burn afrer reading flag found. Deleting paste...")
|
print("Burn afrer reading flag found. Deleting paste…")
|
||||||
api_client.delete(json_encode({'pasteid':pasteid,'deletetoken':'burnafterreading'}))
|
api_client.delete(json_encode({'pasteid':pasteid,'deletetoken':'burnafterreading'}))
|
||||||
|
|
||||||
elif result['status']: # return code is other then zero
|
elif result['status']: # return code is other then zero
|
||||||
PBinCLIError("Something went wrong...\nError:\t\t{}".format(result['message']))
|
PBinCLIError("Something went wrong…\nError:\t\t{}".format(result['message']))
|
||||||
else: # or here no status field in response or it is empty
|
else: # or here no status field in response or it is empty
|
||||||
PBinCLIError("Something went wrong...\nError: Empty response.")
|
PBinCLIError("Something went wrong…\nError: Empty response.")
|
||||||
|
|
||||||
|
|
||||||
def delete(args, api_client, settings=None):
|
def delete(args, api_client, settings=None):
|
||||||
from pbincli.utils import json_encode
|
parseduri, isuri = uri_validator(args.pasteinfo)
|
||||||
|
|
||||||
pasteid = args.paste
|
if isuri:
|
||||||
token = args.token
|
api_client.server = args.pasteinfo.split("?")[0]
|
||||||
|
query = dict(parse_qsl(parseduri.query))
|
||||||
|
else:
|
||||||
|
query = dict(parse_qsl(args.pasteinfo))
|
||||||
|
|
||||||
|
if 'pasteid' in query and 'deletetoken' in query:
|
||||||
|
pasteid = query['pasteid']
|
||||||
|
token = query['deletetoken']
|
||||||
|
else:
|
||||||
|
PBinCLIError("Provided info hasn't contain required information")
|
||||||
|
|
||||||
|
if args.verbose: print("Used server: {}".format(api_client.getServer()))
|
||||||
if args.debug: print("PasteID:\t{}\nToken:\t\t{}".format(pasteid, token))
|
if args.debug: print("PasteID:\t{}\nToken:\t\t{}".format(pasteid, token))
|
||||||
|
|
||||||
|
print("Requesting paste deletion…")
|
||||||
api_client.delete(json_encode({'pasteid':pasteid,'deletetoken':token}))
|
api_client.delete(json_encode({'pasteid':pasteid,'deletetoken':token}))
|
||||||
|
@ -78,6 +78,10 @@ class PrivateBin:
|
|||||||
'@value' in jsonldSchema['@context']['v']) \
|
'@value' in jsonldSchema['@context']['v']) \
|
||||||
else 1
|
else 1
|
||||||
|
|
||||||
|
def getServer(self):
|
||||||
|
return self.server
|
||||||
|
|
||||||
|
|
||||||
class Shortener:
|
class Shortener:
|
||||||
"""Some parts of this class was taken from
|
"""Some parts of this class was taken from
|
||||||
python-yourls (https://github.com/tflink/python-yourls/) library
|
python-yourls (https://github.com/tflink/python-yourls/) library
|
||||||
@ -85,12 +89,17 @@ class Shortener:
|
|||||||
def __init__(self, settings=None):
|
def __init__(self, settings=None):
|
||||||
self.api = settings['short_api']
|
self.api = settings['short_api']
|
||||||
|
|
||||||
|
if self.api is None:
|
||||||
|
PBinCLIError("Unable to activate link shortener without short_api.")
|
||||||
|
|
||||||
# we checking which service is used, because some services doesn't require
|
# we checking which service is used, because some services doesn't require
|
||||||
# any authentication, or have only one domain on which it working
|
# any authentication, or have only one domain on which it working
|
||||||
if self.api == 'yourls':
|
if self.api == 'yourls':
|
||||||
self._yourls_init(settings)
|
self._yourls_init(settings)
|
||||||
elif self.api == 'isgd' or self.api == 'vgd':
|
elif self.api == 'isgd' or self.api == 'vgd':
|
||||||
self._gd_init()
|
self._gd_init()
|
||||||
|
elif self.api == 'custom':
|
||||||
|
self.apiurl = settings['short_url']
|
||||||
|
|
||||||
self.session, self.proxy = _config_requests(settings)
|
self.session, self.proxy = _config_requests(settings)
|
||||||
|
|
||||||
@ -137,7 +146,8 @@ class Shortener:
|
|||||||
'tinyurl': self._tinyurl,
|
'tinyurl': self._tinyurl,
|
||||||
'isgd': self._gd,
|
'isgd': self._gd,
|
||||||
'vgd': self._gd,
|
'vgd': self._gd,
|
||||||
'cuttly': self._cuttly
|
'cuttly': self._cuttly,
|
||||||
|
'custom': self._custom
|
||||||
}
|
}
|
||||||
# run function selected by choosen API
|
# run function selected by choosen API
|
||||||
servicesList[self.api](url)
|
servicesList[self.api](url)
|
||||||
@ -246,3 +256,20 @@ class Shortener:
|
|||||||
print("Short Link:\t{}".format(result.text))
|
print("Short Link:\t{}".format(result.text))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
PBinCLIError("cutt.ly: unexcepted behavior: {}".format(ex))
|
PBinCLIError("cutt.ly: unexcepted behavior: {}".format(ex))
|
||||||
|
|
||||||
|
|
||||||
|
def _custom(self, url):
|
||||||
|
if self.apiurl is None:
|
||||||
|
PBinCLIError("No short_url specified - link will not be shortened.")
|
||||||
|
|
||||||
|
from urllib.parse import quote
|
||||||
|
qUrl = quote(url, safe="") # urlencoded paste url
|
||||||
|
rUrl = self.apiurl.replace("{{url}}", qUrl)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self.session.get(
|
||||||
|
url = rUrl,
|
||||||
|
proxies = self.proxy)
|
||||||
|
print("Short Link:\t{}".format(result.text))
|
||||||
|
except Exception as ex:
|
||||||
|
PBinCLIError("Shorter: unexcepted behavior: {}".format(ex))
|
||||||
|
112
pbincli/cli.py
112
pbincli/cli.py
@ -1,12 +1,21 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import os, sys, argparse
|
import os, sys, argparse
|
||||||
|
from distutils.util import strtobool
|
||||||
|
|
||||||
import pbincli.actions
|
import pbincli.actions
|
||||||
from pbincli.api import PrivateBin
|
from pbincli.api import PrivateBin
|
||||||
from pbincli.utils import PBinCLIException, PBinCLIError, validate_url
|
from pbincli.utils import PBinCLIException, PBinCLIError, validate_url_ending
|
||||||
|
|
||||||
|
CONFIG_PATHS = [
|
||||||
|
os.path.join(".", "pbincli.conf", ),
|
||||||
|
os.path.join(os.getenv("HOME") or "~", ".config", "pbincli", "pbincli.conf")
|
||||||
|
]
|
||||||
|
|
||||||
|
if sys.platform == "win32":
|
||||||
|
CONFIG_PATHS.append(os.path.join(os.getenv("APPDATA"), "pbincli", "pbincli.conf"))
|
||||||
|
elif sys.platform == "darwin":
|
||||||
|
CONFIG_PATHS.append(os.path.join(os.getenv("HOME") or "~", "Library", "Application Support", "pbincli", "pbincli.conf"))
|
||||||
|
|
||||||
CONFIG_PATHS = [os.path.join(".", "pbincli.conf", ),
|
|
||||||
os.path.join(os.getenv("HOME") or "~", ".config", "pbincli", "pbincli.conf") ]
|
|
||||||
|
|
||||||
def read_config(filename):
|
def read_config(filename):
|
||||||
"""Read config variables from a file"""
|
"""Read config variables from a file"""
|
||||||
@ -16,103 +25,119 @@ def read_config(filename):
|
|||||||
if len(l.strip()) == 0:
|
if len(l.strip()) == 0:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
key, value = l.strip().split("=")
|
key, value = l.strip().split("=", 1)
|
||||||
|
if value.strip().lower() in ['true', 'false']:
|
||||||
|
settings[key.strip()] = bool(strtobool(value.strip()))
|
||||||
|
else:
|
||||||
settings[key.strip()] = value.strip()
|
settings[key.strip()] = value.strip()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
PBinCLIError("Unable to parse config file, please check it for errors.")
|
PBinCLIError("Unable to parse config file, please check it for errors.")
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser(description='Full-featured PrivateBin command-line client')
|
||||||
subparsers = parser.add_subparsers(title="actions", help="List of commands")
|
subparsers = parser.add_subparsers(title="actions", help="List of commands")
|
||||||
|
|
||||||
# a send command
|
# a send command
|
||||||
send_parser = subparsers.add_parser("send", description="Send data to PrivateBin instance")
|
send_parser = subparsers.add_parser("send", description="Send data to PrivateBin instance")
|
||||||
send_parser.add_argument("-t", "--text", help="text in quotes. Ignored if used stdin. If not used, forcefully used stdin")
|
send_parser.add_argument("-t", "--text", help="Text in quotes. Ignored if used stdin. If not used, forcefully used stdin")
|
||||||
send_parser.add_argument("-f", "--file", help="example: image.jpg or full path to file")
|
send_parser.add_argument("-f", "--file", help="Example: image.jpg or full path to file")
|
||||||
send_parser.add_argument("-p", "--password", help="password for encrypting paste")
|
send_parser.add_argument("-p", "--password", help="Password for encrypting paste")
|
||||||
send_parser.add_argument("-E", "--expire", default="1day", action="store",
|
send_parser.add_argument("-E", "--expire", default="1day", action="store",
|
||||||
choices=["5min", "10min", "1hour", "1day", "1week", "1month", "1year", "never"], help="paste lifetime (default: 1day)")
|
choices=["5min", "10min", "1hour", "1day", "1week", "1month", "1year", "never"], help="Paste lifetime (default: 1day)")
|
||||||
send_parser.add_argument("-B", "--burn", default=False, action="store_true", help="burn sent paste after reading")
|
send_parser.add_argument("-B", "--burn", default=argparse.SUPPRESS, action="store_true", help="Set \"Burn after reading\" flag")
|
||||||
send_parser.add_argument("-D", "--discus", default=False, action="store_true", help="open discussion for sent paste")
|
send_parser.add_argument("-D", "--discus", default=argparse.SUPPRESS, action="store_true", help="Open discussion for sent paste")
|
||||||
send_parser.add_argument("-F", "--format", default="plaintext", action="store",
|
send_parser.add_argument("-F", "--format", default="plaintext", action="store",
|
||||||
choices=["plaintext", "syntaxhighlighting", "markdown"], help="format of text (default: plaintext)")
|
choices=["plaintext", "syntaxhighlighting", "markdown"], help="Format of text (default: plaintext)")
|
||||||
send_parser.add_argument("-q", "--notext", default=False, action="store_true", help="don't send text in paste")
|
send_parser.add_argument("-q", "--notext", default=False, action="store_true", help="Don't send text in paste")
|
||||||
send_parser.add_argument("-c", "--compression", default="zlib", action="store",
|
send_parser.add_argument("-c", "--compression", default="zlib", action="store",
|
||||||
choices=["zlib", "none"], help="set compression for paste (default: zlib). Note: works only on v2 paste format")
|
choices=["zlib", "none"], help="Set compression for paste (default: zlib). Note: works only on v2 paste format")
|
||||||
## URL shortener
|
## URL shortener
|
||||||
send_parser.add_argument("-S", "--short", default=False, action="store_true", help="use URL shortener")
|
send_parser.add_argument("-S", "--short", default=argparse.SUPPRESS, action="store_true", help="Use URL shortener")
|
||||||
send_parser.add_argument("--short-api", default=argparse.SUPPRESS, action="store",
|
send_parser.add_argument("--short-api", default=argparse.SUPPRESS, action="store",
|
||||||
choices=["tinyurl", "clckru", "isgd", "vgd", "cuttly", "yourls"], help="API used by shortener service")
|
choices=["tinyurl", "clckru", "isgd", "vgd", "cuttly", "yourls", "custom"], help="API used by shortener service")
|
||||||
send_parser.add_argument("--short-url", default=argparse.SUPPRESS, help="URL of shortener service API")
|
send_parser.add_argument("--short-url", default=argparse.SUPPRESS, help="URL of shortener service API")
|
||||||
send_parser.add_argument("--short-user", default=argparse.SUPPRESS, help="Shortener username")
|
send_parser.add_argument("--short-user", default=argparse.SUPPRESS, help="Shortener username")
|
||||||
send_parser.add_argument("--short-pass", default=argparse.SUPPRESS, help="Shortener password")
|
send_parser.add_argument("--short-pass", default=argparse.SUPPRESS, help="Shortener password")
|
||||||
send_parser.add_argument("--short-token", default=argparse.SUPPRESS, help="Shortener token")
|
send_parser.add_argument("--short-token", default=argparse.SUPPRESS, help="Shortener token")
|
||||||
## Connection options
|
## Connection options
|
||||||
send_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="PrivateBin service URL (default: https://paste.i2pd.xyz/)")
|
send_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="Instance URL (default: https://paste.i2pd.xyz/)")
|
||||||
send_parser.add_argument("-x", "--proxy", default=argparse.SUPPRESS, help="Proxy server address (default: None)")
|
send_parser.add_argument("-x", "--proxy", default=argparse.SUPPRESS, help="Proxy server address (default: None)")
|
||||||
send_parser.add_argument("--no-check-certificate", default=False, action="store_true", help="disable certificate validation")
|
send_parser.add_argument("--no-check-certificate", default=argparse.SUPPRESS, action="store_true", help="Disable certificate validation")
|
||||||
send_parser.add_argument("--no-insecure-warning", default=False, action="store_true",
|
send_parser.add_argument("--no-insecure-warning", default=argparse.SUPPRESS, action="store_true",
|
||||||
help="suppress InsecureRequestWarning (only with --no-check-certificate)")
|
help="Suppress InsecureRequestWarning (only with --no-check-certificate)")
|
||||||
##
|
##
|
||||||
send_parser.add_argument("-d", "--debug", default=False, action="store_true", help="enable debug")
|
send_parser.add_argument("-L", "--mirrors", default=argparse.SUPPRESS, help="Comma-separated list of mirrors of service with scheme (default: None)")
|
||||||
send_parser.add_argument("--dry", default=False, action="store_true", help="invoke dry run")
|
send_parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Enable verbose output")
|
||||||
send_parser.add_argument("stdin", help="input paste text from stdin", nargs="?", type=argparse.FileType("r"), default=sys.stdin)
|
send_parser.add_argument("-d", "--debug", default=False, action="store_true", help="Enable debug output")
|
||||||
|
send_parser.add_argument("--dry", default=False, action="store_true", help="Invoke dry run")
|
||||||
|
send_parser.add_argument("stdin", help="Input paste text from stdin", nargs="?", type=argparse.FileType("r"), default=sys.stdin)
|
||||||
send_parser.set_defaults(func=pbincli.actions.send)
|
send_parser.set_defaults(func=pbincli.actions.send)
|
||||||
|
|
||||||
# a get command
|
# a get command
|
||||||
get_parser = subparsers.add_parser("get", description="Get data from PrivateBin instance")
|
get_parser = subparsers.add_parser("get", description="Get data from PrivateBin instance")
|
||||||
get_parser.add_argument("pasteinfo", help="example: aabb#cccddd")
|
get_parser.add_argument("pasteinfo", help="\"PasteID#Passphrase\" or full URL")
|
||||||
get_parser.add_argument("-p", "--password", help="password for decrypting paste")
|
get_parser.add_argument("-p", "--password", help="Password for decrypting paste")
|
||||||
## Connection options
|
## Connection options
|
||||||
get_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="PrivateBin service URL (default: https://paste.i2pd.xyz/)")
|
get_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="Instance URL (default: https://paste.i2pd.xyz/, ignored if URL used in pasteinfo)")
|
||||||
get_parser.add_argument("-x", "--proxy", default=argparse.SUPPRESS, help="Proxy server address (default: None)")
|
get_parser.add_argument("-x", "--proxy", default=argparse.SUPPRESS, help="Proxy server address (default: None)")
|
||||||
get_parser.add_argument("--no-check-certificate", default=False, action="store_true", help="disable certificate validation")
|
get_parser.add_argument("--no-check-certificate", default=argparse.SUPPRESS, action="store_true", help="Disable certificate validation")
|
||||||
get_parser.add_argument("--no-insecure-warning", default=False, action="store_true",
|
get_parser.add_argument("--no-insecure-warning", default=argparse.SUPPRESS, action="store_true",
|
||||||
help="suppress InsecureRequestWarning (only with --no-check-certificate)")
|
help="Suppress InsecureRequestWarning (only with --no-check-certificate)")
|
||||||
##
|
##
|
||||||
get_parser.add_argument("-d", "--debug", default=False, action="store_true", help="enable debug")
|
get_parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Enable verbose output")
|
||||||
|
get_parser.add_argument("-d", "--debug", default=False, action="store_true", help="Enable debug output")
|
||||||
get_parser.set_defaults(func=pbincli.actions.get)
|
get_parser.set_defaults(func=pbincli.actions.get)
|
||||||
|
|
||||||
# a delete command
|
# a delete command
|
||||||
delete_parser = subparsers.add_parser("delete", description="Delete paste from PrivateBin instance using token")
|
delete_parser = subparsers.add_parser("delete", description="Delete paste from PrivateBin instance")
|
||||||
delete_parser.add_argument("-p", "--paste", required=True, help="paste id")
|
delete_parser.add_argument("pasteinfo", help="Paste deletion URL or string in \"pasteid=PasteID&deletetoken=Token\" format")
|
||||||
delete_parser.add_argument("-t", "--token", required=True, help="paste deletion token")
|
|
||||||
## Connection options
|
## Connection options
|
||||||
delete_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="PrivateBin service URL (default: https://paste.i2pd.xyz/)")
|
delete_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="Instance URL (default: https://paste.i2pd.xyz/)")
|
||||||
delete_parser.add_argument("-x", "--proxy", default=argparse.SUPPRESS, help="Proxy server address (default: None)")
|
delete_parser.add_argument("-x", "--proxy", default=argparse.SUPPRESS, help="Proxy server address (default: None)")
|
||||||
delete_parser.add_argument("--no-check-certificate", default=False, action="store_true", help="disable certificate validation")
|
delete_parser.add_argument("--no-check-certificate", default=argparse.SUPPRESS, action="store_true", help="Disable certificate validation")
|
||||||
delete_parser.add_argument("--no-insecure-warning", default=False, action="store_true",
|
delete_parser.add_argument("--no-insecure-warning", default=argparse.SUPPRESS, action="store_true",
|
||||||
help="suppress InsecureRequestWarning (only with --no-check-certificate)")
|
help="Suppress InsecureRequestWarning (only with --no-check-certificate)")
|
||||||
##
|
##
|
||||||
delete_parser.add_argument("-d", "--debug", default=False, action="store_true", help="enable debug")
|
delete_parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Enable verbose output")
|
||||||
|
delete_parser.add_argument("-d", "--debug", default=False, action="store_true", help="Enable debug output")
|
||||||
delete_parser.set_defaults(func=pbincli.actions.delete)
|
delete_parser.set_defaults(func=pbincli.actions.delete)
|
||||||
|
|
||||||
# parse arguments
|
# parse arguments
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# default configuration
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
'server': 'https://paste.i2pd.xyz/',
|
'server': 'https://paste.i2pd.xyz/',
|
||||||
|
'mirrors': None,
|
||||||
'proxy': None,
|
'proxy': None,
|
||||||
|
'expire': '1day',
|
||||||
|
'burn': False,
|
||||||
|
'discus': False,
|
||||||
|
'format': None,
|
||||||
|
'short': False,
|
||||||
'short_api': None,
|
'short_api': None,
|
||||||
'short_url': None,
|
'short_url': None,
|
||||||
'short_user': None,
|
'short_user': None,
|
||||||
'short_pass': None,
|
'short_pass': None,
|
||||||
'short_token': None,
|
'short_token': None,
|
||||||
'no_check_certificate': False,
|
'no_check_certificate': False,
|
||||||
'no_insecure_warning': False
|
'no_insecure_warning': False,
|
||||||
|
'compression': None
|
||||||
}
|
}
|
||||||
|
|
||||||
# Configuration preference order:
|
# Configuration preference order:
|
||||||
# 1. Command line switches
|
# 1. Command line switches
|
||||||
# 2. Environment variables
|
# 2. Environment variables
|
||||||
# 3. Configuration file
|
# 3. Configuration file
|
||||||
# 4. Default values below
|
# 4. Defaults above
|
||||||
|
|
||||||
for p in CONFIG_PATHS:
|
for p in CONFIG_PATHS:
|
||||||
if os.path.exists(p):
|
if os.path.exists(p):
|
||||||
CONFIG.update(read_config(p))
|
fileconfig = read_config(p)
|
||||||
|
if args.debug: print("Configuration readed from file:\t{}".format(fileconfig))
|
||||||
|
CONFIG.update(fileconfig)
|
||||||
break
|
break
|
||||||
|
|
||||||
for key in CONFIG.keys():
|
for key in CONFIG.keys():
|
||||||
@ -124,8 +149,9 @@ def main():
|
|||||||
CONFIG[key] = args_var[key]
|
CONFIG[key] = args_var[key]
|
||||||
|
|
||||||
# Re-validate PrivateBin instance URL
|
# Re-validate PrivateBin instance URL
|
||||||
CONFIG['server'] = validate_url(CONFIG['server'])
|
CONFIG['server'] = validate_url_ending(CONFIG['server'])
|
||||||
|
|
||||||
|
if args.debug: print("Whole configuration:\t\t{}".format(CONFIG))
|
||||||
api_client = PrivateBin(CONFIG)
|
api_client = PrivateBin(CONFIG)
|
||||||
|
|
||||||
if hasattr(args, "func"):
|
if hasattr(args, "func"):
|
||||||
|
@ -254,6 +254,7 @@ class Paste:
|
|||||||
self._discussion = discussion
|
self._discussion = discussion
|
||||||
self._expiration = expiration
|
self._expiration = expiration
|
||||||
|
|
||||||
|
if self._debug: print("[Enc] Starting encyptor…")
|
||||||
if self._version == 2: self._encryptV2()
|
if self._version == 2: self._encryptV2()
|
||||||
else: self._encryptV1()
|
else: self._encryptV1()
|
||||||
|
|
||||||
@ -261,10 +262,13 @@ class Paste:
|
|||||||
def _encryptV2(self):
|
def _encryptV2(self):
|
||||||
from pbincli.utils import json_encode
|
from pbincli.utils import json_encode
|
||||||
|
|
||||||
|
if self._debug: print("[Enc] Preparing IV, Salt…")
|
||||||
iv = get_random_bytes(int(self._tag_bits / 8))
|
iv = get_random_bytes(int(self._tag_bits / 8))
|
||||||
salt = get_random_bytes(self._salt_bytes)
|
salt = get_random_bytes(self._salt_bytes)
|
||||||
|
if self._debug: print("[Enc] Deriving Key…")
|
||||||
key = self.__deriveKey(salt)
|
key = self.__deriveKey(salt)
|
||||||
|
|
||||||
|
if self._debug: print("[Enc] Preparing aData and message…")
|
||||||
# prepare encryption authenticated data and message
|
# prepare encryption authenticated data and message
|
||||||
adata = [
|
adata = [
|
||||||
[
|
[
|
||||||
@ -286,6 +290,7 @@ class Paste:
|
|||||||
cipher_message['attachment'] = self._attachment
|
cipher_message['attachment'] = self._attachment
|
||||||
cipher_message['attachment_name'] = self._attachment_name
|
cipher_message['attachment_name'] = self._attachment_name
|
||||||
|
|
||||||
|
if self._debug: print("[Enc] Encrypting message…")
|
||||||
cipher = self.__initializeCipher(key, iv, adata, int(self._tag_bits /8 ))
|
cipher = self.__initializeCipher(key, iv, adata, int(self._tag_bits /8 ))
|
||||||
ciphertext, tag = cipher.encrypt_and_digest(self.__compress(json_encode(cipher_message)))
|
ciphertext, tag = cipher.encrypt_and_digest(self.__compress(json_encode(cipher_message)))
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ class PBinCLIException(Exception):
|
|||||||
|
|
||||||
def PBinCLIError(message):
|
def PBinCLIError(message):
|
||||||
print("PBinCLI Error: {}".format(message), file=sys.stderr)
|
print("PBinCLI Error: {}".format(message), file=sys.stderr)
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def path_leaf(path):
|
def path_leaf(path):
|
||||||
@ -30,7 +30,16 @@ def json_encode(s):
|
|||||||
return json.dumps(s, separators=(',',':')).encode()
|
return json.dumps(s, separators=(',',':')).encode()
|
||||||
|
|
||||||
|
|
||||||
def validate_url(s):
|
def validate_url_ending(s):
|
||||||
if not s.endswith('/'):
|
if not s.endswith('/'):
|
||||||
s = s + "/"
|
s = s + "/"
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
def uri_validator(x):
|
||||||
|
from urllib.parse import urlsplit
|
||||||
|
try:
|
||||||
|
result = urlsplit(x)
|
||||||
|
isuri = all([result.scheme, result.netloc])
|
||||||
|
return result, isuri
|
||||||
|
except ValueError:
|
||||||
|
return False
|
2
pyproject.toml
Normal file
2
pyproject.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools", "wheel"]
|
Loading…
x
Reference in New Issue
Block a user