Skip to content

rdegraaf/dyphal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dyphal, the Dynamic Photo Album
===============================

Dyphal is a web photo album designed to allow photographers to show off their 
work without needing to turn to commercial providers that inject advertising 
and may do ethically dubious things with the photos and information that they 
host.

An album is displayed as a list of photos; each photo can be opened to show a 
larger view with metadata.  The photo's size scales to best fit the browser 
window.  An album can be navigated by mouse, touch, or keyboard.

An album can be hosted on any web document server; no server-side scripts or 
databases are required.  All necessary files are generated from original 
photographs and their metadata up front and stored on the server.  The album 
and its metadata files are designed to require minimal storage space and 
minimal network traffic. 

There is a demo album at <https://ciphertext.info/software/dyphal/#/demo>.


Design principles
-----------------

Dyphal was designed to satisfy the following goals:

1.  The photo is central.  It must be the most prominent element of the page. 
    It must be entirely visible within the browser window; scrolling is not 
    allowed.  Do not up-scale the photo beyond its actual size.  Do down-scale 
    the photo to fit the browser window if necessary.
2.  Metadata is important.  Information about what is in a photo, where and 
    when it was taken, and photographic properties such as exposure and 
    aperture must be visible for each photo.
3.  The album must not break the web.  Browsers' Forward and Back buttons must 
    work.  Bookmarks and saved links to photos and albums must work.  The URI 
    in the address bar must represent the current album or photo. 
4.  The album must work using a document server as the back end.
5.  The album must be as small as possible with as many elements as possible 
    shared between pages.  
6.  The album must use modern web standards with a minimum of cruft to support 
    old browsers.  It must follow security best practices and support CSP.
7.  All features of the album must work properly on recent versions of Chrome 
    and Firefox.  There should be no major problems in minor browsers such as 
    Opera or Edge.  
8.  All content must be in HTML or be inserted at run-time.  All styling must 
    be in CSS or inserted at run-time.
9.  Album styles should be changeable without needing to change scripts.


Generating Dyphal albums
------------------------

Dyphal albums are created using DyphalGenerator: 

1.  Use the **Add Photos** button to import photo files or gThumb 3 catalogs. 
    Photos can be re-ordered by dragging.
2.  Choose the caption and property fields to display for each photo.  The 
    fields available depend on what metadata tags are stored in the selected 
    photos.  By default, only the fields available in *all* selected photos are 
    listed; check the **Show all** box to list all fields that are available in 
    *any* selected photo.  Fields can be re-ordered by dragging.
3.  Set a title, description and footer for the album.  The title and footer 
    will appear on photo pages as well as the album page.  No HTML markup is 
    allowed.
4.  Choose the size for photos in the album.  Larger sizes will show more 
    detail, but require more space and time to load.
5.  Use the **Generate Album** button to save the album and generate the 
    down-scaled photos, thumbnails, and metadata files that it requires.
6.  If you haven't already done so, use the **Install Album Template** button 
    to install the Dyphal web template into the album directory.

If you generated the album "Vacation" in a directory served as 
http://example.com/photos and installed the web template to the same directory, 
you can view the album at http://example.com/photos/#/Vacation.

To edit an album in DyphalGenerator, open the ".dyphal" file for the album.

DyphalGenerator does not currently have a metadata editor.  Instead, it pulls 
metadata fields from embedded photo tags.  The following tags are recognized 
as "properties" (using exiftool's naming scheme): 

*   Composite:Aperture
*   Composite:DigitalZoom
*   Composite:DriveMode
*   Composite:FlashType
*   Composite:FOV
*   Composite:FocalLength35efl
*   Composite:HyperfocalDistance
*   Composite:ImageSize
*   Composite:Lens35efl
*   Composite:LensID
*   Composite:LightValue
*   Composite:ScaleFactor35efl
*   Composite:ShootingMode
*   Composite:ShutterSpeed
*   EXIF:DateTimeOriginal
*   EXIF:ExposureCompensation
*   EXIF:ExposureMode
*   EXIF:Flash
*   EXIF:FocalLength
*   EXIF:ISO
*   EXIF:Make
*   EXIF:Model
*   EXIF:Orientation
*   File:FileSize
*   File:FileType
*   MakerNotes:MacroMode
*   MakerNotes:Rotation

Only three fields are recognized as "captions": Description, Location, and 
Date.  They are synthesized from various tags that are commonly used to store 
such information (again, using exiftool's naming scheme):

*   Description
    *   XMP:Description
    *   IPTC:Caption-Abstract
    *   EXIF:UserComment
*   Location
    *   XMP:Location
    *   IPTC:ContentLocationName
*   Date:
    *   XMP:DateTimeOriginal
    *   Composite:DateTimeCreated
    *   EXIF:DateTimeOriginal

Recent versions of gThumb use these fields for metadata; there are probably 
other photo managers that do as well.  Older versions of gThumb stored metadata 
only in external XML files; the tool "gthumb-comment-update" can be used to 
import metadata from the XML files into embedded tags.


Serving Dyphal albums
---------------------

Nothing special is required to serve Dyphal albums, just a basic web document 
server such as Apache or lighttpd.  For improved security, server operators are 
encouraged to serve Dyphal albums over HTTPS with the following headers:

*   `Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 
    'self'; img-src 'self'; connect-src 'self';`
*   `X-Frame-Options: DENY`
*   `Strict-Transport-Security: max-age=31536000`

Since Dyphal uses asynchronous queries, it won't work from file:// URIs.  

The ".dyphal" files created by DyphalGeneraor should **not** be served with the 
web page.  These files are intended to allow DyphalGenerator to re-open and 
edit albums.


Technical details
-----------------

An album is represented by a single web page, index.html.  JavaScript running 
within that page determines what to display based on hash parameters.  The 
first parameter, which must always be present, identifies the album.  This 
value is the name of the JSON file that describes the album with the ".json" 
suffix omitted.  If your directory structure looks like this:

        album.css
        common.css
        dyphal.js
        index.html
        photo.css
        vacation.json
        photos/photo1.jpeg
        metadata/photo1.jpeg.json
        ...

then the path to load the "vacation" album is 

        /#/vacation

This will load **album view**, which shows the title and description of the 
album as a whole alongside thumbnails for every photo in the album.  Clicking 
on any one of the thumbnails will open **photo view** for that photo, 
containing a larger view of the selected photo and its metadata, along with 
controls to move to the previous or next photos in the album, or to return to 
album view.  The photo will re-scale to the largest size that fits in your 
browser window without hiding the header, footer, or metadata or going beyond 
its physical dimensions.  If the photo is not shown at its full size, clicking 
on it will open **overlay view**  to show the photo at the largest size that 
can fit in the window, potentially hiding the header, footer, and metadata. 
There is also a **help view** that can be opened from either album or photo 
view using the question mark icon.

When loaded in a window smaller than 750 CSS-pixels wide or tall, the album 
will use **compact layout** that omits the metadata and footer from photo 
view in order to show the photo at the largest size possible.  In compact 
layout, overlay view shows the metadata and footer rather than another view of 
the photo.  The album is also displayed full-screen in browsers that support 
this feature if the display is less than 750 CSS-pixels wide or tall (though, 
for technical reasons, the user needs to interact with the page before it can 
enter full-screen mode).

Each photo view has its own URI, consisting of the URI to the album followed by 
the index number of the photo (indices start from 1).  For example, the path 
for the third photo in the "Vacation" album might be

        /#/vacation/3

The photo and help overlays do not have independent URIs.

Since only one HTML document is used for all views, its differences in 
appearance are implemented by switching styles and, to a lesser extent, by 
adding and removing document elements.  Photos, thumbnails, and photo metadata 
are not stored in the HTML; they are loaded from JSON files and their contents 
are inserted or removed from the document when appropriate.  The album JSON 
file (`vacation.json` in the above example) contains a title, footer, and 
description of the album, plus a list of the photos in the album.  Each photo 
has its own JSON file (the photo's name from the album JSON with "`.json`" 
appended), containing the name of the photo file, its dimensions, and its 
metadata.  JSON files, thumbnails, and photos are loaded on demand; the JSON 
and photo for the next photo in the album are pre-cached to reduce load time. 

Appending "`/debug`" to any album or photo URI will load it in debug mode, 
which suppresses some of the styling, outlines various document elements in 
various colours, and logs some information to the browser console.

Everything is implemented in as standards-conformant a manner as possible.  
Browser-specific logic is implemented using feature detection rather than 
browser detection where possible; browser detection is only used to work around 
the buggy touch tracking in Android browsers and the broken scrolling in 
full-screen Internet Explorer because no ways to fix these issues using feature 
detection could be found.

If my JavaScript has idiosyncrasies, it's probably because I'm a C++ programmer 
who learned JavaScript by trial and error.


History
-------

The original version of Dyphal was the BestFit template for gThumb, which I 
wrote in 2005 and became part of gThumb starting with version 2.9.1 in 2006. 
That template was designed to take scale the photo to fit in the browser window 
and show metadata around its edges; superficially, it looked a lot like this 
album.  However, it was limited by the hacks required to support Internet 
Explorer 6 and albums built from that template consisted of a large number of 
repetitive generated HTML files.  

At some point after 2008, gThumb removed the BestFit template without telling 
its author (i.e., me) why.  Since working with the gThumb maintainer in the 
past had been a pain, I opted to not try to get it reinstated.  Instead, I 
dropped support for IE6 (simplifying the code considerably) and started work on 
a version where photos and metadata would be inserted into a single page at 
run-time using JavaScript.  I eventually merged the album and photo pages, 
accidentally re-invented JSON before learning how to use JSON properly, and 
ended up with this.  

The main disadvantage of the new album over the old gThumb-generated one is 
that I can no longer rely on gThumb to select the photos to include, create 
down-sized versions of them, and extract their metadata.  Instead, I wrote my 
own tool, DyphalGenerator.  gThumb, despite a couple annoying mis-features, 
still has the best support for photo metadata of any Linux photo manager that I 
could find, so DyphalGenerator pulls metadata from the places where gThumb 
stores it and pulls lists of photos from gThumb collections.


Security Model
--------------

DyphalGenerator, which generates the server-side data, fills the role of a 
server component.  It is trusted, as are the shell commands that it invokes. 
The image files loaded by DyphalGenerator are *not* trusted and are assumed to 
contain arbitrary malicious content.  The JSON files emitted by DyphalGenerator 
are partially trusted.  The following fields are safe to insert into URIs with 
no additional encoding:

*   `metadataDir`
*   `thumbnail`
*   `path`
*   `name`
*   `photo`

The following fields are restricted to hard-coded values:

*   `orientation`
*   `albumVersion`
*   `captionFields`
*   `propertyFields`
*   `photoResolution`
*   the first element of each `properties` tuple

All other values should be assumed to be attacker-controlled.

To be secure, DyphalGenerator must never emit photo URIs outside of the album 
directory and albums must never execute any scripts or interpret any markup 
found in properties, captions, or other fields.  Additionally, albums must 
never load JSON files or images from outside of the album directory.

See [javascript.html](javascript.html) for information about Dyphal's use of 
JavaScript.

The ".dyphal" files created by DyphalGenerator contain local paths and should 
not be served with the web page.

Why am I so concerned about TOCTOU in DyphalGenerator when there's no privilege 
separation to be attacked?  Practice.


Licence
-------

Dyphal is copyright (c) Rennie deGraaf, 2005-2023.  It is distributed under the 
terms of the GNU General Public Licence, either version 2 or (at your option) 
version 3.  See LICENCE or <http://www.gnu.org/licenses/> for details.


Contact information
-------------------

Rennie deGraaf (rennie-dot-degraaf-at-gmail-dot-com)

The latest version of Dyphal, including full source code, can be obtained at 
<https://ciphertext.info/software/dyphal/>.


Version VERSION, DATE