139 lines
4.3 KiB
Perl
139 lines
4.3 KiB
Perl
|
|
#!/usr/bin/perl
|
||
|
|
#
|
||
|
|
# Plack::Middleware::DW::SubdomainFunction
|
||
|
|
#
|
||
|
|
# Handles subdomain-based routing that matches the Apache::LiveJournal::trans
|
||
|
|
# behavior for functional subdomains (shop, support, mobile, cssproxy) and
|
||
|
|
# user journal subdomains (username.dreamwidth.org).
|
||
|
|
#
|
||
|
|
# Authors:
|
||
|
|
# Mark Smith <mark@dreamwidth.org>
|
||
|
|
#
|
||
|
|
# Copyright (c) 2026 by Dreamwidth Studios, LLC.
|
||
|
|
#
|
||
|
|
# This program is free software; you may redistribute it and/or modify it under
|
||
|
|
# the same terms as Perl itself. For a copy of the license, please reference
|
||
|
|
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
||
|
|
#
|
||
|
|
|
||
|
|
package Plack::Middleware::DW::SubdomainFunction;
|
||
|
|
|
||
|
|
use strict;
|
||
|
|
use v5.10;
|
||
|
|
use Log::Log4perl;
|
||
|
|
my $log = Log::Log4perl->get_logger(__PACKAGE__);
|
||
|
|
|
||
|
|
use parent qw/ Plack::Middleware /;
|
||
|
|
|
||
|
|
use DW::Request;
|
||
|
|
use LJ::Session;
|
||
|
|
|
||
|
|
sub call {
|
||
|
|
my ( $self, $env ) = @_;
|
||
|
|
|
||
|
|
my $r = DW::Request->get;
|
||
|
|
my $host = $r->host;
|
||
|
|
|
||
|
|
# Extract subdomain: match (subdomain).USER_DOMAIN, skip www
|
||
|
|
if ( $LJ::USER_DOMAIN
|
||
|
|
&& $host =~ /^(www\.)?([\w\-]{1,25})\.\Q$LJ::USER_DOMAIN\E$/
|
||
|
|
&& $2 ne "www" )
|
||
|
|
{
|
||
|
|
# www.username.domain → redirect to canonical (drop www prefix)
|
||
|
|
return $r->redirect( "$LJ::PROTOCOL://$2.$LJ::USER_DOMAIN" . $r->path )
|
||
|
|
if $1 && $1 eq 'www.';
|
||
|
|
|
||
|
|
my $user = $2;
|
||
|
|
|
||
|
|
# Handle __setdomsess on any subdomain — sets the domain session cookie
|
||
|
|
# and redirects to the destination. Matches Apache::LiveJournal::trans
|
||
|
|
# behavior for shop (line 959) and journal subdomains (line 704).
|
||
|
|
if ( $r->path eq '/__setdomsess' ) {
|
||
|
|
return $r->redirect( LJ::Session->setdomsess_handler );
|
||
|
|
}
|
||
|
|
|
||
|
|
my $func = $LJ::SUBDOMAIN_FUNCTION{$user};
|
||
|
|
|
||
|
|
# "normal" means treat as www (ignore subdomain)
|
||
|
|
if ( $func && $func eq 'normal' ) {
|
||
|
|
|
||
|
|
# fall through to app
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( $func && $func eq 'cssproxy' ) {
|
||
|
|
$env->{PATH_INFO} = '/extcss';
|
||
|
|
return $self->app->($env);
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( $func && $func eq 'shop' ) {
|
||
|
|
my $uri = $r->path;
|
||
|
|
$uri =~ s/\/$//;
|
||
|
|
my $args = $env->{QUERY_STRING};
|
||
|
|
my $dest = "$LJ::SITEROOT/shop$uri";
|
||
|
|
$dest .= "?$args" if $args && length $args;
|
||
|
|
return $r->redirect($dest);
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( $func && $func eq 'support' ) {
|
||
|
|
return $r->redirect("$LJ::SITEROOT/support/");
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( $func && $func eq 'mobile' ) {
|
||
|
|
my $uri = $r->path;
|
||
|
|
return $r->redirect("$LJ::SITEROOT/mobile$uri");
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( $func && ref $func eq 'ARRAY' && $func->[0] eq 'changehost' ) {
|
||
|
|
my $args = $env->{QUERY_STRING};
|
||
|
|
my $dest = "$LJ::PROTOCOL://$func->[1]" . $r->path;
|
||
|
|
$dest .= "?$args" if $args && length $args;
|
||
|
|
return $r->redirect($dest);
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( $func && $func eq 'userpics' ) {
|
||
|
|
|
||
|
|
# Userpic subdomain (e.g., v.dreamwidth.org): URLs are /{picid}/{userid}.
|
||
|
|
# Rewrite to /userpic/{picid}/{userid} to match DW::Controller::Userpic's
|
||
|
|
# route. Skip domain session bounce since userpics are public images that
|
||
|
|
# don't need authentication. (Apache::LiveJournal::trans line 982)
|
||
|
|
$env->{PATH_INFO} = "/userpic" . $r->path;
|
||
|
|
$env->{'dw.skip_domain_bounce'} = 1;
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( $func && $func eq 'journal' ) {
|
||
|
|
|
||
|
|
# "journal" function: URI contains /username/path
|
||
|
|
my $uri = $r->path;
|
||
|
|
if ( $uri =~ m!^/(\w{1,25})(/.*)?$! ) {
|
||
|
|
$env->{'dw.journal_user'} = $1;
|
||
|
|
$env->{'dw.journal_path'} = $2 || '/';
|
||
|
|
}
|
||
|
|
|
||
|
|
# else: not a valid journal path, let it fall through (will 404)
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( !$func ) {
|
||
|
|
|
||
|
|
# No SUBDOMAIN_FUNCTION entry: treat subdomain as username
|
||
|
|
if ( $user eq 'shop' ) {
|
||
|
|
|
||
|
|
# Legacy shop subdomain without config: rewrite URI
|
||
|
|
my $path = $r->path;
|
||
|
|
$path =~ s/\/$//;
|
||
|
|
$env->{PATH_INFO} = "/shop$path";
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
# User journal subdomain (username.dreamwidth.org)
|
||
|
|
$env->{'dw.journal_user'} = $user;
|
||
|
|
$env->{'dw.journal_path'} = $r->path || '/';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# else: unknown $func, fall through
|
||
|
|
}
|
||
|
|
|
||
|
|
return $self->app->($env);
|
||
|
|
}
|
||
|
|
|
||
|
|
1;
|