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, Firefox, Internet Explorer, and Android. There should be no major problems in minor browsers such as Opera and on old but still common browsers such as Internet Explorer 9.
  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):

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):

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:

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 taught himself 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 (ie, 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:

The following fields are restricted to hard-coded values:

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 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-2017. 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 3.0-beta2-7-g4d3905e-dirty, 2017-01-21