| 1 | /* |
| 2 | * noVNC: HTML5 VNC client |
| 3 | * Copyright (C) 2019 The noVNC Authors |
| 4 | * Licensed under MPL 2.0 (see LICENSE.txt) |
| 5 | * |
| 6 | * See README.md for usage and integration instructions. |
| 7 | */ |
| 8 | |
| 9 | import * as Log from '../core/util/logging.js'; |
| 10 | |
| 11 | // init log level reading the logging HTTP param |
| 12 | export function initLogging(level) { |
| 13 | "use strict"; |
| 14 | if (typeof level !== "undefined") { |
| 15 | Log.initLogging(level); |
| 16 | } else { |
| 17 | const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/); |
| 18 | Log.initLogging(param || undefined); |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | // Read a query string variable |
| 23 | // A URL with a query parameter can look like this (But will most probably get logged on the http server): |
| 24 | // https://www.example.com?myqueryparam=myvalue |
| 25 | // |
| 26 | // For privacy (Using a hastag #, the parameters will not be sent to the server) |
| 27 | // the url can be requested in the following way: |
| 28 | // https://www.example.com#myqueryparam=myvalue&password=secretvalue |
| 29 | // |
| 30 | // Even Mixing public and non public parameters will work: |
| 31 | // https://www.example.com?nonsecretparam=example.com#password=secretvalue |
| 32 | export function getQueryVar(name, defVal) { |
| 33 | "use strict"; |
| 34 | const re = new RegExp('.*[?&]' + name + '=([^&#]*)'), |
| 35 | match = document.location.href.match(re); |
| 36 | if (typeof defVal === 'undefined') { defVal = null; } |
| 37 | |
| 38 | if (match) { |
| 39 | return decodeURIComponent(match[1]); |
| 40 | } |
| 41 | |
| 42 | return defVal; |
| 43 | } |
| 44 | |
| 45 | // Read a hash fragment variable |
| 46 | export function getHashVar(name, defVal) { |
| 47 | "use strict"; |
| 48 | const re = new RegExp('.*[&#]' + name + '=([^&]*)'), |
| 49 | match = document.location.hash.match(re); |
| 50 | if (typeof defVal === 'undefined') { defVal = null; } |
| 51 | |
| 52 | if (match) { |
| 53 | return decodeURIComponent(match[1]); |
| 54 | } |
| 55 | |
| 56 | return defVal; |
| 57 | } |
| 58 | |
| 59 | // Read a variable from the fragment or the query string |
| 60 | // Fragment takes precedence |
| 61 | export function getConfigVar(name, defVal) { |
| 62 | "use strict"; |
| 63 | const val = getHashVar(name); |
| 64 | |
| 65 | if (val === null) { |
| 66 | return getQueryVar(name, defVal); |
| 67 | } |
| 68 | |
| 69 | return val; |
| 70 | } |
| 71 | |
| 72 | /* |
| 73 | * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html |
| 74 | */ |
| 75 | |
| 76 | // No days means only for this browser session |
| 77 | export function createCookie(name, value, days) { |
| 78 | "use strict"; |
| 79 | let date, expires; |
| 80 | if (days) { |
| 81 | date = new Date(); |
| 82 | date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); |
| 83 | expires = "; expires=" + date.toGMTString(); |
| 84 | } else { |
| 85 | expires = ""; |
| 86 | } |
| 87 | |
| 88 | let secure; |
| 89 | if (document.location.protocol === "https:") { |
| 90 | secure = "; secure"; |
| 91 | } else { |
| 92 | secure = ""; |
| 93 | } |
| 94 | document.cookie = name + "=" + value + expires + "; path=/" + secure; |
| 95 | } |
| 96 | |
| 97 | export function readCookie(name, defaultValue) { |
| 98 | "use strict"; |
| 99 | const nameEQ = name + "="; |
| 100 | const ca = document.cookie.split(';'); |
| 101 | |
| 102 | for (let i = 0; i < ca.length; i += 1) { |
| 103 | let c = ca[i]; |
| 104 | while (c.charAt(0) === ' ') { |
| 105 | c = c.substring(1, c.length); |
| 106 | } |
| 107 | if (c.indexOf(nameEQ) === 0) { |
| 108 | return c.substring(nameEQ.length, c.length); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | return (typeof defaultValue !== 'undefined') ? defaultValue : null; |
| 113 | } |
| 114 | |
| 115 | export function eraseCookie(name) { |
| 116 | "use strict"; |
| 117 | createCookie(name, "", -1); |
| 118 | } |
| 119 | |
| 120 | /* |
| 121 | * Setting handling. |
| 122 | */ |
| 123 | |
| 124 | let settings = {}; |
| 125 | |
| 126 | export function initSettings() { |
| 127 | if (!window.chrome || !window.chrome.storage) { |
| 128 | settings = {}; |
| 129 | return Promise.resolve(); |
| 130 | } |
| 131 | |
| 132 | return new Promise(resolve => window.chrome.storage.sync.get(resolve)) |
| 133 | .then((cfg) => { settings = cfg; }); |
| 134 | } |
| 135 | |
| 136 | // Update the settings cache, but do not write to permanent storage |
| 137 | export function setSetting(name, value) { |
| 138 | settings[name] = value; |
| 139 | } |
| 140 | |
| 141 | // No days means only for this browser session |
| 142 | export function writeSetting(name, value) { |
| 143 | "use strict"; |
| 144 | if (settings[name] === value) return; |
| 145 | settings[name] = value; |
| 146 | if (window.chrome && window.chrome.storage) { |
| 147 | window.chrome.storage.sync.set(settings); |
| 148 | } else { |
| 149 | localStorageSet(name, value); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | export function readSetting(name, defaultValue) { |
| 154 | "use strict"; |
| 155 | let value; |
| 156 | if ((name in settings) || (window.chrome && window.chrome.storage)) { |
| 157 | value = settings[name]; |
| 158 | } else { |
| 159 | value = localStorageGet(name); |
| 160 | settings[name] = value; |
| 161 | } |
| 162 | if (typeof value === "undefined") { |
| 163 | value = null; |
| 164 | } |
| 165 | |
| 166 | if (value === null && typeof defaultValue !== "undefined") { |
| 167 | return defaultValue; |
| 168 | } |
| 169 | |
| 170 | return value; |
| 171 | } |
| 172 | |
| 173 | export function eraseSetting(name) { |
| 174 | "use strict"; |
| 175 | // Deleting here means that next time the setting is read when using local |
| 176 | // storage, it will be pulled from local storage again. |
| 177 | // If the setting in local storage is changed (e.g. in another tab) |
| 178 | // between this delete and the next read, it could lead to an unexpected |
| 179 | // value change. |
| 180 | delete settings[name]; |
| 181 | if (window.chrome && window.chrome.storage) { |
| 182 | window.chrome.storage.sync.remove(name); |
| 183 | } else { |
| 184 | localStorageRemove(name); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | let loggedMsgs = []; |
| 189 | function logOnce(msg, level = "warn") { |
| 190 | if (!loggedMsgs.includes(msg)) { |
| 191 | switch (level) { |
| 192 | case "error": |
| 193 | Log.Error(msg); |
| 194 | break; |
| 195 | case "warn": |
| 196 | Log.Warn(msg); |
| 197 | break; |
| 198 | case "debug": |
| 199 | Log.Debug(msg); |
| 200 | break; |
| 201 | default: |
| 202 | Log.Info(msg); |
| 203 | } |
| 204 | loggedMsgs.push(msg); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | let cookiesMsg = "Couldn't access noVNC settings, are cookies disabled?"; |
| 209 | |
| 210 | function localStorageGet(name) { |
| 211 | let r; |
| 212 | try { |
| 213 | r = localStorage.getItem(name); |
| 214 | } catch (e) { |
| 215 | if (e instanceof DOMException) { |
| 216 | logOnce(cookiesMsg); |
| 217 | logOnce("'localStorage.getItem(" + name + ")' failed: " + e, |
| 218 | "debug"); |
| 219 | } else { |
| 220 | throw e; |
| 221 | } |
| 222 | } |
| 223 | return r; |
| 224 | } |
| 225 | function localStorageSet(name, value) { |
| 226 | try { |
| 227 | localStorage.setItem(name, value); |
| 228 | } catch (e) { |
| 229 | if (e instanceof DOMException) { |
| 230 | logOnce(cookiesMsg); |
| 231 | logOnce("'localStorage.setItem(" + name + "," + value + |
| 232 | ")' failed: " + e, "debug"); |
| 233 | } else { |
| 234 | throw e; |
| 235 | } |
| 236 | } |
| 237 | } |
| 238 | function localStorageRemove(name) { |
| 239 | try { |
| 240 | localStorage.removeItem(name); |
| 241 | } catch (e) { |
| 242 | if (e instanceof DOMException) { |
| 243 | logOnce(cookiesMsg); |
| 244 | logOnce("'localStorage.removeItem(" + name + ")' failed: " + e, |
| 245 | "debug"); |
| 246 | } else { |
| 247 | throw e; |
| 248 | } |
| 249 | } |
| 250 | } |