<commit_before>__author__ = "AstroPrint Product Team <<EMAIL>>"
__license__ = "GNU Affero General Public License http://www.gnu.org/licenses/agpl.html"

# singleton
_instance = None


def astroprintCloud():
    global _instance
    if _instance is None:
        _instance = AstroPrintCloud()
    return _instance


import requests
import hmac
import binascii
import uuid
import os
import json
import logging

from urllib import quote_plus
from os.path import splitext, split
from hashlib import sha256

from time import sleep

from requests_toolbelt import MultipartEncoder

from flask import current_app
from flask.ext.login import login_user, logout_user, current_user
from flask.ext.principal import Identity, identity_changed, AnonymousIdentity

from octoprint.settings import settings
from octoprint.events import eventManager, Events

from astroprint.software import softwareManager
from astroprint.boxrouter import boxrouterManager
from astroprint.printer.manager import printerManager
from astroprint.printfiles.downloadmanager import downloadManager


class AstroPrintCloudException(Exception):
    pass


class AstroPrintCloudNoConnectionException(AstroPrintCloudException):
    # There no connection to the astroprint cloud
    pass


class HMACAuth(requests.auth.AuthBase):
    def __init__(self, publicKey, privateKey):
        self.publicKey = publicKey
        self.privateKey = privateKey

    def __call__(self, r):
        r.headers['User-Agent'] = softwareManager().userAgent
        sig_base = '&'.join((r.method, r.headers['User-Agent']))
        hashed = hmac.new(self.privateKey, sig_base, sha256)

        r.headers['X-Public'] = self.publicKey
        r.headers['X-Hash'] = binascii.b2a_base64(hashed.digest())[:-1]

        return r


class AstroPrintCloud(object):
    def __init__(self):
        self.settings = settings()
        self.hmacAuth = None

        self.tryingLoggingTimes = 0

        loggedUser = self.settings.get(['cloudSlicer', 'loggedUser'])
        if loggedUser:
            from octoprint.server import userManager

            user = userManager.findUser(loggedUser)

            if user and user.publicKey and user.privateKey:
                self.hmacAuth = HMACAuth(user.publicKey, user.privateKey)

        self.apiHost = self.settings.get(['cloudSlicer', 'apiHost'])
        self._print_file_store = None
        self._sm = softwareManager()
        self._logger = logging.getLogger(__name__)

    def cloud_enabled(self):
        return settings().get(['cloudSlicer', 'apiHost']) and self.hmacAuth

    def signin(self, email, password):
        from octoprint.server import userManager
        from astroprint.network.manager import networkManager

        user = None
        userLoggedIn = False

        online = networkManager().isOnline()

        if online:
            private_key = self.get_private_key(email, password)

            if private_key:
                public_key = self.get_public_key(email, private_key)

                if public_key:
                    # Let's protect the box now:
                    user = userManager.findUser(email)

                    if user:
                        userManager.changeUserPassword(email, password)
                        userManager.changeCloudAccessKeys(
                            email, public_key, private_key)
                    else:
                        user = userManager.addUser(
                            email, password, public_key, private_key, True)

                    userLoggedIn = True

        else:
            user = userManager.findUser(email)
            userLoggedIn = user and \
                user.check_password(userManager.createPasswordHash(password))

        if userLoggedIn:
            login_user(user, remember=True)
            userId = user.get_id()

            self.settings.set(["cloudSlicer", "loggedUser"], userId)
            self.settings.save()

            boxrouterManager().boxrouter_connect()

            identity_changed.send(
                current_app._get_current_object(), identity=Identity(userId))

            # let the singleton be recreated again, so new
            # credentials are taken into use
            global _instance
            _instance = None

            eventManager().fire(Events.LOCK_STATUS_CHANGED, userId)

            return True

        elif not online:
            raise AstroPrintCloudNoConnectionException()

        return False

    def remove_logged_user(self):
        self.settings.set(["cloudSlicer", "loggedUser"], None)
        self.settings.save()
        boxrouterManager().boxrouter_disconnect()

        # let the singleton be recreated again, so credentials and
        # print_files are forgotten
        global _instance
        _instance = None

        eventManager().fire(Events.LOCK_STATUS_CHANGED, None)

    def signout(self):
        from flask import session

        logout_user()

        for key in ('identity.name', 'identity.auth_type'):
            session.pop(key, None)

        identity_changed.send(
            current_app._get_current_object(), identity=AnonymousIdentity())
        self.remove_logged_user()

    def get_upload_info(self, filePath):
        path, filename = split(filePath)
        path, fileExtension = splitext(filename)
        design_id = uuid.uuid4().hex
        s3_key = design_id + fileExtension

        # only registered users can upload files to the cloud
        if current_user and not current_user.is_anonymous:
            try:
                # Get credentials to upload the file
                r = requests.get(
                    "%s/files/upload_credentials?key=%s" % \
                    (self.apiHost, s3_key), auth=self.hmacAuth)
                data = r.json()
            except:
                data = None

            if data:
                publicKey = current_user.publicKey
                privateKey = current_user.privateKey

                request = json.dumps({
                    'design_id': design_id,
                    's3_key': s3_key,
                    'filename': filename
                })

                hashed = hmac.new(privateKey, request, sha256)
                signature = binascii.b2a_base64(hashed.digest())[:-1]

                redirect_url = \
                    "%s/files/file_redirect?remote_key=%s" % (
                        self.apiHost.replace('api', 'www'),
                        s3_key
                    )

                # url, post parameters, redirect Url
                return {
                    'url': data['url'],
                    'params': data['post_data'],
                    'redirect': redirect_url
                }

            else:
                return {
                    'error': 'invalid_data',
                }

        else:
            return {
                'error': '<PASSWORD>',
            }

    def get_private_key(self, email, password):

        r = requests.post(
            "%s/%s" % (self.apiHost, 'api/privateKey/'),
            data={
                "email": email,
                "password": password
            },
            headers={
                'User-Agent': self._sm.userAgent,
                'X-API-Key': self.settings.get(['api', 'key'])
            }
        )

        try:
            data = r.json()
        except:
            data = None

        if data and "private_key" in data:
            return data["private_key"]
        else:
            return None

    def get_public_key(self, email, private_key):
        r = requests.post(
            "%s/%s" % (self.apiHost, 'api/publicKey/'),
            data={
                "email": email,
                "private_key": private_key
            },
            headers={'User-Agent': self._sm.userAgent}
        )

        try:
            data = r.json()
        except:
            data = None

        if data and "public_key" in data:
            return data["public_key"]
        else:
            return None

    def get_login_key(self):
        r = requests.get(
            "%s/%s" % (self.apiHost, 'api/loginKey'),
            headers={'User-Agent': self._sm.userAgent},
            auth=self.hmacAuth
        )
<commit_msg>api: correct endpoints, public_key, private_key and login_key<commit_after>__author__ = "AstroPrint Product Team <<EMAIL>>"
__license__ = "GNU Affero General Public License http://www.gnu.org/licenses/agpl.html"

# singleton
_instance = None


def astroprintCloud():
    global _instance
    if _instance is None:
        _instance = AstroPrintCloud()
    return _instance


import requests
import hmac
import binascii
import uuid
import os
import json
import logging

from urllib import quote_plus
from os.path import splitext, split
from hashlib import sha256

from time import sleep

from requests_toolbelt import MultipartEncoder

from flask import current_app
from flask.ext.login import login_user, logout_user, current_user
from flask.ext.principal import Identity, identity_changed, AnonymousIdentity

from octoprint.settings import settings
from octoprint.events import eventManager, Events

from astroprint.software import softwareManager
from astroprint.boxrouter import boxrouterManager
from astroprint.printer.manager import printerManager
from astroprint.printfiles.downloadmanager import downloadManager


class AstroPrintCloudException(Exception):
    pass


class AstroPrintCloudNoConnectionException(AstroPrintCloudException):
    # There no connection to the astroprint cloud
    pass


class HMACAuth(requests.auth.AuthBase):
    def __init__(self, publicKey, privateKey):
        self.publicKey = publicKey
        self.privateKey = privateKey

    def __call__(self, r):
        r.headers['User-Agent'] = softwareManager().userAgent
        sig_base = '&'.join((r.method, r.headers['User-Agent']))
        hashed = hmac.new(self.privateKey, sig_base, sha256)

        r.headers['X-Public'] = self.publicKey
        r.headers['X-Hash'] = binascii.b2a_base64(hashed.digest())[:-1]

        return r


class AstroPrintCloud(object):
    def __init__(self):
        self.settings = settings()
        self.hmacAuth = None

        self.tryingLoggingTimes = 0

        loggedUser = self.settings.get(['cloudSlicer', 'loggedUser'])
        if loggedUser:
            from octoprint.server import userManager

            user = userManager.findUser(loggedUser)

            if user and user.publicKey and user.privateKey:
                self.hmacAuth = HMACAuth(user.publicKey, user.privateKey)

        self.apiHost = self.settings.get(['cloudSlicer', 'apiHost'])
        self._print_file_store = None
        self._sm = softwareManager()
        self._logger = logging.getLogger(__name__)

    def cloud_enabled(self):
        return settings().get(['cloudSlicer', 'apiHost']) and self.hmacAuth

    def signin(self, email, password):
        from octoprint.server import userManager
        from astroprint.network.manager import networkManager

        user = None
        userLoggedIn = False

        online = networkManager().isOnline()

        if online:
            private_key = self.get_private_key(email, password)

            if private_key:
                public_key = self.get_public_key(email, private_key)

                if public_key:
                    # Let's protect the box now:
                    user = userManager.findUser(email)

                    if user:
                        userManager.changeUserPassword(email, password)
                        userManager.changeCloudAccessKeys(
                            email, public_key, private_key)
                    else:
                        user = userManager.addUser(
                            email, password, public_key, private_key, True)

                    userLoggedIn = True

        else:
            user = userManager.findUser(email)
            userLoggedIn = user and \
                user.check_password(userManager.createPasswordHash(password))

        if userLoggedIn:
            login_user(user, remember=True)
            userId = user.get_id()

            self.settings.set(["cloudSlicer", "loggedUser"], userId)
            self.settings.save()

            boxrouterManager().boxrouter_connect()

            identity_changed.send(
                current_app._get_current_object(), identity=Identity(userId))

            # let the singleton be recreated again, so new
            # credentials are taken into use
            global _instance
            _instance = None

            eventManager().fire(Events.LOCK_STATUS_CHANGED, userId)

            return True

        elif not online:
            raise AstroPrintCloudNoConnectionException()

        return False

    def remove_logged_user(self):
        self.settings.set(["cloudSlicer", "loggedUser"], None)
        self.settings.save()
        boxrouterManager().boxrouter_disconnect()

        # let the singleton be recreated again, so credentials and
        # print_files are forgotten
        global _instance
        _instance = None

        eventManager().fire(Events.LOCK_STATUS_CHANGED, None)

    def signout(self):
        from flask import session

        logout_user()

        for key in ('identity.name', 'identity.auth_type'):
            session.pop(key, None)

        identity_changed.send(
            current_app._get_current_object(), identity=AnonymousIdentity())
        self.remove_logged_user()

    def get_upload_info(self, filePath):
        path, filename = split(filePath)
        path, fileExtension = splitext(filename)
        design_id = uuid.uuid4().hex
        s3_key = design_id + fileExtension

        # only registered users can upload files to the cloud
        if current_user and not current_user.is_anonymous:
            try:
                # Get credentials to upload the file
                r = requests.get(
                    "%s/files/upload_credentials?key=%s" % \
                    (self.apiHost, s3_key), auth=self.hmacAuth)
                data = r.json()
            except:
                data = None

            if data:
                publicKey = current_user.publicKey
                privateKey = current_user.privateKey

                request = json.dumps({
                    'design_id': design_id,
                    's3_key': s3_key,
                    'filename': filename
                })

                hashed = hmac.new(privateKey, request, sha256)
                signature = binascii.b2a_base64(hashed.digest())[:-1]

                redirect_url = \
                    "%s/files/file_redirect?remote_key=%s" % (
                        self.apiHost.replace('api', 'www'),
                        s3_key
                    )

                # url, post parameters, redirect Url
                return {
                    'url': data['url'],
                    'params': data['post_data'],
                    'redirect': redirect_url
                }

            else:
                return {
                    'error': 'invalid_data',
                }

        else:
            return {
                'error': 'no_user',
            }

    def get_private_key(self, email, password):

        r = requests.post(
            "%s/%s" % (self.apiHost, 'api/private_key/'),
            data={
                "email": email,
                "password": password
            },
            headers={
                'User-Agent': self._sm.userAgent,
                'X-API-Key': self.settings.get(['api', 'key'])
            }
        )

        try:
            data = r.json()
        except:
            data = None

        if data and "private_key" in data:
            return data["private_key"]
        else:
            return None

    def get_public_key(self, email, private_key):
        r = requests.post(
            "%s/%s" % (self.apiHost, 'api/public_key/'),
            data={
                "email": email,
                "private_key": private_key
            },
            headers={'User-Agent': self._sm.userAgent}
        )

        try:
            data = r.json()
        except:
            data = None

        if data and "public_key" in data:
            return data["public_key"]
        else:
            return None

    def get_login_key(self):
        r = requests.get(
            "%s/%s" % (self.apiHost, 'api/login_key'),
            headers={'User-Agent': self._sm.userAgent},
            auth=self.hmacAuth
        )

{'fraction_non_alphanumeric': 0.08869596891694997, 'fraction_numerical': 0.0029140359397765905, 'mean_word_length': 1.8279828326180259, 'pattern_counts': {'":': 8, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6504240', 'n_tokens_mistral': 4533, 'n_tokens_neox': 4288, 'n_words': 1022}