WSGIDaemonProcess stickynotes2modernpaste user=apache group=apache threads=5
WSGIScriptAlias /stickynotes2modernpaste /usr/share/stickynotes2modernpaste/stickynotes2modernpaste.wsgi
WSGISocketPrefix run/wsgi
# Grab a cup of coffee, a light snack, and turn on some classical music.
# You're in for a bit of a novel.
#
# The below rules are worthy of some comment so that later on when I (or
# heaven forbid anyone else) have to revisit them for some horrible reason,
# they can be referred to and maybe (but unlikely) useful.
#
# Chapter 1. Background.
#
# The rewrite rules exist solely for the purpose of continuing to support old
# `fpaste` (the CLI app). This is in the process of being rewritten, and one
# day we won't have to support it anymore. But for now, we do, because it's on
# live media (and, I believe, Desktop installs, by default), and when a user is
# having issues and asking for help in IRC, they need to be able to use
# `fpaste` to do so. So, that is why we care about `fpaste` in its current
# (F25-F26) form.
#
# You see, fpaste was written in such a way that it makes a lot of weird
# assumptions that don't hold anymore. I will not speculate on why it was
# written the way it was, but I _will_ briefly outline some of the intricacies
# of supporting it.
#
# First off the workflow is something like this:
# 1. User wants to paste some text. Who knows why they want to do this. Maybe
# they are bored and want to see how broken our rewrite rules are. Maybe
# they hate me and want to see me cry trying to fix them. Who knows?!
#
# 2. The fpaste client makes a POST on their behalf to /. This POST payload
# includes the text of the paste and some other information (paste
# language, etc).
#
# 3. The server sees the POST, matches it against our rules below, and
# decides that it needs to redirect them to stickynotes2modernpaste, a
# custom Flask app that I (relrod) wrote so that we could handle requests
# that are in the form our old stickynotes pastebin accepted, and proxy
# them to modernpaste.
#
# Note that this matches the first set of crazy RewriteConds below. We
# only want to send CLI users there, and only when they POST to /. At
# this point, at least.
#
# 4. sn2mp says "okay cool," proxies the paste to modernpaste via its JSON
# API, and returns back to fpaste a JSON blob that contains JSON with two
# keys that fpaste requires exists. In our response, one of them is always
# an empty string, and the other is the id of the paste, prefixed with
# "paste/".
#
# 5. At this point, fpaste has enough information to return a URL to the
# paste. However, things are not all okay in the world. You see, fpaste
# wants to show a short-url too. Apparently people don't like typing or
# something. To generate a short-url, the fpaste client sends another POST
# to us, at the path "/paste/[the paste id]//". In the past, when it would
# do this, stickynotes would return a JSON blob that included the
# short-url. In fact, it would always include the short-url at the
# third-line from the last in its JSON response, and the fpaste client
# hardcoded that assumption. See Chapter 2 for information about the "//".
#
# 6. When we get this second POST, we again send the client to sn2mp. We add
# a few more crazy RewriteConds to ensure that we only add this behavior
# for fpaste and not most users. We know paste IDs are 22-24 characters
# long (as per https://github.com/LINKIWI/modern-paste/pull/33) and that
# the client will always POST to "/paste/[the paste id]//". So we match on
# that. If we match, sn2mp will take everything after its name and append
# it to the URL that ultimately gets shipped to da.gd for shortening. Then
# it returns a (malformed) JSON blob that is written in exactly the way
# fpaste expects.
#
# 7. Then fpaste shows the user both URLs, and all is okay.
#
# Chapter 2. Trailing slashes.
#
# The fpaste client defaults to private mode, but modernpaste doesn't support
# that, per se. You can password-protect pastes, but that's about it.
#
# However, they way stickynotes worked, it used /[paste id]/[secret] when a
# paste was private. Since modernpaste does things differently, sn2mp never
# returns the [secret] part of that URL. Or rather, it returns the empty string
# in its place. This means, by default (private mode = true), fpaste will
# both render, and internally use, a URL that has /[paste id]/[secret]. But
# since [secret] is the empty string, this is equivalent to /[paste id]/, with
# the trailing slash.
#
# To make matters worse, this little gem is found in the fpaste procedure for
# doing the second POST (#5 and 6 above):
#
# eq = urllib.request.Request(url=long_url+'/', data=params.encode())
#
# Yep, it adds a '/' for the second POST. So we get POSTs to
# /paste/[paste id]// during the second POST.
#
# Our capture of the paste ID below (the ".{22,24}" part) will match the first
# trailing slash, but not the second (because of the /$ that comes after).
# Nevertheless sn2mp handles all three cases anyway, and will STRIP OFF
# trailing slashes if they occur 0, 1, or 2 times.
#
# Lastly, the long url that fpaste shows the user contains one trailing slash
# (due to the /[secret] part from how stickynotes worked). So we add one final
# rewrite that redirects users who go to that, to the non-slash version.
#
# If you have made it this far, you are a champion. You should get a badge.
#
# Warm regards and good luck,
# relrod
RewriteEngine on
#LogLevel alert rewrite:trace6
RewriteRule login / [L,R]
RewriteCond %{HTTP_USER_AGENT} ^fpaste\/0\.3.*$ [OR]
RewriteCond %{HTTP_USER_AGENT} ^Python\-urllib.*$
RewriteCond %{REQUEST_METHOD} POST
RewriteRule ^/$ /stickynotes2modernpaste/$1 [L,PT]
RewriteCond %{HTTP_USER_AGENT} ^fpaste\/0\.3.*$ [OR]
RewriteCond %{HTTP_USER_AGENT} ^Python\-urllib.*$
RewriteCond %{REQUEST_METHOD} POST
RewriteRule ^/paste/(.{22,24})/$ /stickynotes2modernpaste/paste/$1 [L,PT]
# Otherwise, if we're given a URL with a trailing slash, kill it.
RewriteRule ^/paste/([^/]{22,24})/$ /paste/$1 [R,L]
WSGIScriptAlias / /usr/share/modern-paste/modern_paste.wsgi
WSGIProcessGroup stickynotes2modernpaste
WSGIApplicationGroup %{GLOBAL}
WSGIScriptReloading On
Order deny,allow
Require all granted
Require all granted
DocumentRoot /usr/share/modern-paste
#ErrorLog logs/modern-paste-error.log
#CustomLog logs/modern-paste-access.log combined
#LogLevel info