mike-jumper commented on a change in pull request #698:
URL: https://github.com/apache/guacamole-client/pull/698#discussion_r806087238



##########
File path: 
extensions/guacamole-history-recording-storage/src/main/java/org/apache/guacamole/history/connection/HistoryConnectionRecord.java
##########
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.history.connection;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.environment.LocalEnvironment;
+import org.apache.guacamole.io.GuacamoleReader;
+import org.apache.guacamole.io.ReaderGuacamoleReader;
+import org.apache.guacamole.language.TranslatableMessage;
+import org.apache.guacamole.net.auth.ActivityLog;
+import org.apache.guacamole.net.auth.ConnectionRecord;
+import org.apache.guacamole.net.auth.DelegatingConnectionRecord;
+import org.apache.guacamole.net.auth.FileActivityLog;
+import org.apache.guacamole.properties.FileGuacamoleProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ConnectionRecord implementation that automatically defines ActivityLogs for
+ * files that relate to the wrapped record.
+ */
+public class HistoryConnectionRecord extends DelegatingConnectionRecord {
+
+    /**
+     * Logger for this class.
+     */
+    private static final Logger logger = 
LoggerFactory.getLogger(HistoryConnectionRecord.class);
+
+    /**
+     * The namespace for URL UUIDs as defined by RFC 4122.
+     */
+    private static final UUID UUID_NAMESPACE_URL = 
UUID.fromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
+
+    /**
+     * The filename suffix of typescript timing files.
+     */
+    private static final String TIMING_FILE_SUFFIX = ".timing";
+
+    /**
+     * The default directory to search for associated session recordings, if
+     * not overridden with the "recording-search-path" property.
+     */
+    private static final File DEFAULT_RECORDING_SEARCH_PATH = new 
File("/var/lib/guacamole/recordings");
+
+    /**
+     * The directory to search for associated session recordings. By default,
+     * "/var/lib/guacamole/recordings" will be used.
+     */
+    private static final FileGuacamoleProperty RECORDING_SEARCH_PATH = new 
FileGuacamoleProperty() {
+
+        @Override
+        public String getName() {
+            return "recording-search-path";
+        }
+
+    };
+
+    /**
+     * The recording file associated with the wrapped connection record. This
+     * may be a single file or a directory that may contain any number of
+     * relevant recordings.
+     */
+    private final File recording;
+
+    /**
+     * Creates a new HistoryConnectionRecord that wraps the given
+     * ConnectionRecord, automatically associating ActivityLogs based on
+     * related files (session recordings, typescripts, etc.).
+     *
+     * @param record
+     *     The ConnectionRecord to wrap.
+     *
+     * @throws GuacamoleException
+     *     If the configured path for stored recordings cannot be read.
+     */
+    public HistoryConnectionRecord(ConnectionRecord record) throws 
GuacamoleException {
+        super(record);
+
+        Environment environment = LocalEnvironment.getInstance();
+        File recordingPath = environment.getProperty(RECORDING_SEARCH_PATH,
+                DEFAULT_RECORDING_SEARCH_PATH);
+
+        String uuid = record.getUUID().toString();
+        File recordingFile = new File(recordingPath, uuid);
+        this.recording = recordingFile.canRead() ? recordingFile : null;
+
+    }
+
+    /**
+     * Returns whether the given file appears to be a Guacamole session
+     * recording. As there is no standard extension for session recordings,
+     * this is determined by attempting to read a single Guacamole instruction
+     * from the file.
+     *
+     * @param file
+     *     The file to test.
+     *
+     * @return
+     *     true if the file appears to be a Guacamole session recording, false
+     *     otherwise.
+     */
+    private boolean isSessionRecording(File file) {
+
+        try (Reader reader = new InputStreamReader(new FileInputStream(file), 
StandardCharsets.UTF_8)) {
+
+            GuacamoleReader guacReader = new ReaderGuacamoleReader(reader);
+            if (guacReader.readInstruction() != null)
+                return true;
+
+        }
+        catch (GuacamoleException e) {
+            logger.debug("File \"{}\" does not appear to be a session "
+                    + "recording, as it could not be parsed as Guacamole "
+                    + "protocol data.", file, e);
+        }
+        catch (IOException e) {
+            logger.warn("Possible session recording \"{}\" could not be "
+                    + "identified as it cannot be read: {}", file, 
e.getMessage());
+            logger.debug("Possible session recording \"{}\" could not be 
read.", file, e);
+        }
+
+        return false;
+
+    }
+
+    /**
+     * Returns whether the given file appears to be a typescript (text
+     * recording of a terminal session). As there is no standard extension for
+     * session recordings, this is determined by testing whether there is an
+     * associated timing file. Guacamole will always include a timing file for
+     * its typescripts.
+     *
+     * @param file
+     *     The file to test.
+     *
+     * @return
+     *     true if the file appears to be a typescript, false otherwise.
+     */
+    private boolean isTypescript(File file) {
+        return new File(file.getAbsolutePath() + TIMING_FILE_SUFFIX).exists();
+    }
+
+    /**
+     * Returns whether the given file appears to be a typescript timing file.
+     * Typescript timing files have the standard extension ".timing".
+     *
+     * @param file
+     *     The file to test.
+     *
+     * @return
+     *     true if the file appears to be a typescript timing file, false
+     *     otherwise.
+     */
+    private boolean isTypescriptTiming(File file) {
+        return file.getName().endsWith(TIMING_FILE_SUFFIX);
+    }
+
+    /**
+     * Returns the type of session recording or log contained within the given
+     * file by inspecting its name and contents.
+     *
+     * @param file
+     *     The file to test.
+     *
+     * @return
+     *     The type of session recording or log contained within the given
+     *     file, or null if this cannot be determined.

Review comment:
       No, you're right - it will not currently return `null` under any 
circumstance. I defined explicit meaning and handling for `null` here to ensure 
that the caller does not assume that this will always be the case. While the 
function will currently never return `null`, that's due to an implementation 
detail of the function and not some inherent property of what the function does.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to