<commit_before># coding=utf-8
__author__ = "AstroPrint Product Team <<EMAIL>> based on work by <NAME>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2016 3DaGoGo, Inc - Released under terms of the AGPLv3 License"

from flask.ext.principal import identity_changed, Identity
from tornado.web import StaticFileHandler, HTTPError, RequestHandler, asynchronous
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from flask import url_for, make_response, request, current_app
from flask.ext.login import login_required, login_user, current_user
from werkzeug.utils import redirect
from ext.sockjs.tornado import SockJSConnection
from itsdangerous import base64_decode

import datetime
import stat
import mimetypes
import email
import time
import os
import threading
import logging
import zlib
import json
from functools import wraps

import octoprint.server
import octoprint.util as util

from octoprint.settings import settings
from octoprint.events import Events

from astroprint.boxrouter import boxrouterManager
from astroprint.printfiles import FileDestinations
from astroprint.printfiles.map import SUPPORTED_EXTENSIONS
from astroprint.printer.manager import printerManager
from astroprint.camera import cameraManager
from astroprint.users import ApiUser
from astroprint.printfiles.watchdogs import EtherBoxHandler


def restricted_access(func, apiEnabled=True):
    """
    If you decorate a view with this, it will ensure that first setup has been
    done for AstroBox's Access Control plus that any conditions of the
    login_required decorator are met. It also allows to login using the masterkey or any
    of the user's apikeys if API access is enabled globally and for the decorated view.

    If AstroBox's Access Control has not been setup yet (indicated by the "firstRun"
    flag from the settings being set to True and the userManager not indicating
    that it's user database has been customized from default), the decorator
    will cause a HTTP 403 status code to be returned by the decorated resource.

    If an API key is provided and it matches a known key, the user will be logged in and
    the view will be called directly. If the provided key doesn't match any known key,
    a HTTP 403 status code will be returned by the decorated resource.

    Otherwise the result of calling login_required will be returned.
    """
    @wraps(func)
    def decorated_view(*args, **kwargs):
        # if AstroBox hasn't been set up yet, abort
        if settings().getBoolean(["server", "firstRun"]) and (octoprint.server.userManager is None or not octoprint.server.userManager.hasBeenCustomized()):
            return make_response("AstroBox isn't setup yet", 403)

        # if API is globally enabled, enabled for this request and an api key is provided that is not the current UI API key, try to use that
        apikey = getApiKey(request)

        if settings().get(["api", "enabled"]) and apiEnabled and apikey is not None:
            if apikey != octoprint.server.UI_API_KEY:
                if apikey == settings().get(["api", "key"]):
                    # master key was used
                    user = ApiUser()
                else:
                    # user key might have been used
                    user = octoprint.server.userManager.findUser(apikey=apikey)

                if user is None:
                    return make_response("Invalid API key", 401)

                if login_user(user, remember=False):
                    identity_changed.send(current_app._get_current_object(), identity=Identity(user.get_id()))
                    return func(*args, **kwargs)

            else:
                return func(*args, **kwargs)

        return make_response("Invalid Api Key or API Disabled", 401)

    return decorated_view


def api_access(func):
    @wraps(func)
    def decorated_view(*args, **kwargs):
        if not settings().get(["api", "enabled"]):
            make_response("API disabled", 401)
        apikey = getApiKey(request)
        if apikey is None:
            make_response("No API key provided", 401)
        if apikey != settings().get(["api", "key"]):
            make_response("Invalid API key", 403)
        return func(*args, **kwargs)
    return decorated_view


def getUserForApiKey(apikey):
    if settings().get(["api", "enabled"]) and apikey is not None:
        if apikey == settings().get(["api", "key"]):
            # master key was used
            return ApiUser()
        else:
            # user key might have been used
            return octoprint.server.userManager.findUser(apikey=apikey)
    else:
        return None


def getApiKey(request):
    # Check Flask GET/POST arguments
    if hasattr(request, "values") and "apikey" in request.values:
        return request.values["apikey"]

    # Check Tornado GET/POST arguments
    if hasattr(request, "arguments") and "apikey" in request.arguments and len(request.arguments["apikey"].strip()) > 0:
        return request.arguments["apikey"]

    # Check Tornado and Flask headers
    if "X-Api-Key" in request.headers.keys():
        return request.headers.get("X-Api-Key")

    return None


#~~ Printer state


class PrinterStateConnection(SockJSConnection):
	EVENTS = [Events.UPDATED_FILES, Events.METADATA_ANALYSIS_FINISHED, Events.SLICING_STARTED, Events.SLICING_DONE, Events.SLICING_FAILED,
				Events.TRANSFER_STARTED, Events.TRANSFER_DONE, Events.CLOUD_DOWNLOAD, Events.ASTROPRINT_STATUS, Events.SOFTWARE_UPDATE,
				Events.CAPTURE_INFO_CHANGED, Events.LOCK_STATUS_CHANGED, Events.NETWORK_STATUS, Events.INTERNET_CONNECTING_STATUS,
				Events.GSTREAMER_EVENT, Events.TOOL_CHANGE]

	def __init__(self, userManager, eventManager, session):
		SockJSConnection.__init__(self, session)

		self._logger = logging.getLogger(__name__)

		self._temperatureBacklog = []
		self._temperatureBacklogMutex = threading.Lock()
		self._logBacklog = []
		self._logBacklogMutex = threading.Lock()
		self._messageBacklog = []
		self._messageBacklogMutex = threading.Lock()
		self._emitLock = threading.Lock()

		self._userManager = userManager
		self._eventManager = eventManager

	def _getRemoteAddress(self, request):
		forwardedFor = request.headers.get("X-Forwarded-For")
		if forwardedFor is not None:
			return forwardedFor.split(",")[0]
		return request.ip

	def on_open(self, request):
		s = settings()
		loggedUsername = s.get(["cloudSlicer", "loggedUser"])

		if loggedUsername:
			token = request.arguments.get("token")
			token = token[0] if token else None
			tokenContents = octoprint.server.read_ws_token(token)
			if not tokenContents or tokenContents['public_key'] != self._userManager.findUser(loggedUsername).publicKey:
				return False

		remoteAddress = self._getRemoteAddress(request)
		self._logger.info("New connection from client [IP address: %s, Session id: %s]", remoteAddress, self.session.session_id)

		# connected => update the API key, might be necessary if the client was left open while the server restarted
		self._emit("connected", {"apikey": octoprint.server.UI_API_KEY, "version": octoprint.server.VERSION, "sessionId": self.session.session_id})
		self.sendEvent(Events.ASTROPRINT_STATUS, boxrouterManager().status)

		printer = printerManager()

		printer.registerCallback(self)
		printer.fileManager.registerCallback(self)
        #EtherBoxHandler.registerCallback(self)

        self._eventManager.fire(Events.CLIENT_OPENED, {"remoteAddress": remoteAddress})
        for event in PrinterStateConnection.EVENTS:
            self._eventManager.subscribe(event, self._onEvent)

    def on_close(self):
        self._logger.info("Client connection closed [Session id: %s]", self.session.session_id)

        printer = printerManager()

        printer.unregisterCallback(self)
        printer.fileManager.unregisterCallback(self)
        cameraManager().closeLocalVideoSession(self.session.session_id)

        self._eventManager.fire(Events.CLIENT_CLOSED)
        for event in PrinterStateConnection.EVENTS:<commit_msg>Abl api (#76)<commit_after># coding=utf-8
__author__ = "AstroPrint Product Team <<EMAIL>> based on work by <NAME>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2016 3DaGoGo, Inc - Released under terms of the AGPLv3 License"

from flask.ext.principal import identity_changed, Identity
from tornado.web import StaticFileHandler, HTTPError, RequestHandler, asynchronous
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from flask import url_for, make_response, request, current_app
from flask.ext.login import login_required, login_user, current_user
from werkzeug.utils import redirect
from ext.sockjs.tornado import SockJSConnection
from itsdangerous import base64_decode

import datetime
import stat
import mimetypes
import email
import time
import os
import threading
import logging
import zlib
import json
from functools import wraps

import octoprint.server
import octoprint.util as util

from octoprint.settings import settings
from octoprint.events import Events

from astroprint.boxrouter import boxrouterManager
from astroprint.printfiles import FileDestinations
from astroprint.printfiles.map import SUPPORTED_EXTENSIONS
from astroprint.printer.manager import printerManager
from astroprint.camera import cameraManager
from astroprint.users import ApiUser
from astroprint.printfiles.watchdogs import EtherBoxHandler


def restricted_access(func, apiEnabled=True):
    """
    If you decorate a view with this, it will ensure that first setup has been
    done for AstroBox's Access Control plus that any conditions of the
    login_required decorator are met. It also allows to login using the masterkey or any
    of the user's apikeys if API access is enabled globally and for the decorated view.

    If AstroBox's Access Control has not been setup yet (indicated by the "firstRun"
    flag from the settings being set to True and the userManager not indicating
    that it's user database has been customized from default), the decorator
    will cause a HTTP 403 status code to be returned by the decorated resource.

    If an API key is provided and it matches a known key, the user will be logged in and
    the view will be called directly. If the provided key doesn't match any known key,
    a HTTP 403 status code will be returned by the decorated resource.

    Otherwise the result of calling login_required will be returned.
    """
    @wraps(func)
    def decorated_view(*args, **kwargs):
        # if AstroBox hasn't been set up yet, abort
        if settings().getBoolean(["server", "firstRun"]) and (octoprint.server.userManager is None or not octoprint.server.userManager.hasBeenCustomized()):
            return make_response("AstroBox isn't setup yet", 403)

        # if API is globally enabled, enabled for this request and an api key is provided that is not the current UI API key, try to use that
        apikey = getApiKey(request)

        if settings().get(["api", "enabled"]) and apiEnabled and apikey is not None:
            if apikey != octoprint.server.UI_API_KEY:
                if apikey == settings().get(["api", "key"]):
                    # master key was used
                    user = ApiUser()
                else:
                    # user key might have been used
                    user = octoprint.server.userManager.findUser(apikey=apikey)

                if user is None:
                    return make_response("Invalid API key", 401)

                if login_user(user, remember=False):
                    identity_changed.send(current_app._get_current_object(), identity=Identity(user.get_id()))
                    return func(*args, **kwargs)

            else:
                return func(*args, **kwargs)

        return make_response("Invalid Api Key or API Disabled", 401)

    return decorated_view


def api_access(func):
    @wraps(func)
    def decorated_view(*args, **kwargs):
        if not settings().get(["api", "enabled"]):
            make_response("API disabled", 401)
        apikey = getApiKey(request)
        if apikey is None:
            make_response("No API key provided", 401)
        if apikey != settings().get(["api", "key"]):
            make_response("Invalid API key", 403)
        return func(*args, **kwargs)
    return decorated_view


def getUserForApiKey(apikey):
    if settings().get(["api", "enabled"]) and apikey is not None:
        if apikey == settings().get(["api", "key"]):
            # master key was used
            return ApiUser()
        else:
            # user key might have been used
            return octoprint.server.userManager.findUser(apikey=apikey)
    else:
        return None


def getApiKey(request):
    # Check Flask GET/POST arguments
    if hasattr(request, "values") and "apikey" in request.values:
        return request.values["apikey"]

    # Check Tornado GET/POST arguments
    if hasattr(request, "arguments") and "apikey" in request.arguments and len(request.arguments["apikey"].strip()) > 0:
        return request.arguments["apikey"]

    # Check Tornado and Flask headers
    if "X-Api-Key" in request.headers.keys():
        return request.headers.get("X-Api-Key")

    return None


#~~ Printer state


class PrinterStateConnection(SockJSConnection):
    EVENTS = [Events.UPDATED_FILES, Events.METADATA_ANALYSIS_FINISHED, Events.SLICING_STARTED, Events.SLICING_DONE, Events.SLICING_FAILED,
                Events.TRANSFER_STARTED, Events.TRANSFER_DONE, Events.CLOUD_DOWNLOAD, Events.ASTROPRINT_STATUS, Events.SOFTWARE_UPDATE,
                Events.CAPTURE_INFO_CHANGED, Events.LOCK_STATUS_CHANGED, Events.NETWORK_STATUS, Events.INTERNET_CONNECTING_STATUS,
                Events.GSTREAMER_EVENT, Events.TOOL_CHANGE]

    def __init__(self, userManager, eventManager, session):
        SockJSConnection.__init__(self, session)

        self._logger = logging.getLogger(__name__)

        self._temperatureBacklog = []
        self._temperatureBacklogMutex = threading.Lock()
        self._logBacklog = []
        self._logBacklogMutex = threading.Lock()
        self._messageBacklog = []
        self._messageBacklogMutex = threading.Lock()
        self._emitLock = threading.Lock()

        self._userManager = userManager
        self._eventManager = eventManager

    def _getRemoteAddress(self, request):
        forwardedFor = request.headers.get("X-Forwarded-For")
        if forwardedFor is not None:
            return forwardedFor.split(",")[0]
        return request.ip

    def on_open(self, request):
        s = settings()
        loggedUsername = s.get(["cloudSlicer", "loggedUser"])

        if loggedUsername:
            token = request.arguments.get("token")
            token = token[0] if token else None
            tokenContents = octoprint.server.read_ws_token(token)
            if not tokenContents or tokenContents['public_key'] != self._userManager.findUser(loggedUsername).publicKey:
                return False

        remoteAddress = self._getRemoteAddress(request)
        self._logger.info("New connection from client [IP address: %s, Session id: %s]", remoteAddress, self.session.session_id)

        # connected => update the API key, might be necessary if the client was left open while the server restarted
        self._emit("connected", {"apikey": octoprint.server.UI_API_KEY, "version": octoprint.server.VERSION, "sessionId": self.session.session_id})
        self.sendEvent(Events.ASTROPRINT_STATUS, boxrouterManager().status)

        printer = printerManager()

        printer.registerCallback(self)
        printer.fileManager.registerCallback(self)
        #EtherBoxHandler.registerCallback(self)

        self._eventManager.fire(Events.CLIENT_OPENED, {"remoteAddress": remoteAddress})
        for event in PrinterStateConnection.EVENTS:
            self._eventManager.subscribe(event, self._onEvent)

    def on_close(self):
        self._logger.info("Client connection closed [Session id: %s]", self.session.session_id)

        printer = printerManager()

        printer.unregisterCallback(self)
        printer.fileManager.unregisterCallback(self)
        cameraManager().closeLocalVideoSession(self.session.session_id)

        self._eventManager.fire(Events.CLIENT_CLOSED)
        for event in PrinterStateConnection.EVENTS:
{'fraction_non_alphanumeric': 0.0850375434955131, 'fraction_numerical': 0.004517428728404859, 'mean_word_length': 3.4061323292092522, 'pattern_counts': {'":': 8, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3075270', 'n_tokens_mistral': 4707, 'n_tokens_neox': 4482, 'n_words': 1397}