Resizing photos natively on the iPhone

In preparation for being able to upload pictures to my blog and have them get processed and added to posts, I wanted a way to shrink photos. It’s likely my main source of pictures will be my phone and with ballooning megapixel counts those tend to be pretty hefty. I can delegate resizing to the server-side but I thought I might as well find a way to resize them device-side to save on bandwidth costs and prevent my tiny blog app from blowing up.

I found this guide on how to create a shortcut that’s available in the “Share” menu for photos, that resizes them to the desired size and saves the result to the photo album, with this I can resize photos to 800x600 (typically). I was able to reduce a file’s size from 4.5MB to 450KB in a moment. Quality is lower and detail is lost, for sure, but this is my personal blog that nobody cares about so I’m OK with it.

Conservatives behaving like 12-year-olds

As Trump taunts Trudeau by calling him ‘governor’ of ‘a great state’, and Doug Ford crassly jokes “I’m sure not thinking of Justin Trudeau at midnight. So if he’s thinking of Justin at midnight, it’s probably a good relationship,”, it’s important to keep an eye on who the true enemies are. Conservatives will be conservatives, joking and taunting like middle-school bullies as seen above. You know who’s not joking? Pierre Poilievre. He’s the most conservative and fascist of all and is not telling jokes, instead using the incident to weaken and attack the current government. Just keep in mind that if he were in power, he’d be the first to fold to Trump’s demands.

Can people pray in public in Quebec?

Quebec Premier François Legault said Friday afternoon that he wants to ban praying in public and that he was considering using the notwithstanding clause to do so.

I find it incredibly worrisome that he continues to curtail basic human rights in the name of secularism. Secularism applies to state institutions (government offices, hospitals, schools) but does not in any way apply to individuals’ constitutionally protected freedoms in public spaces.

Legault is a power-crazed fascist, pure and simple - and he’s also a recalcitrant islamophobe because it’s entirely transparent whom he’s trying to target with these measures.

This asshat can’t be voted out of office soon enough - ideally before he does irreparable damage to Quebec’s inclusive multicultural society.

https://www.cbc.ca/news/canada/montreal/religion-in-schools-new-law-quebec-1.7403485

X-rays in DICOM format

When requesting copies of X-ray or other medical imaging studies, it’s typical to receive a CD-ROM containing the images in an apparently inscrutable DICOM format. The CD usually contains a viewer since it’s recognized that DICOM is not a format typically openable with consumer-grade applications; however, the viewer is typically a Windows-only application.

Luckily it turns out that DICOM is a widespread, well-documented and general standard for medical imaging, including provisions for metadata and custom fields, ability to use multi-layer images, and it supports all kinds of medical imaging needs, not just X-rays. So even if it’s a bit niche and not heard-of too much, it’s fairly easy to find ways to view DICOM images on Linux or Mac.

On Linux apt-cache search dicom reveals a plethora of applications able to not only open DICOM images, but connect to a DICOM network to send data back and forth. Some of them have a GUI but in the end I settled for dcmtk which has a command (dcm2pnm +on DICOM_FILE image.png) with which I was able to convert those files to pngs to share with, you know, actual humans who live in the 21st century.

At least they didn’t send us the imaging results by fax…

The web with no ads

I just noticed that after the upgrade to iOS 18, 1Blocker is no longer working, so I see ads in all websites on my phone. I’d forgotten how horrible the web with ads is - I use an ad blocker everywhere (uBlock Origin on desktop, 1Blocker on mobile) and going back to the adful web again is horrible. I hope they fix 1Blocker soon!

Mounting a Flask app under a URL prefix

As part of setting this thing up I had to learn a bit about how a WSGI app and the path under which it is mounted or exposed in a server’s URL hierarchy interact. The main key was this page that describes the rather obscure SCRIPT_NAME variable and how it designates URL components that WSGI will chop off or add back when sending requests back and forth with the fronting proxy (Apache in this case).

Long story short, in the systemd unit that starts the gunicorn/flask app I had to set SCRIPT_NAME=miniblog and in the Apache proxypass config, this:

       RequestHeader set X-Forwarded-Proto "https"
        ProxyPass "/miniblog" "http://localhost:19891/miniblog/"
        ProxyPassReverse "/miniblog" "http://localhost:10891/miniblog/"

Testing my mini blog

I wrote a primitive web-to-git-to-hugo pipeline so I can author blog content on the web while using hugo.

Updating to Exim 4.94 and Taintedness

I finally got around to updating my server to Debian Bullseye from Buster. The thing that had been holding me was this notice in the upgrade notes:

Please consider the version of Exim in bullseye a major Exim upgrade. It introduces the concept of tainted data read from untrusted sources. The basic strategy for dealing with this change is to use the result of a lookup in further processing instead of the original (remote provided) value.

To ease upgrading there is a new main configuration option to temporarily downgrade taint errors to warnings, letting the old configuration work with the newer Exim. To make use of this feature add

.ifdef _OPT_MAIN_ALLOW_INSECURE_TAINTED_DATA
 allow_insecure_tainted_data = yes
.endif

to the Exim configuration (e.g. to /etc/exim4/exim4.conf.localmacros) before upgrading and check the logfile for taint warnings. This is a temporary workaround which is already marked for removal on introduction.

This sounded scary, so I put it off for years, but then I decided to just do it since the docs above said one could set the allow_insecure option and then check the logs for specific problems. Alas, after the upgrade my exim started bouncing mails due to a lookup error:

temporarily rejected RCPT <someonetomechangosubanana.com>: Tainted name '/etc/exim4/WHATEVER' for file read not permitted

Long story short, instead of injecting a tainted variable when expanding a string; in this case, for a router’s file option like so:

store_and_forward_1:
        driver = redirect
        file=/etc/exim4/lists/${lc:$local_part}@${lc:$domain}.remote
        forbid_pipe
        forbid_file
        unseen

lists:
    driver = redirect
    file=/etc/exim4/lists/${lc:$local_part}@${lc:$domain}
    forbid_pipe
    forbid_file

domain_catchall:
        driver = redirect
        file=/etc/exim4/lists/${lc:$domain}
        forbid_pipe
        forbid_file

One has to do a lookup instead:

store_and_forward_1:
        driver = redirect
        file=${lookup {${local_part}@${domain}.remote} dsearch,ret=full {/etc/exim4/lists} {$value} fail}
        forbid_pipe
        forbid_file
        unseen
lists:
    driver = redirect
    file=${lookup {${local_part}@${domain}} dsearch,ret=full {/etc/exim4/lists} {$value} fail}
    forbid_pipe
    forbid_file

domain_catchall:
        driver = redirect
        file=${lookup {${lc:domain}} dsearch,ret=full {/etc/exim4/lists} {$value} fail}
        forbid_pipe
        forbid_file

The lookup syntax is devilish.

${lookup {KEY} dsearch,ret=full {ABS_DIR} {$value} fail}

  1       2    3       4         5         6       7
  1. The operation to perform, a single-key lookup in this case.
  2. The key to search for. In the examples above, we build the key from user-input data, which is fine by the tainting rules, as long as that’s used just to look up data in a database, table or file listing. The point of taintedness is to NOT use the tainted value itself to build values that will go, for example, in filenames.
  3. This is the type of lookup, dsearch is “directory search” - this will look for a file named KEY in the ABS_DIR directory, and return (this is the important part) the NAME OF THE FILE IT FOUND, not the value of the key (which might be evil).
  4. ret=full just means “return the entire value”, in this case, the full path, rather than just the file name.
  5. ABS_DIR is the directory where we will search for the file.
  6. {$value} is what gets returned if the lookup is successful. In this case we want to return the actual value that was found.
  7. If the lookup fails, then this value gets returned. If not specified, it always returns the empty string, which resulted in another error:"" is not an absolute path because then it thinks we’re assigning "" to the router’s file. Instead, what we want is for the thing to fail so the router gets marked as unprocessed and the processing continues in the normal order. Specifying the special value fail gives that behavior.

With these changes, the routers work as they did before, while following the rules about when and how to use a tainted user-input value.

Luckily for the upgrade to Bookworm and Exim 4.96, there is no such breaking change in exim configuration!