Example #1
0
    def _yourls_init(self, settings):
        if not settings['short_url']:
            PBinCLIError("YOURLS: An API URL is required")

        # setting API URL
        apiurl = settings['short_url']
        if apiurl.endswith('/yourls-api.php'):
            self.apiurl = apiurl
        elif apiurl.endswith('/'):
            self.apiurl = apiurl + 'yourls-api.php'
        else:
            PBinCLIError(
                "YOURLS: Incorrect URL is provided.\n" +
                "It must contain full address to 'yourls-api.php' script (like https://example.com/yourls-api.php)\n"
                +
                "or just contain instance URL with '/' at the end (like https://example.com/)"
            )

        # validating for required credentials
        if settings['short_user'] and settings[
                'short_pass'] and settings['short_token'] is None:
            self.auth_args = {
                'username': settings['short_user'],
                'password': settings['short_pass']
            }
        elif settings['short_user'] is None and settings[
                'short_pass'] is None and settings['short_token']:
            self.auth_args = {'signature': settings['short_token']}
        elif settings['short_user'] is None and settings[
                'short_pass'] is None and settings['short_token'] is None:
            self.auth_args = {}
        else:
            PBinCLIError(
                "YOURLS: either username and password or token are required. Otherwise set to default (None)"
            )
Example #2
0
    def _gd(self, url):
        request = {
            'format': 'json',
            'url': url,
            'logstats': 0  # we don't want use any statistics
        }
        headers = {'User-Agent': self.useragent}

        try:
            result = self.session.post(url=self.apiurl + "create.php",
                                       headers=headers,
                                       proxies=self.proxy,
                                       data=request)

            response = result.json()

            if 'shorturl' in response:
                print("Short Link:\t{}".format(response['shorturl']))
            else:
                PBinCLIError("{}: got error {} from API: {}".format(
                    "is.gd" if self.api == 'isgd' else 'v.gd',
                    response['errorcode'], response['errormessage']))

        except Exception as ex:
            PBinCLIError("{}: unexcepted behavior: {}".format(
                "is.gd" if self.api == 'isgd' else 'v.gd', ex))
Example #3
0
    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))
Example #4
0
    def _clckru(self, url):
        request = {'url': url}

        try:
            result = self.session.post(url="https://clck.ru/--",
                                       proxies=self.proxy,
                                       data=request)
            print("Short Link:\t{}".format(result.text))
        except Exception as ex:
            PBinCLIError("clck.ru: unexcepted behavior: {}".format(ex))
Example #5
0
    def _cuttly(self, url):
        request = {'url': url, 'domain': 0}

        try:
            result = self.session.post(
                url="https://cutt.ly/scripts/shortenUrl.php",
                proxies=self.proxy,
                data=request)
            print("Short Link:\t{}".format(result.text))
        except Exception as ex:
            PBinCLIError("cutt.ly: unexcepted behavior: {}".format(ex))
Example #6
0
    def _tinyurl(self, url):
        request = {'url': url}

        try:
            result = self.session.post(
                url="https://tinyurl.com/api-create.php",
                proxies=self.proxy,
                data=request)
            print("Short Link:\t{}".format(result.text))
        except Exception as ex:
            PBinCLIError("TinyURL: unexcepted behavior: {}".format(ex))
Example #7
0
    def post(self, request):
        result = self.session.post(url=self.server,
                                   headers=self.headers,
                                   proxies=self.proxy,
                                   data=request)

        try:
            return result.json()
        except ValueError:
            PBinCLIError(
                "Unable parse response as json. Received (size = {}):\n{}".
                format(len(result.text), result.text))
Example #8
0
    def delete(self, request):
        # using try as workaround for versions < 1.3 due to we cant detect
        # if server used version 1.2, where auto-deletion is added
        try:
            result = self.session.post(url=self.server,
                                       headers=self.headers,
                                       proxies=self.proxy,
                                       data=request).json()
        except ValueError:
            # unable parse response as json because it can be empty (1.2), so simulate correct answer
            print(
                "NOTICE: Received empty response. We interpret that as our paste has already been deleted."
            )
            from json import loads as json_loads
            result = json_loads('{"status":0}')

        if not result['status']:
            print("Paste successfully deleted!")
        elif result['status']:
            PBinCLIError("Something went wrong...\nError:\t\t{}".format(
                result['message']))
        else:
            PBinCLIError("Something went wrong...\nError: Empty response.")
Example #9
0
    def _yourls(self, url):
        request = {'action': 'shorturl', 'format': 'json', 'url': url}
        request.update(self.auth_args)

        result = self.session.post(url=self.apiurl,
                                   proxies=self.proxy,
                                   data=request)

        try:
            result.raise_for_status()
        except HTTPError:
            try:
                response = result.json()
            except ValueError:
                PBinCLIError(
                    "YOURLS: Unable parse response. Received (size = {}):\n{}".
                    format(len(result.text), result.text))
            else:
                PBinCLIError(
                    "YOURLS: Received error from API: {} with JSON {}".format(
                        result, response))
        else:
            response = result.json()

            if {'status', 'statusCode', 'message'} <= set(response.keys()):
                if response['status'] == 'fail':
                    PBinCLIError("YOURLS: Received error from API: {}".format(
                        response['message']))
                if not 'shorturl' in response:
                    PBinCLIError("YOURLS: Unknown error: {}".format(
                        response['message']))
                else:
                    print("Short Link:\t{}".format(response['shorturl']))
            else:
                PBinCLIError(
                    "YOURLS: No status, statusCode or message fields in response! Received:\n{}"
                    .format(response))
Example #10
0
File: cli.py Project: r4sas/PBinCLI
def read_config(filename):
    """Read config variables from a file"""
    settings = {}
    with open(filename) as f:
        for l in f.readlines():
            if len(l.strip()) == 0:
                continue
            try:
                key, value = l.strip().split("=", 1)
                settings[key.strip()] = value.strip()
            except ValueError:
                PBinCLIError(
                    "Unable to parse config file, please check it for errors.")

    return settings
Example #11
0
 def __compress(self, s):
     if self._version == 2 and self._compression == 'zlib':
         # using compressobj as compress doesn't let us specify wbits
         # needed to get the raw stream without headers
         co = zlib.compressobj(wbits=-zlib.MAX_WBITS)
         return co.compress(s) + co.flush()
     elif self._version == 2 and self._compression == 'none':
         # nothing to do, just return original data
         return s
     elif self._version == 1:
         co = zlib.compressobj(wbits=-zlib.MAX_WBITS)
         b = co.compress(s) + co.flush()
         return b64encode(''.join(map(chr, b)).encode('utf-8'))
     else:
         PBinCLIError('Unknown compression type provided!')
Example #12
0
 def __decompress(self, s):
     if self._version == 2 and self._compression == 'zlib':
         # decompress data
         return zlib.decompress(s, -zlib.MAX_WBITS)
     elif self._version == 2 and self._compression == 'none':
         # nothing to do, just return original data
         return s
     elif self._version == 1:
         return zlib.decompress(
             bytearray(
                 map(lambda c: ord(c) & 255,
                     b64decode(s.encode('utf-8')).decode('utf-8'))),
             -zlib.MAX_WBITS)
     else:
         PBinCLIError('Unknown compression type provided in paste!')
Example #13
0
    def __init__(self, settings=None):
        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
        # any authentication, or have only one domain on which it working
        if self.api == 'yourls':
            self._yourls_init(settings)
        elif self.api == 'isgd' or self.api == 'vgd':
            self._gd_init()
        elif self.api == 'custom':
            self.apiurl = settings['short_url']

        self.session, self.proxy = _config_requests(settings)
Example #14
0
def send(args, api_client, settings=None):
    from pbincli.api import Shortener
    if args.short:
        shortener = Shortener(settings)

    if not args.notext:
        if args.text:
            text = args.text
        elif args.stdin:
            print("Reading text from stdin…")
            text = args.stdin.read()
    elif not args.file:
        PBinCLIError("Nothing to send!")
    else:
        text = ""

    print("Preparing paste…")
    paste = Paste(args.debug)

    if args.verbose: print("Used server: {}".format(api_client.getServer()))

    # 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()
    paste.setVersion(version)

    if args.verbose: print("Filling paste with data…")
    # set compression type, works only on v2 pastes
    if version == 2:
        paste.setCompression(args.compression)

    # add text in paste (if it provided)
    paste.setText(text)

    # If we set PASSWORD variable
    if args.password:
        paste.setPassword(args.password)

    # If we set FILE variable
    if args.file:
        paste.setAttachment(args.file)

    if args.verbose: print("Encrypting paste…")
    paste.encrypt(formatter=args.format,
                  burnafterreading=args.burn,
                  discussion=args.discus,
                  expiration=args.expire)

    if args.verbose: print("Sending request to server…")
    request = paste.getJSON()

    if args.debug:
        print("Passphrase:\t{}\nRequest:\t{}".format(paste.getHash(), request))

    # If we use dry option, exit now
    if args.dry: exit(0)

    print("Uploading paste…")
    result = api_client.post(request)

    if args.debug: print("Response:\t{}\n".format(result))

    # Paste was sent. Checking for returned status code
    if not result['status']:  # return code is zero
        passphrase = paste.getHash()

        # Paste information
        print(
            "Paste uploaded!\nPasteID:\t{}\nPassword:\t{}\nDelete token:\t{}".
            format(result['id'], passphrase, result['deletetoken']))

        # Paste link
        print("\nLink:\t\t{}?{}#{}".format(settings['server'], result['id'],
                                           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
        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:
        print("\nQuerying URL shortening service…")
        shortener.getlink("{}?{}#{}".format(settings['server'], result['id'],
                                            passphrase))
Example #15
0
from base64 import b64encode, b64decode
from pbincli.utils import PBinCLIError
import zlib

# try import AES cipher and check if it has GCM mode (prevent usage of pycrypto)
try:
    from Crypto.Cipher import AES
    if not hasattr(AES, 'MODE_GCM'):
        try:
            from Cryptodome.Cipher import AES
            from Cryptodome.Random import get_random_bytes
        except ImportError:
            PBinCLIError(
                "AES GCM mode is not found in imported crypto module.\n" +
                "That can happen if you have installed pycrypto.\n\n" +
                "We tried to import pycryptodomex but it is not available.\n" +
                "Please install it via pip, if you still need pycrypto, by running:\n"
                + "\tpip install pycryptodomex\n" +
                "... otherwise use separate python environment or uninstall pycrypto:\n"
                + "\tpip uninstall pycrypto")
    else:
        from Crypto.Random import get_random_bytes
except ImportError:
    PBinCLIError("Unable import pycryptodome")

CIPHER_ITERATION_COUNT = 100000
CIPHER_SALT_BYTES = 8
CIPHER_BLOCK_BITS = 256
CIPHER_TAG_BITS = 128


class Paste:
def get(args, api_client, settings=None):
    from pbincli.utils import check_writable, json_encode

    try:
        pasteid, passphrase = args.pasteinfo.split("#")
    except ValueError:
        PBinCLIError(
            "Provided info hasn't contain valid PasteID#Passphrase string")

    if not (pasteid and passphrase):
        PBinCLIError("Incorrect request")

    if args.debug:
        print("PasteID:\t{}\nPassphrase:\t{}".format(pasteid, passphrase))

    paste = Paste(args.debug)

    if args.password:
        paste.setPassword(args.password)
        if args.debug: print("Password:\t{}".format(args.password))

    result = api_client.get(pasteid)

    if args.debug: print("Response:\t{}\n".format(result))

    # Paste was received. Checking received status code
    if not result['status']:  # return code is zero
        print("Paste received!")

        version = result['v'] if 'v' in result else 1
        paste.setVersion(version)

        if version == 2:
            if args.debug:
                print("Authentication data:\t{}".format(result['adata']))

        paste.setHash(passphrase)
        paste.loadJSON(result)
        paste.decrypt()

        text = paste.getText()

        if args.debug: print("Decoded text size: {}\n".format(len(text)))

        if len(text):
            if args.debug: print("{}\n".format(text.decode()))
            filename = "paste-" + pasteid + ".txt"
            print("Found text in paste. Saving it to {}".format(filename))

            check_writable(filename)
            with open(filename, "wb") as f:
                f.write(text)
                f.close()

        attachment, attachment_name = paste.getAttachment()

        if attachment:
            print("Found file, attached to paste. Saving it to {}\n".format(
                attachment_name))

            check_writable(attachment_name)
            with open(attachment_name, "wb") as f:
                f.write(attachment)
                f.close()

        if version == 1 and 'meta' in result and 'burnafterreading' in result[
                'meta'] and result['meta']['burnafterreading']:
            print("Burn afrer reading flag found. Deleting paste...")
            api_client.delete(
                json_encode({
                    'pasteid': pasteid,
                    'deletetoken': 'burnafterreading'
                }))

    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.")
def send(args, api_client, settings=None):
    from pbincli.api import Shortener
    if args.short:
        shortener = Shortener(settings)

    if not args.notext:
        if args.text:
            text = args.text
        elif args.stdin:
            text = args.stdin.read()
    elif not args.file:
        PBinCLIError("Nothing to send!")
    else:
        text = ""

    paste = Paste(args.debug)

    # get from server supported paste format version and update object
    version = api_client.getVersion()
    paste.setVersion(version)

    # set compression type, works only on v2 pastes
    if version == 2:
        paste.setCompression(args.compression)

    # add text in paste (if it provided)
    paste.setText(text)

    # If we set PASSWORD variable
    if args.password:
        paste.setPassword(args.password)

    # If we set FILE variable
    if args.file:
        paste.setAttachment(args.file)

    paste.encrypt(formatter=args.format,
                  burnafterreading=args.burn,
                  discussion=args.discus,
                  expiration=args.expire)

    request = paste.getJSON()

    if args.debug:
        print("Passphrase:\t{}".format(paste.getHash()))
        print("Request:\t{}".format(request))

    # If we use dry option, exit now
    if args.dry: exit(0)

    result = api_client.post(request)

    if args.debug: print("Response:\t{}\n".format(result))

    # Paste was sent. Checking for returned status code
    if not result['status']:  # return code is zero
        passphrase = paste.getHash()

        print(
            "Paste uploaded!\nPasteID:\t{}\nPassword:\t{}\nDelete token:\t{}\n\nLink:\t\t{}?{}#{}"
            .format(result['id'], passphrase, result['deletetoken'],
                    settings['server'], 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 args.short:
        print("\nQuerying URL shortening service...")
        shortener.getlink("{}?{}#{}".format(settings['server'], result['id'],
                                            passphrase))