# HG changeset patch # User David Barts # Date 1585776234 25200 # Node ID efd9fe2d70d7fe33904082e6061d89a1b8b7b364 # Parent 42277ce58ace01d5b47ff063fafc7977bb706188 Rationalize exceptions, code whitelist, add command-line tool. diff -r 42277ce58ace -r efd9fe2d70d7 src/name/blackcap/exifwasher/Test2.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/Test2.kt Wed Apr 01 14:23:54 2020 -0700 @@ -0,0 +1,56 @@ +/* + * A basic test of the library: try to use it to print out the EXIF + * data. + */ +package name.blackcap.exifwasher + +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import name.blackcap.exifwasher.exiv2.* + +/* entry point */ +fun main(args: Array) { + /* must have a file name */ + if (args.size != 1) { + System.err.println("expecting a file name") + System.exit(1) + } + + /* copy the file; we don't want to scribble on the original */ + val (name, ext) = splitext(args[0]) + val newName = "${name}_washed${ext}" + FileInputStream(args[0]).use { source -> + FileOutputStream(newName).use { target -> + source.copyTo(target) + } + } + + /* load the whitelist and image data */ + val white = Whitelist.parse(PROPERTIES.getProperty("whitelist")) + val image = Image(newName) + + /* do the washing */ + val meta = image.metadata + val keys = meta.keys + val keysin = keys.size + var deleted = 0 + keys.forEach { + if (!white.contains(it)) { + meta.erase(it) + deleted += 1 + } + } + + /* save and summarize */ + image.store() + println("${keysin} in - ${deleted} deleted = ${keysin - deleted} out") +} + +fun splitext(s: String): Pair { + val pos = s.lastIndexOf('.') + if (pos == -1) { + return Pair(s, "") + } + return Pair(s.substring(0, pos), s.substring(pos)) +} diff -r 42277ce58ace -r efd9fe2d70d7 src/name/blackcap/exifwasher/Whitelist.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/Whitelist.kt Wed Apr 01 14:23:54 2020 -0700 @@ -0,0 +1,48 @@ +/* + * An exif key whitelist. Supports both prefixes and entire strings. + */ +package name.blackcap.exifwasher + +import java.util.regex.Pattern +import kotlin.collections.mutableSetOf +import kotlin.collections.mutableListOf + +class Whitelist { + private val entire = mutableSetOf() + private val prefixes = mutableListOf() + + fun addEntire(s: String) = entire.add(s) + + fun addPrefix(s: String) = prefixes.add(s) + + private fun autoOp(s: String, pfx: (String) -> Boolean, ent: (String) -> Boolean): Boolean { + return if (s.endsWith('*')) { pfx(s.dropLast(1)) } else { ent(s) } + } + + fun add(s: String) = autoOp(s, ::addPrefix, ::addEntire) + + fun removeEntire(s: String) = entire.remove(s) + + fun removePrefix(s: String) = prefixes.remove(s) + + fun remove(s: String) = autoOp(s, ::removePrefix, ::removeEntire) + + fun contains(s: String) = entire.contains(s) || prefixes.find { s.startsWith(it) } != null + + fun toList(): List = mutableListOf().also { + it.addAll(entire) + it.addAll(prefixes) + it.sort() + } + + override fun toString(): String = toList().joinToString(",") + + companion object { + private val SPLITTER = Pattern.compile(",\\s*") + fun parse(raw: String) = Whitelist().also { + for (s in raw.split(SPLITTER)) { + it.add(s) + } + } + } +} diff -r 42277ce58ace -r efd9fe2d70d7 src/name/blackcap/exifwasher/default.properties --- a/src/name/blackcap/exifwasher/default.properties Tue Mar 31 15:38:25 2020 -0700 +++ b/src/name/blackcap/exifwasher/default.properties Wed Apr 01 14:23:54 2020 -0700 @@ -1,1 +1,17 @@ -# A placeholder, because we currently don't use properties. +whitelist=\ + Exif.Image.Orientation,\ + Exif.Image.ResolutionUnit,\ + Exif.Image.XResolution,\ + Exif.Image.YCbCrCoefficients,\ + Exif.Image.YCbCrPositioning,\ + Exif.Image.YCbCrSubSampling,\ + Exif.Image.YResolution,\ + Exif.Iop.*,\ + Exif.Photo.ColorSpace,\ + Exif.Photo.ComponentsConfiguration,\ + Exif.Photo.CompressedBitsPerPixel,\ + Exif.Photo.ExifVersion,\ + Exif.Photo.InteroperabilityTag,\ + Exif.Photo.PixelXDimension,\ + Exif.Photo.PixelYDimension,\ + Exif.Thumbnail.* diff -r 42277ce58ace -r efd9fe2d70d7 src/name/blackcap/exifwasher/exiv2/Initialize.kt --- a/src/name/blackcap/exifwasher/exiv2/Initialize.kt Tue Mar 31 15:38:25 2020 -0700 +++ b/src/name/blackcap/exifwasher/exiv2/Initialize.kt Wed Apr 01 14:23:54 2020 -0700 @@ -61,7 +61,6 @@ die("unable to create ${tPath}: ${message}") } try { - println("loading: ${tPath}") /* debug */ System.load(tPath) } catch (e: UnsatisfiedLinkError) { val message = e.message ?: "unsatisfied link" @@ -70,7 +69,6 @@ } initialized = true - System.err.println("libraries loaded") /* debug */ } private fun die(message: String) { diff -r 42277ce58ace -r efd9fe2d70d7 src/name/blackcap/exifwasher/exiv2/native.cpp --- a/src/name/blackcap/exifwasher/exiv2/native.cpp Tue Mar 31 15:38:25 2020 -0700 +++ b/src/name/blackcap/exifwasher/exiv2/native.cpp Wed Apr 01 14:23:54 2020 -0700 @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -30,15 +31,10 @@ jlong ret = 0; try { ret = reinterpret_cast (Exiv2::ImageFactory::open(cPath).release()); - } catch (...) { + } catch (std::exception& e) { jEnv->ExceptionClear(); jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); - const char *pfx = "unable to open "; - char *message = (char *) malloc(strlen(cPath) + strlen(pfx) + 1); - strcpy(message, pfx); - strcat(message, cPath); - jEnv->ThrowNew(ex, message); - free(message); + jEnv->ThrowNew(ex, e.what()); } jEnv->ReleaseStringUTFChars(path, cPath); return ret; @@ -55,10 +51,10 @@ if (jEnv->ExceptionCheck()) return; try { image->writeMetadata(); - } catch (...) { + } catch (std::exception& e) { jEnv->ExceptionClear(); jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); - jEnv->ThrowNew(ex, "unable to write metadata"); + jEnv->ThrowNew(ex, e.what()); } } @@ -73,10 +69,10 @@ if (jEnv->ExceptionCheck()) return 0; try { image->readMetadata(); - } catch (...) { + } catch (std::exception& e) { jEnv->ExceptionClear(); jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); - jEnv->ThrowNew(ex, "unable to read metadata"); + jEnv->ThrowNew(ex, e.what()); return 0; } return reinterpret_cast (&(image->exifData())); @@ -118,15 +114,10 @@ Exiv2::ExifData::iterator found = data->findKey(Exiv2::ExifKey(std::string(cKey))); try { data->erase(found); - } catch (...) { + } catch (std::exception& e) { jEnv->ExceptionClear(); jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); - const char *pfx = "unable to delete "; - char *message = (char *) malloc(strlen(cKey) + strlen(pfx) + 1); - strcpy(message, pfx); - strcat(message, cKey); - jEnv->ThrowNew(ex, message); - free(message); + jEnv->ThrowNew(ex, e.what()); } jEnv->ReleaseStringUTFChars(key, cKey); }