Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot-preview[bot]
051f259fa7
Upgrade to GitHub-native Dependabot 2021-04-29 21:00:42 +00:00
14 changed files with 210 additions and 612 deletions

1
.gitattributes vendored
View File

@ -1,4 +1,3 @@
.github/** export-ignore
.gitattributes export-ignore .gitattributes export-ignore
.gitignore export-ignore .gitignore export-ignore
README.md export-ignore README.md export-ignore

View File

@ -1,24 +0,0 @@
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
View File

@ -54,7 +54,6 @@ 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

136
README.md
View File

@ -1,139 +1,73 @@
[![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://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) [![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)
# PBinCLI PBinCLI
=====
PBinCLI is a command line client for [PrivateBin](https://github.com/PrivateBin/PrivateBin/) written in Python 3. PBinCLI is command line client for [PrivateBin](https://github.com/PrivateBin/PrivateBin/) written on Python 3.
# Installation Installing
-----
Installing globally using pip3:
```bash ```bash
pip3 install pbincli virtualenv --python=python3 venv
```
Installing with `virtualenv`:
```bash
python3 -m virtualenv --python=python3 venv
. venv/bin/activate . venv/bin/activate
pip install pbincli pip install pbincli
``` ```
*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 Usage
-----
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
```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
``` ```
## List of OPTIONS available Run inside `venv` command:
| 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|get|delete} -h pbincli send --text "Hello!"
``` ```
## Sending Or use stdin input to read text for paste:
* 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 a test paste! Hello! This is test paste!
EOF EOF
``` ```
* Sending a file with text attached into a paste: It will send string `Hello! This is test paste!` to PrivateBin.
To send file use `--file` or `-f` with filename. Example:
```bash ```bash
pbincli send -f info.pdf -t "I'm sending my document." pbincli send -c "My document" -f info.pdf
``` ```
* Sending a file only with no text attached: To retrieve paste from server, use `get` command with paste info.
It must be formated like `pasteID#passphrase`. Example:
```bash ```bash
pbincli send -q -f info.pdf pbincli get 49eeb1326cfa9491#vfeortoVWaYeJlviDdhxQBtj5e0I2kArpynrtu/tnGs=
``` ```
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:
```bash ```bash
pbincli get "xxx#yyy" ### receive paste xxx from https://paste.i2pd.xyz/ by default pbincli [-h] {send, get, delete}
pbincli get "https://example.com/?xxx#yyy" ### receive paste xxx from https://example.com/
``` ```
## Deletion TODO
----
Write a more complete usage documentation.
To delete a paste from a server, use `delete` command with paste info: License
```bash -------
pbincli delete "pasteid=xxx&deletetoken=yyy" ### delete paste xxx from https://paste.i2pd.xyz/ by default This project is licensed under the MIT license, which can be found in the file
pbincli delete "https://example.com/?pasteid=xxx&deletetoken=yyy" ### delete paste xxx from https://example.com/ [LICENSE](https://github.com/r4sas/PBinCLI/blob/master/LICENSE) in the root of the project source code.
```
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.

View File

@ -1,219 +1,91 @@
.. image:: https://img.shields.io/github/license/r4sas/PBinCLI.svg .. image:: https://img.shields.io/github/license/r4sas/PBinCLI.svg
:target: https://github.com/r4sas/PBinCLI/blob/master/LICENSE :target: https://github.com/r4sas/PBinCLI/blob/master/LICENSE
:alt: GitHub license :alt: GitHub license
.. image:: https://img.shields.io/github/tag/r4sas/PBinCLI.svg .. image:: https://img.shields.io/github/tag/r4sas/PBinCLI.svg
:target: https://github.com/r4sas/PBinCLI/tags/ :target: https://github.com/r4sas/PBinCLI/tags/
:alt: GitHub tag :alt: GitHub tag
.. image:: https://app.codacy.com/project/badge/Grade/4f24f43356a84621bbd9078c4b3f1b70 .. image:: https://api.codacy.com/project/badge/Grade/4f24f43356a84621bbd9078c4b3f1b70
:target: https://www.codacy.com/gh/r4sas/PBinCLI/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=r4sas/PBinCLI&amp;utm_campaign=Badge_Grade :target: https://www.codacy.com/app/r4sas/PBinCLI?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=r4sas/PBinCLI&amp;utm_campaign=Badge_Grade
:alt: Codacy Badge :alt: Codacy Badge
PBinCLI PBinCLI
======= =======
PBinCLI is a command line client for `PrivateBin <https://github.com/PrivateBin/PrivateBin/>`_ written in Python 3. PBinCLI is command line client for `PrivateBin <https://github.com/PrivateBin/PrivateBin/>`_ written on Python 3.
Installation Installing
============ ----------
Installing globally using pip3: .. code-block:: bash
.. code-block:: bash virtualenv --python=python3 venv
. venv/bin/activate
pip3 install pbincli pip install pbincli
Installing with ``virtualenv``\ : Usage
-----
.. code-block:: bash
By default pbincli configured to use ``https://paste.i2pd.xyz/`` for sending and receiving pastes. No proxy used by default.
python3 -m virtualenv --python=python3 venv
. venv/bin/activate You can create config file with variables ``server`` and ``proxy`` in ``~/.config/pbincli/pbincli.conf`` to use different settings.
pip install pbincli
Example contents:
*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
.. code-block:: ini
Configuration
============= server=https://paste.i2pd.xyz/
proxy=http://127.0.0.1:3128
By default PBinCLI is configured to use ``https://paste.i2pd.xyz/`` for sending and receiving pastes. No proxy is used by default.
Run inside ``venv`` command:
You can always create a config file to use different settings.
.. code-block:: bash
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)
pbincli send --text "Hello!"
Example of config file content
------------------------------ Or use stdin input to read text for paste:
.. code-block:: ini .. code-block:: bash
server=https://paste.i2pd.xyz/ pbincli send - <<EOF
proxy=http://127.0.0.1:3128 Hello! This is test paste!
EOF
List of OPTIONS available
------------------------- It will send string ``Hello! This is test paste!`` to PrivateBin.
.. list-table:: To send file use ``--file`` or ``-f`` with filename. Example:
:header-rows: 1
.. code-block:: bash
* - Option
- Default pbincli send -c "My document" -f info.pdf
- Possible value
* - server To retrieve paste from server, use ``get`` command with paste info.
- https://paste.i2pd.xyz/
- Domain ending with slash It must be formated like ``pasteID#passphrase``. Example:
* - mirrors
- None .. code-block:: bash
- Domains separated with comma, like ``http://privatebin.ygg/,http://privatebin.i2p/``
* - proxy pbincli get 49eeb1326cfa9491#vfeortoVWaYeJlviDdhxQBtj5e0I2kArpynrtu/tnGs=
- None
- Proxy address starting with scheme ``http://`` or ``socks5://`` More info you can find by typing
* - expire
- 1day .. code-block:: bash
- 5min / 10min / 1hour / 1day / 1week / 1month / 1year / never
* - burn pbincli [-h] {send, get, delete}
- False
- True / False TODO
* - discus ----
- False
- True / False Write a more complete usage documentation.
* - format
- plaintext License
- plaintext / syntaxhighlighting / markdown -------
* - short
- False This project is licensed under the MIT license, which can be found in the file
- True / False `LICENSE <https://github.com/r4sas/PBinCLI/blob/master/LICENSE>`_ in the root of the project source code.
* - 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
pbincli {send|get|delete} -h
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
pbincli send - <<EOF
Hello! This is a test paste!
EOF
*
Sending a file with text attached into a paste:
.. code-block:: bash
pbincli send -f info.pdf -t "I'm sending my document."
*
Sending a file only with no text attached:
.. code-block:: bash
pbincli send -q -f info.pdf
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
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:
.. 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
=======
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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,67 +0,0 @@
# -*- 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)

View File

@ -2,6 +2,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__author__ = "R4SAS <r4sas@i2pmail.org>" __author__ = "R4SAS <r4sas@i2pmail.org>"
__version__ = "0.3.4" __version__ = "0.3.1"
__copyright__ = "Copyright (c) R4SAS" __copyright__ = "Copyright (c) R4SAS"
__license__ = "MIT" __license__ = "MIT"

View File

@ -1,47 +1,38 @@
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, check_writable, json_encode, uri_validator, validate_url_ending from pbincli.utils import PBinCLIError
import signal
def signal_handler(sig, frame): def signal_handler(sig, frame):
print('Keyboard interrupt received, terminating') print('Keyboard interrupt received, terminating...')
sys.exit(0) 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):
if settings['short']: from pbincli.api import Shortener
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(settings['compression']) paste.setCompression(args.compression)
# add text in paste (if it provided) # add text in paste (if it provided)
paste.setText(text) paste.setText(text)
@ -54,22 +45,21 @@ 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 = settings['format'], formatter = args.format,
burnafterreading = settings['burn'], burnafterreading = args.burn,
discussion = settings['discus'], discussion = args.discus,
expiration = settings['expire']) expiration = args.expire)
if args.verbose: print("Sending request to server…")
request = paste.getJSON() request = paste.getJSON()
if args.debug: print("Passphrase:\t{}\nRequest:\t{}".format(paste.getHash(), request)) if args.debug:
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: sys.exit(0) if args.dry: 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))
@ -78,41 +68,20 @@ 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()
# Paste information print("Paste uploaded!\nPasteID:\t{}\nPassword:\t{}\nDelete token:\t{}\n\nLink:\t\t{}?{}#{}".format(
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))
# Paste deletion link
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 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.")
if settings['short']: if args.short:
print("\nQuerying URL shortening service") print("\nQuerying URL shortening service...")
shortener.getlink("{}?{}#{}".format( shortener.getlink("{}?{}#{}".format(
settings['server'], settings['server'],
result['id'], result['id'],
@ -120,19 +89,16 @@ def send(args, api_client, settings=None):
def get(args, api_client, settings=None): def get(args, api_client, settings=None):
parseduri, isuri = uri_validator(args.pasteinfo) from pbincli.utils import check_writable, json_encode
if isuri and parseduri.query and parseduri.fragment: try:
api_client.server = args.pasteinfo.split("?")[0] pasteid, passphrase = args.pasteinfo.split("#")
pasteid = parseduri.query except ValueError:
passphrase = parseduri.fragment PBinCLIError("Provided info hasn't contain valid PasteID#Passphrase string")
elif parseduri.path and parseduri.path != "/" and parseduri.fragment:
pasteid = parseduri.path if not (pasteid and passphrase):
passphrase = parseduri.fragment PBinCLIError("Incorrect request")
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)
@ -141,14 +107,13 @@ 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! Decoding…") print("Paste received!")
version = result['v'] if 'v' in result else 1 version = result['v'] if 'v' in result else 1
paste.setVersion(version) paste.setVersion(version)
@ -185,32 +150,21 @@ 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):
parseduri, isuri = uri_validator(args.pasteinfo) from pbincli.utils import json_encode
if isuri: pasteid = args.paste
api_client.server = args.pasteinfo.split("?")[0] token = args.token
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}))

View File

@ -78,10 +78,6 @@ 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
@ -89,17 +85,12 @@ 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)
@ -146,8 +137,7 @@ 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)
@ -256,20 +246,3 @@ 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))

View File

@ -1,21 +1,12 @@
#!/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_ending from pbincli.utils import PBinCLIException, PBinCLIError, validate_url
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"""
@ -25,119 +16,103 @@ def read_config(filename):
if len(l.strip()) == 0: if len(l.strip()) == 0:
continue continue
try: try:
key, value = l.strip().split("=", 1) key, value = l.strip().split("=")
if value.strip().lower() in ['true', 'false']: settings[key.strip()] = value.strip()
settings[key.strip()] = bool(strtobool(value.strip()))
else:
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(description='Full-featured PrivateBin command-line client') parser = argparse.ArgumentParser()
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=argparse.SUPPRESS, action="store_true", help="Set \"Burn after reading\" flag") send_parser.add_argument("-B", "--burn", default=False, action="store_true", help="burn sent paste after reading")
send_parser.add_argument("-D", "--discus", default=argparse.SUPPRESS, action="store_true", help="Open discussion for sent paste") send_parser.add_argument("-D", "--discus", default=False, 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=argparse.SUPPRESS, action="store_true", help="Use URL shortener") send_parser.add_argument("-S", "--short", default=False, 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", "custom"], help="API used by shortener service") choices=["tinyurl", "clckru", "isgd", "vgd", "cuttly", "yourls"], 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="Instance URL (default: https://paste.i2pd.xyz/)") send_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="PrivateBin service 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=argparse.SUPPRESS, action="store_true", help="Disable certificate validation") send_parser.add_argument("--no-check-certificate", default=False, action="store_true", help="disable certificate validation")
send_parser.add_argument("--no-insecure-warning", default=argparse.SUPPRESS, action="store_true", send_parser.add_argument("--no-insecure-warning", default=False, action="store_true",
help="Suppress InsecureRequestWarning (only with --no-check-certificate)") help="suppress InsecureRequestWarning (only with --no-check-certificate)")
## ##
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("-d", "--debug", default=False, action="store_true", help="enable debug")
send_parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Enable verbose output") send_parser.add_argument("--dry", default=False, action="store_true", help="invoke dry run")
send_parser.add_argument("-d", "--debug", default=False, action="store_true", help="Enable debug output") send_parser.add_argument("stdin", help="input paste text from stdin", nargs="?", type=argparse.FileType("r"), default=sys.stdin)
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="\"PasteID#Passphrase\" or full URL") get_parser.add_argument("pasteinfo", help="example: aabb#cccddd")
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="Instance URL (default: https://paste.i2pd.xyz/, ignored if URL used in pasteinfo)") get_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="PrivateBin service URL (default: https://paste.i2pd.xyz/)")
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=argparse.SUPPRESS, action="store_true", help="Disable certificate validation") get_parser.add_argument("--no-check-certificate", default=False, action="store_true", help="disable certificate validation")
get_parser.add_argument("--no-insecure-warning", default=argparse.SUPPRESS, action="store_true", get_parser.add_argument("--no-insecure-warning", default=False, action="store_true",
help="Suppress InsecureRequestWarning (only with --no-check-certificate)") help="suppress InsecureRequestWarning (only with --no-check-certificate)")
## ##
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")
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") delete_parser = subparsers.add_parser("delete", description="Delete paste from PrivateBin instance using token")
delete_parser.add_argument("pasteinfo", help="Paste deletion URL or string in \"pasteid=PasteID&deletetoken=Token\" format") delete_parser.add_argument("-p", "--paste", required=True, help="paste id")
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="Instance URL (default: https://paste.i2pd.xyz/)") delete_parser.add_argument("-s", "--server", default=argparse.SUPPRESS, help="PrivateBin service 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=argparse.SUPPRESS, action="store_true", help="Disable certificate validation") delete_parser.add_argument("--no-check-certificate", default=False, action="store_true", help="disable certificate validation")
delete_parser.add_argument("--no-insecure-warning", default=argparse.SUPPRESS, action="store_true", delete_parser.add_argument("--no-insecure-warning", default=False, action="store_true",
help="Suppress InsecureRequestWarning (only with --no-check-certificate)") help="suppress InsecureRequestWarning (only with --no-check-certificate)")
## ##
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")
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. Defaults above # 4. Default values below
for p in CONFIG_PATHS: for p in CONFIG_PATHS:
if os.path.exists(p): if os.path.exists(p):
fileconfig = read_config(p) CONFIG.update(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():
@ -149,9 +124,8 @@ 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_ending(CONFIG['server']) CONFIG['server'] = validate_url(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"):

View File

@ -254,7 +254,6 @@ 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()
@ -262,13 +261,10 @@ 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 = [
[ [
@ -290,7 +286,6 @@ 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)))

View File

@ -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)
sys.exit(1) exit(1)
def path_leaf(path): def path_leaf(path):
@ -30,16 +30,7 @@ def json_encode(s):
return json.dumps(s, separators=(',',':')).encode() return json.dumps(s, separators=(',',':')).encode()
def validate_url_ending(s): def validate_url(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

View File

@ -1,2 +0,0 @@
[build-system]
requires = ["setuptools", "wheel"]