From 6e91a15ca71f3c0303ee0b9608ab1d28ce9a8e8a Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Mon, 24 Jun 2024 11:07:53 +0200 Subject: [PATCH] Sanitize filenames of downloads. Some routes have characters like ":" in them which can be illegal on certain filesystems. Remove the known common ones and replace them with an underscore. --- README.md | 29 ++++++++++++++----------- background.js | 60 +++++++++++++++++++++++++++++++++++++++++---------- manifest.json | 2 +- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 5e58206..bc253fb 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,28 @@ # SAC Route Portal GPX Downloader -The [Swiss Alpine Club](https://www.sac-cas.ch/en/) has a great -[route portal](https://www.sac-cas.ch/en/huts-and-tours/sac-route-portal/) -for finding interesting hiking routes. -However you can not download the tracks as gpx files, as they argue that the exact -paths are subject to change and it could be dangerous to adhere too closely to them. +The [Swiss Alpine Club](https://www.sac-cas.ch/en/) has a great +[route portal](https://www.sac-cas.ch/en/huts-and-tours/sac-route-portal/) for +finding interesting hiking routes. -I do agree with that, but you can draw your own routes -and download those, mainly taking some time to draw after the route they are -already showing on the map. +However you can not download the tracks as gpx files, as they argue that the +exact paths are subject to change and it could be dangerous to adhere too +closely to them. -With this extension you can select any track and then download it as a gpx file +I do agree with that, but you can draw your own routes and download those, +mainly taking some time to draw after the route they are already showing on the +map. + +With this extension you can select any track and then download it as a gpx file (an active subscription is needed). ## Be Cautious! -Heed their warning and do not blindly follow the gps track! Use your brain and -plan your route beforehand to be aware of dangerous parts and alternatives. -Take a look at their [safety instructions](https://www.sac-cas.ch/en/training-and-safety/safety/). +Heed their warning and do not blindly follow the gps track! Use your brain and +plan your route beforehand to be aware of dangerous parts and alternatives. + +Take a look at their +[safety instructions](https://www.sac-cas.ch/en/training-and-safety/safety/). ## Contributors + - wizche diff --git a/background.js b/background.js index e03772c..e69a4e3 100644 --- a/background.js +++ b/background.js @@ -21,14 +21,16 @@ function toWGS84(point) { const x_aux = (point[1] - 1200000) / 1000000; // calculate longitude and latitude in the unit 10000" - let lat = 16.9023892 + + let lat = + 16.9023892 + 3.238272 * x_aux - 0.270978 * Math.pow(y_aux, 2) - 0.002528 * Math.pow(x_aux, 2) - 0.0447 * Math.pow(y_aux, 2) * x_aux - 0.014 * Math.pow(x_aux, 3); - let lon = 2.6779094 + + let lon = + 2.6779094 + 4.728982 * y_aux + 0.791484 * y_aux * x_aux + 0.1306 * y_aux * Math.pow(x_aux, 2) - @@ -102,10 +104,10 @@ function toGpx(geoJson) { const endPoint = geoJson.end_point ? toWayPoint(geoJson.end_point) : ""; const waypoints = geoJson.waypoints ? geoJson.waypoints - .map((wp) => { - return toWayPoint(wp.reference_poi); - }) - .join("") + .map((wp) => { + return toWayPoint(wp.reference_poi); + }) + .join("") : ""; const routeTitle = trackTitle(geoJson); @@ -179,6 +181,41 @@ function checkTrack(tab) { return tab.url.match("https://www.sac-cas.ch/.*") && gpxTrack; } +/** + * Remove characters that lead to problems in common filesystems. + * + * Windows (NTFS) seems to have the most restrictions, so it is mostly sourced from there. + * + * Reserved names on windows: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file + * + * Code comes from https://github.com/parshap/node-sanitize-filename/blob/master/index.js + */ +function sanitizeFilename(input) { + const illegal = /[\/\?<>\\:\*\|"]/g; + // deno-lint-ignore no-control-regex + const control = /[\x00-\x1f\x80-\x9f]/g; + const reserved = /^\.+$/; + const windowsReserved = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; + const windowsTrailing = /[\. ]+$/; + const replacement = "_"; + + const sanitized = input + .replace(illegal, replacement) + .replace(control, replacement) + .replace(reserved, replacement) + .replace(windowsReserved, replacement) + .replace(windowsTrailing, replacement); + + if (sanitized.length === 0) { + sanitized = "unnamed"; + } + + const maxLength = 250; + const uint8Array = new TextEncoder().encode(sanitized); + const truncated = uint8Array.slice(0, maxLength); + return new TextDecoder().decode(truncated); +} + /** * If a valid tack was selected, download it as a gpx file. */ @@ -191,9 +228,10 @@ function handleClick(tab) { const blob = new Blob([gpxTrack.data], { type: "application/gpx+xml" }); const objectURL = URL.createObjectURL(blob); + const filename = sanitizeFilename(gpxTrack.title); const downloading = browser.downloads.download({ url: objectURL, - filename: `${gpxTrack.title}.gpx`, + filename: `${filename}.gpx`, saveAs: true, conflictAction: "uniquify", }); @@ -231,11 +269,11 @@ function updateIcon(tab) { browser.browserAction.setIcon({ path: hasTrack ? { - 48: "icons/map.png", - } + 48: "icons/map.png", + } : { - 48: "icons/map-disabled.png", - }, + 48: "icons/map-disabled.png", + }, tabId: tab.id, }); browser.browserAction.setTitle({ diff --git a/manifest.json b/manifest.json index 8c464a2..4f2230d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "SAC Route Portal GPX Downloader", - "version": "0.8", + "version": "0.9", "developer": { "name": "Sebastian Hugentobler", "url": "https://code.vanwa.ch/sebastian/sac-route-portal-gpx-fx"