Search in sources :

Example 1 with RequestState

use of qz.auth.RequestState in project tray by qzind.

the class PrintSocketClient method processMessage.

/**
 * Determine which method was called from web API
 *
 * @param session WebSocket session
 * @param json    JSON received from web API
 */
private void processMessage(Session session, JSONObject json, SocketConnection connection, RequestState request) throws JSONException, SerialPortException, DeviceException, IOException, ListenerNotFoundException {
    String UID = json.optString("uid");
    SocketMethod call = SocketMethod.findFromCall(json.optString("call"));
    JSONObject params = json.optJSONObject("params");
    if (params == null) {
        params = new JSONObject();
    }
    if (call == SocketMethod.INVALID && (UID == null || UID.isEmpty())) {
        // incorrect message format, likely incompatible qz version
        session.close(4003, "Connected to incompatible " + Constants.ABOUT_TITLE + " version");
        return;
    }
    String prompt = call.getDialogPrompt();
    if (call == SocketMethod.PRINT) {
        // special formatting for print dialogs
        JSONObject pr = params.optJSONObject("printer");
        if (pr != null) {
            prompt = String.format(prompt, pr.optString("name", pr.optString("file", pr.optString("host", "an undefined location"))));
        } else {
            sendError(session, UID, "A printer must be specified before printing");
            return;
        }
    }
    if (call.isDialogShown() && !allowedFromDialog(request, prompt, findDialogPosition(session, json.optJSONObject("position")))) {
        sendError(session, UID, "Request blocked");
        return;
    }
    if (call != SocketMethod.GET_VERSION) {
        trayManager.voidIdleActions();
    }
    // used in usb calls
    DeviceOptions dOpts = new DeviceOptions(params, DeviceOptions.DeviceMode.parse(call.getCallName()));
    // call appropriate methods
    switch(call) {
        case PRINTERS_GET_DEFAULT:
            sendResult(session, UID, PrintServiceLookup.lookupDefaultPrintService() == null ? null : PrintServiceLookup.lookupDefaultPrintService().getName());
            break;
        case PRINTERS_FIND:
            if (params.has("query")) {
                String name = PrintServiceMatcher.findPrinterName(params.getString("query"));
                if (name != null) {
                    sendResult(session, UID, name);
                } else {
                    sendError(session, UID, "Specified printer could not be found.");
                }
            } else {
                JSONArray services = PrintServiceMatcher.getPrintersJSON();
                JSONArray names = new JSONArray();
                for (int i = 0; i < services.length(); i++) {
                    names.put(services.getJSONObject(i).getString("name"));
                }
                sendResult(session, UID, names);
            }
            break;
        case PRINTERS_DETAIL:
            sendResult(session, UID, PrintServiceMatcher.getPrintersJSON());
            break;
        case PRINTERS_START_LISTENING:
            if (!connection.hasStatusListener()) {
                connection.startStatusListener(new StatusSession(session));
            }
            StatusMonitor.startListening(connection, params.getJSONArray("printerNames"));
            sendResult(session, UID, null);
            break;
        case PRINTERS_GET_STATUS:
            if (connection.hasStatusListener()) {
                StatusMonitor.sendStatuses(connection);
            } else {
                sendError(session, UID, "No printer listeners started for this client.");
            }
            sendResult(session, UID, null);
            break;
        case PRINTERS_STOP_LISTENING:
            if (connection.hasStatusListener()) {
                connection.stopStatusListener();
            }
            sendResult(session, UID, null);
            break;
        case PRINT:
            PrintingUtilities.processPrintRequest(session, UID, params);
            break;
        case SERIAL_FIND_PORTS:
            sendResult(session, UID, SerialUtilities.getSerialPortsJSON());
            break;
        case SERIAL_OPEN_PORT:
            SerialUtilities.setupSerialPort(session, UID, connection, params);
            break;
        case SERIAL_SEND_DATA:
            {
                SerialOptions opts = null;
                // properties param is deprecated legacy here and will be overridden by options if provided
                if (!params.isNull("properties")) {
                    opts = new SerialOptions(params.optJSONObject("properties"), false);
                }
                if (!params.isNull("options")) {
                    opts = new SerialOptions(params.optJSONObject("options"), false);
                }
                SerialIO serial = connection.getSerialPort(params.optString("port"));
                if (serial != null) {
                    serial.sendData(params, opts);
                    sendResult(session, UID, null);
                } else {
                    sendError(session, UID, String.format("Serial port [%s] must be opened first.", params.optString("port")));
                }
                break;
            }
        case SERIAL_CLOSE_PORT:
            {
                SerialIO serial = connection.getSerialPort(params.optString("port"));
                if (serial != null) {
                    serial.close();
                    connection.removeSerialPort(params.optString("port"));
                    sendResult(session, UID, null);
                } else {
                    sendError(session, UID, String.format("Serial port [%s] is not open.", params.optString("port")));
                }
                break;
            }
        case SOCKET_OPEN_PORT:
            SocketUtilities.setupSocket(session, UID, connection, params);
            break;
        case SOCKET_SEND_DATA:
            {
                String location = String.format("%s:%s", params.optString("host"), params.optInt("port"));
                SocketIO socket = connection.getNetworkSocket(location);
                if (socket != null) {
                    socket.sendData(params);
                    sendResult(session, UID, null);
                } else {
                    sendError(session, UID, String.format("Socket [%s] is not open.", location));
                }
                break;
            }
        case SOCKET_CLOSE_PORT:
            {
                String location = String.format("%s:%s", params.optString("host"), params.optInt("port"));
                SocketIO socket = connection.getNetworkSocket(location);
                if (socket != null) {
                    socket.close();
                    connection.removeNetworkSocket(location);
                    sendResult(session, UID, null);
                } else {
                    sendError(session, UID, String.format("Socket [%s] is not open.", location));
                }
                break;
            }
        case USB_LIST_DEVICES:
            sendResult(session, UID, UsbUtilities.getUsbDevicesJSON(params.getBoolean("includeHubs")));
            break;
        case USB_LIST_INTERFACES:
            sendResult(session, UID, UsbUtilities.getDeviceInterfacesJSON(dOpts));
            break;
        case USB_LIST_ENDPOINTS:
            sendResult(session, UID, UsbUtilities.getInterfaceEndpointsJSON(dOpts));
            break;
        case HID_LIST_DEVICES:
            if (SystemUtilities.isWindows()) {
                sendResult(session, UID, PJHA_HidUtilities.getHidDevicesJSON());
            } else {
                sendResult(session, UID, H4J_HidUtilities.getHidDevicesJSON());
            }
            break;
        case HID_START_LISTENING:
            if (!connection.isDeviceListening()) {
                if (SystemUtilities.isWindows()) {
                    connection.startDeviceListening(new PJHA_HidListener(session));
                } else {
                    connection.startDeviceListening(new H4J_HidListener(session));
                }
                sendResult(session, UID, null);
            } else {
                sendError(session, UID, "Already listening HID device events");
            }
            break;
        case HID_STOP_LISTENING:
            if (connection.isDeviceListening()) {
                connection.stopDeviceListening();
                sendResult(session, UID, null);
            } else {
                sendError(session, UID, "Not already listening HID device events");
            }
            break;
        case USB_CLAIM_DEVICE:
        case HID_CLAIM_DEVICE:
            {
                if (connection.getDevice(dOpts) == null) {
                    DeviceIO device;
                    if (call == SocketMethod.USB_CLAIM_DEVICE) {
                        device = new UsbIO(dOpts);
                    } else {
                        if (SystemUtilities.isWindows()) {
                            device = new PJHA_HidIO(dOpts);
                        } else {
                            device = new H4J_HidIO(dOpts);
                        }
                    }
                    if (session.isOpen()) {
                        connection.openDevice(device, dOpts);
                    }
                    if (device.isOpen()) {
                        sendResult(session, UID, null);
                    } else {
                        sendError(session, UID, "Failed to open connection to device");
                    }
                } else {
                    sendError(session, UID, String.format("USB Device [v:%s p:%s] is already claimed.", params.opt("vendorId"), params.opt("productId")));
                }
                break;
            }
        case USB_CLAIMED:
        case HID_CLAIMED:
            {
                sendResult(session, UID, connection.getDevice(dOpts) != null);
                break;
            }
        case USB_SEND_DATA:
        case HID_SEND_FEATURE_REPORT:
        case HID_SEND_DATA:
            {
                DeviceIO usb = connection.getDevice(dOpts);
                if (usb != null) {
                    if (call == SocketMethod.HID_SEND_FEATURE_REPORT) {
                        usb.sendFeatureReport(DeviceUtilities.getDataBytes(params, null), dOpts.getEndpoint());
                    } else {
                        usb.sendData(DeviceUtilities.getDataBytes(params, null), dOpts.getEndpoint());
                    }
                    sendResult(session, UID, null);
                } else {
                    sendError(session, UID, String.format("USB Device [v:%s p:%s] must be claimed first.", params.opt("vendorId"), params.opt("productId")));
                }
                break;
            }
        case USB_READ_DATA:
        case HID_GET_FEATURE_REPORT:
        case HID_READ_DATA:
            {
                DeviceIO usb = connection.getDevice(dOpts);
                if (usb != null) {
                    byte[] response;
                    if (call == SocketMethod.HID_GET_FEATURE_REPORT) {
                        response = usb.getFeatureReport(dOpts.getResponseSize(), dOpts.getEndpoint());
                    } else {
                        response = usb.readData(dOpts.getResponseSize(), dOpts.getEndpoint());
                    }
                    JSONArray hex = new JSONArray();
                    for (byte b : response) {
                        hex.put(UsbUtil.toHexString(b));
                    }
                    sendResult(session, UID, hex);
                } else {
                    sendError(session, UID, String.format("USB Device [v:%s p:%s] must be claimed first.", params.opt("vendorId"), params.opt("productId")));
                }
                break;
            }
        case USB_OPEN_STREAM:
        case HID_OPEN_STREAM:
            {
                StreamEvent.Stream stream = (call == SocketMethod.USB_OPEN_STREAM ? StreamEvent.Stream.USB : StreamEvent.Stream.HID);
                UsbUtilities.setupUsbStream(session, UID, connection, dOpts, stream);
                break;
            }
        case USB_CLOSE_STREAM:
        case HID_CLOSE_STREAM:
            {
                DeviceIO usb = connection.getDevice(dOpts);
                if (usb != null && usb.isStreaming()) {
                    usb.setStreaming(false);
                    sendResult(session, UID, null);
                } else {
                    sendError(session, UID, String.format("USB Device [v:%s p:%s] is not streaming data.", params.opt("vendorId"), params.opt("productId")));
                }
                break;
            }
        case USB_RELEASE_DEVICE:
        case HID_RELEASE_DEVICE:
            {
                DeviceIO usb = connection.getDevice(dOpts);
                if (usb != null) {
                    usb.close();
                    connection.removeDevice(dOpts);
                    sendResult(session, UID, null);
                } else {
                    sendError(session, UID, String.format("USB Device [v:%s p:%s] is not claimed.", params.opt("vendorId"), params.opt("productId")));
                }
                break;
            }
        case FILE_START_LISTENING:
            {
                FileParams fileParams = new FileParams(params);
                Path absPath = FileUtilities.getAbsolutePath(params, request, true);
                FileIO fileIO = new FileIO(session, params, fileParams.getPath(), absPath);
                if (connection.getFileListener(absPath) == null && !fileIO.isWatching()) {
                    connection.addFileListener(absPath, fileIO);
                    FileUtilities.setupListener(fileIO);
                    sendResult(session, UID, null);
                } else {
                    sendError(session, UID, "Already listening to path events");
                }
                break;
            }
        case FILE_STOP_LISTENING:
            {
                if (params.isNull("path")) {
                    connection.removeAllFileListeners();
                    sendResult(session, UID, null);
                } else {
                    Path absPath = FileUtilities.getAbsolutePath(params, request, true);
                    FileIO fileIO = connection.getFileListener(absPath);
                    if (fileIO != null) {
                        fileIO.close();
                        FileWatcher.deregisterWatch(fileIO);
                        connection.removeFileListener(absPath);
                        sendResult(session, UID, null);
                    } else {
                        sendError(session, UID, "Not already listening to path events");
                    }
                }
                break;
            }
        case FILE_LIST:
            {
                Path absPath = FileUtilities.getAbsolutePath(params, request, true);
                if (Files.exists(absPath)) {
                    if (Files.isDirectory(absPath)) {
                        ArrayList<String> files = new ArrayList<>();
                        Files.list(absPath).forEach(file -> files.add(file.getFileName().toString()));
                        sendResult(session, UID, new JSONArray(files));
                    } else {
                        log.error("Failed to list '{}' (not a directory)", absPath);
                        sendError(session, UID, "Path is not a directory");
                    }
                } else {
                    log.error("Failed to list '{}' (does not exist)", absPath);
                    sendError(session, UID, "Path does not exist");
                }
                break;
            }
        case FILE_READ:
            {
                Path absPath = FileUtilities.getAbsolutePath(params, request, false);
                if (Files.exists(absPath)) {
                    if (Files.isReadable(absPath)) {
                        sendResult(session, UID, new String(Files.readAllBytes(absPath)));
                    } else {
                        log.error("Failed to read '{}' (not readable)", absPath);
                        sendError(session, UID, "Path is not readable");
                    }
                } else {
                    log.error("Failed to read '{}' (does not exist)", absPath);
                    sendError(session, UID, "Path does not exist");
                }
                break;
            }
        case FILE_WRITE:
            {
                FileParams fileParams = new FileParams(params);
                Path absPath = FileUtilities.getAbsolutePath(params, request, false);
                Files.write(absPath, fileParams.getData(), StandardOpenOption.CREATE, fileParams.getAppendMode());
                FileUtilities.inheritParentPermissions(absPath);
                sendResult(session, UID, null);
                break;
            }
        case FILE_REMOVE:
            {
                Path absPath = FileUtilities.getAbsolutePath(params, request, false);
                if (Files.exists(absPath)) {
                    Files.delete(absPath);
                    sendResult(session, UID, null);
                } else {
                    log.error("Failed to remove '{}' (does not exist)", absPath);
                    sendError(session, UID, "Path does not exist");
                }
                break;
            }
        case NETWORKING_DEVICE_LEGACY:
            JSONObject networkDevice = NetworkUtilities.getDeviceJSON(params);
            JSONObject legacyDevice = new JSONObject();
            legacyDevice.put("ipAddress", networkDevice.optString("ip", null));
            legacyDevice.put("macAddress", networkDevice.optString("mac", null));
            sendResult(session, UID, legacyDevice);
            break;
        case NETWORKING_DEVICE:
            sendResult(session, UID, NetworkUtilities.getDeviceJSON(params));
            break;
        case NETWORKING_DEVICES:
            sendResult(session, UID, NetworkUtilities.getDevicesJSON(params));
            break;
        case GET_VERSION:
            sendResult(session, UID, Constants.VERSION);
            break;
        case WEBSOCKET_STOP:
            log.info("Another instance of {} is asking this to close", Constants.ABOUT_TITLE);
            String challenge = json.optString("challenge", "");
            if (SystemUtilities.validateSaltedChallenge(challenge)) {
                log.info("Challenge validated: {}, honoring shutdown request", challenge);
                session.close(SingleInstanceChecker.REQUEST_INSTANCE_TAKEOVER);
                try {
                    server.stop();
                } catch (Exception ignore) {
                }
                trayManager.exit(0);
            } else {
                log.warn("A valid challenge was not provided: {}, ignoring request to close", challenge);
            }
            break;
        case INVALID:
        default:
            sendError(session, UID, "Invalid function call: " + json.optString("call", "NONE"));
            break;
    }
}
Also used : TrayManager(qz.common.TrayManager) WebSocketException(org.eclipse.jetty.websocket.api.WebSocketException) StatusMonitor(qz.printer.status.StatusMonitor) SerialPortException(jssc.SerialPortException) qz.communication(qz.communication) TimeoutException(java.util.concurrent.TimeoutException) HashMap(java.util.HashMap) java.nio.file(java.nio.file) UsbUtil(javax.usb.util.UsbUtil) ArrayList(java.util.ArrayList) org.eclipse.jetty.websocket.api.annotations(org.eclipse.jetty.websocket.api.annotations) Locale(java.util.Locale) Session(org.eclipse.jetty.websocket.api.Session) Constants(qz.common.Constants) Server(org.eclipse.jetty.server.Server) CloseException(org.eclipse.jetty.websocket.api.CloseException) StatusSession(qz.printer.status.StatusSession) RequestState(qz.auth.RequestState) Semaphore(java.util.concurrent.Semaphore) JSONObject(org.codehaus.jettison.json.JSONObject) JSONArray(org.codehaus.jettison.json.JSONArray) Certificate(qz.auth.Certificate) IOException(java.io.IOException) Reader(java.io.Reader) CertificateException(java.security.cert.CertificateException) EOFException(java.io.EOFException) qz.utils(qz.utils) java.awt(java.awt) IOUtils(org.apache.commons.io.IOUtils) PrintServiceMatcher(qz.printer.PrintServiceMatcher) Logger(org.apache.logging.log4j.Logger) JSONException(org.codehaus.jettison.json.JSONException) ListenerNotFoundException(javax.management.ListenerNotFoundException) PrintServiceLookup(javax.print.PrintServiceLookup) LogManager(org.apache.logging.log4j.LogManager) StatusSession(qz.printer.status.StatusSession) JSONArray(org.codehaus.jettison.json.JSONArray) ArrayList(java.util.ArrayList) WebSocketException(org.eclipse.jetty.websocket.api.WebSocketException) SerialPortException(jssc.SerialPortException) TimeoutException(java.util.concurrent.TimeoutException) CloseException(org.eclipse.jetty.websocket.api.CloseException) IOException(java.io.IOException) CertificateException(java.security.cert.CertificateException) EOFException(java.io.EOFException) JSONException(org.codehaus.jettison.json.JSONException) ListenerNotFoundException(javax.management.ListenerNotFoundException) JSONObject(org.codehaus.jettison.json.JSONObject)

Example 2 with RequestState

use of qz.auth.RequestState in project tray by qzind.

the class PrintSocketClient method onMessage.

@OnWebSocketMessage
public void onMessage(Session session, Reader reader) throws IOException {
    String message = IOUtils.toString(reader);
    if (message == null || message.isEmpty()) {
        sendError(session, null, "Message is empty");
        return;
    }
    if (Constants.PROBE_REQUEST.equals(message)) {
        try {
            session.getRemote().sendString(Constants.PROBE_RESPONSE);
        } catch (Exception ignore) {
        }
        log.warn("Second instance of {} likely detected, asking it to close", Constants.ABOUT_TITLE);
        return;
    }
    // keep-alive call / no need to process
    if ("ping".equals(message)) {
        return;
    }
    String UID = null;
    try {
        log.debug("Message: {}", message);
        JSONObject json = new JSONObject(message);
        UID = json.optString("uid");
        Integer connectionPort = session.getRemoteAddress().getPort();
        SocketConnection connection = openConnections.get(connectionPort);
        RequestState request = new RequestState(connection.getCertificate(), json);
        // if sent a certificate use that instead for this connection
        if (json.has("certificate")) {
            try {
                Certificate certificate = new Certificate(json.optString("certificate"));
                connection.setCertificate(certificate);
                request.markNewConnection(certificate);
                log.debug("Received new certificate from connection through {}", connectionPort);
            } catch (CertificateException ignore) {
                request.markNewConnection(Certificate.UNKNOWN);
            }
            if (allowedFromDialog(request, "connect to " + Constants.ABOUT_TITLE, findDialogPosition(session, json.optJSONObject("position")))) {
                sendResult(session, UID, null);
            } else {
                sendError(session, UID, "Connection blocked by client");
                session.disconnect();
            }
            // this is a setup call, so no further processing is needed
            return;
        }
        // check request signature
        if (request.hasCertificate()) {
            if (json.optLong("timestamp") + Constants.VALID_SIGNING_PERIOD < System.currentTimeMillis() || json.optLong("timestamp") - Constants.VALID_SIGNING_PERIOD > System.currentTimeMillis()) {
                // bad timestamps use the expired certificate
                log.warn("Expired signature on request");
                request.setStatus(RequestState.Validity.EXPIRED);
            } else if (json.isNull("signature") || !validSignature(request.getCertUsed(), json)) {
                // bad signatures use the unsigned certificate
                log.warn("Bad signature on request");
                request.setStatus(RequestState.Validity.UNSIGNED);
            } else {
                log.trace("Valid signature from {}", request.getCertName());
                request.setStatus(RequestState.Validity.TRUSTED);
            }
        }
        processMessage(session, json, connection, request);
    } catch (JSONException e) {
        log.error("Bad JSON: {}", e.getMessage());
        sendError(session, UID, e);
    } catch (InvalidPathException | FileSystemException e) {
        log.error("FileIO exception occurred", e);
        sendError(session, UID, String.format("FileIO exception occurred: %s: %s", e.getClass().getSimpleName(), e.getMessage()));
    } catch (Exception e) {
        log.error("Problem processing message", e);
        sendError(session, UID, e);
    }
}
Also used : RequestState(qz.auth.RequestState) JSONObject(org.codehaus.jettison.json.JSONObject) JSONException(org.codehaus.jettison.json.JSONException) CertificateException(java.security.cert.CertificateException) WebSocketException(org.eclipse.jetty.websocket.api.WebSocketException) SerialPortException(jssc.SerialPortException) TimeoutException(java.util.concurrent.TimeoutException) CloseException(org.eclipse.jetty.websocket.api.CloseException) IOException(java.io.IOException) CertificateException(java.security.cert.CertificateException) EOFException(java.io.EOFException) JSONException(org.codehaus.jettison.json.JSONException) ListenerNotFoundException(javax.management.ListenerNotFoundException) Certificate(qz.auth.Certificate)

Aggregations

EOFException (java.io.EOFException)2 IOException (java.io.IOException)2 CertificateException (java.security.cert.CertificateException)2 TimeoutException (java.util.concurrent.TimeoutException)2 ListenerNotFoundException (javax.management.ListenerNotFoundException)2 SerialPortException (jssc.SerialPortException)2 JSONException (org.codehaus.jettison.json.JSONException)2 JSONObject (org.codehaus.jettison.json.JSONObject)2 CloseException (org.eclipse.jetty.websocket.api.CloseException)2 WebSocketException (org.eclipse.jetty.websocket.api.WebSocketException)2 Certificate (qz.auth.Certificate)2 RequestState (qz.auth.RequestState)2 java.awt (java.awt)1 Reader (java.io.Reader)1 java.nio.file (java.nio.file)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 Locale (java.util.Locale)1 Semaphore (java.util.concurrent.Semaphore)1 PrintServiceLookup (javax.print.PrintServiceLookup)1