root/trunk/cgi-bin/Apache/LiveJournal.pm @ 1082

Revision 1082, 9.2 KB (checked in by bradfitz, 11 years ago)

hi, my name is brad and i am a crack monkey.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/perl
2#
3
4package Apache::LiveJournal;
5
6use strict;
7use Apache::Constants qw(:common REDIRECT HTTP_NOT_MODIFIED);
8use Apache::File ();
9use CGI;
10
11my %RQ;       # per-request data
12my %USERPIC;  # conf related to userpics
13
14# init handler.
15sub handler
16{
17    my $r = shift;
18
19    $r->set_handlers(PerlTransHandler => [ \&trans ]);
20    $r->push_handlers(PerlCleanupHandler => sub { %RQ = (); });
21   
22    $USERPIC{'cache_dir'} = "$ENV{'LJHOME'}/htdocs/userpics";
23    $USERPIC{'use_disk_cache'} = -d $USERPIC{'cache_dir'};
24
25    return OK;
26}
27
28sub trans
29{
30    my $r = shift;
31    my $uri = $r->uri;
32    my $host = $r->header_in("Host");
33
34    LJ::start_request();
35
36    return trans_userpic($r, $1) if $uri =~ m!^/userpic/(\d+)$!;
37
38    my $redir = sub {
39        my $url = shift;
40        $r->content_type("text/html");
41        $r->header_out(Location => $url);
42        return REDIRECT;
43    };
44   
45    my $journal_view = sub {
46        my $opts = shift;
47        $opts ||= {};
48
49        if ($opts->{'user'} ne lc($opts->{'user'})) {
50            my $url = LJ::journal_base(lc($opts->{'user'}), $opts->{'vhost'}) .
51                "/$opts->{'mode'}$opts->{'args'}";
52            return $redir->($url);
53        }
54
55        $opts->{'user'} = LJ::canonical_username($opts->{'user'});
56
57        if ($opts->{'mode'} eq "info") {
58            return $redir->("$LJ::SITEROOT/userinfo.bml?user=$opts->{'user'}");
59        }
60
61        %RQ = %$opts;
62        $r->handler("perl-script");
63        $r->push_handlers(PerlHandler => \&journal_content);
64        return OK;
65    };
66
67    if ($LJ::USER_VHOSTS &&
68        $host =~ /^([\w\-]{1,15})\.\Q$LJ::USER_DOMAIN\E(:\d+)?$/ &&
69        $1 ne "www")
70    {
71        my $user = $1;
72        return $journal_view->({'vhost' => 'users',
73                                'mode' => $1,
74                                'args' => $2,
75                                'user' => $user, })
76            if $uri =~ m!/(\w+)?([^\?]*)!;
77        return $journal_view->(undef); # undef
78    }
79
80    if ($LJ::DOMAIN_PREPEND_WWW &&
81        $host =~ /^\Q$LJ::DOMAIN\E(:\d+)?$/)
82    {
83        $r->content_type("text/html");
84        $r->header_out(Location => "$LJ::SITEROOT$uri");
85        return REDIRECT;
86    }
87
88    # normal (non-domain) journal view
89    if ($uri =~ m!
90        ^/(users\/|community\/|\~)  # users/community/tilde
91        (\w{1,15})                  # mandatory username
92        (?:/(\w+)?)?                # optional /<viewname>
93        ([^\?]*)                    # extra args
94        !x && ($3 eq "" || defined $LJ::viewinfo{$3}))
95    {
96        my $vhost = { 'users/' => '', 'community/' => 'community',
97                      '~' => 'tilde' }->{$1};
98        return $journal_view->({'vhost' => $vhost,
99                                'mode' => $3,
100                                'args' => $4,
101                                'user' => $2, });
102    }
103
104    return FORBIDDEN if $uri =~ m!^/userpics!;
105    return DECLINED;
106}
107
108sub trans_userpic
109{
110    my $r = shift;
111    my $picid = shift;
112
113    # we can safely do this without checking since we never re-use
114    # picture IDs and don't let the contents get modified
115    return HTTP_NOT_MODIFIED if $r->header_in('If-Modified-Since');
116
117    $RQ{'picid'} = $picid;
118
119    my @dirs_make;
120    my $file;
121    if ($picid =~ /^\d*(\d\d)(\d\d\d)$/) {
122        push @dirs_make, ("$USERPIC{'cache_dir'}/$2",
123                          "$USERPIC{'cache_dir'}/$2/$1");
124        $file = "$USERPIC{'cache_dir'}/$2/$1/$picid";
125    } else {
126        my $mod = sprintf("%03d", $picid % 1000);
127        push @dirs_make, "$USERPIC{'cache_dir'}/$mod";
128        $file = "$USERPIC{'cache_dir'}/$mod/p$picid";
129    }
130
131    if ($USERPIC{'use_disk_cache'}) {
132        foreach (@dirs_make) {
133            next if -d $_;
134            mkdir $_, 0777;
135        }
136    }
137
138    # set both, so we can compared later if they're the same,
139    # and thus know if directories were created (if not,
140    # apache will give us a pathinfo)
141    $RQ{'userpicfile'} = $file;
142    $r->filename($file);
143
144    $r->handler("perl-script");
145    $r->push_handlers(PerlHandler => \&content_userpic);
146    return OK;
147}
148
149sub content_userpic
150{
151    my $r = shift;
152    my $file = $r->filename;
153
154    my $picid = $RQ{'picid'};
155
156    # will we try to use disk cache?
157    my $disk_cache = $USERPIC{'use_disk_cache'} &&
158        $file eq $RQ{'userpicfile'};
159
160    my ($pic, $data, $lastmod);
161    my $need_cache;
162
163    my $mime = "image/jpeg";
164    my $set_mime = sub {
165        my $data = shift;
166        if ($data =~ /^GIF/) { $mime = "image/gif"; }
167        elsif ($data =~ /^\x89PNG/) { $mime = "image/png"; }
168    };
169    my $size;
170
171    my $send_headers = sub {
172        $r->content_type($mime);
173        $r->header_out("Content-length", $size);
174        $r->header_out("Expires", LJ::time_to_http(time()+3000000));
175        $r->header_out("Cache-Control", "no-transform");
176        $r->header_out("Last-Modified", LJ::time_to_http($lastmod));
177        $r->send_http_header();
178    };
179
180    # try to get it from disk if in disk-cache mode
181    if ($disk_cache) {
182        if (-s $r->finfo) {
183            $lastmod = (stat _)[9];
184            $size = -s _;
185            my $fh = Apache::File->new($file);
186            my $magic;
187            read($fh, $magic, 4);
188            $set_mime->($magic);
189            $send_headers->();
190            $r->print($magic);
191            $r->send_fd($fh);
192            $fh->close();
193            return OK;
194        } else {
195            $need_cache = 1;
196        }
197    }
198
199    # else, get it from db.
200    unless ($data) {
201        my $dbr = LJ::get_dbh("slave", "master");
202        my $query = "SELECT p.state, p.userid, p.contenttype, UNIX_TIMESTAMP(p.picdate) ".
203            "AS 'lastmod', u.clusterid, u.dversion FROM userpic p, user u WHERE ".
204            "p.picid=$picid AND u.userid=p.userid";
205        $pic = $dbr->selectrow_hashref($query);
206        return NOT_FOUND unless $pic;
207
208        $lastmod = $pic->{'lastmod'};
209        if ($pic->{'dversion'} >= 2) {
210            my $dbb = LJ::get_cluster_reader($pic->{'clusterid'});
211            return SERVER_ERROR unless $dbb;
212            $data = $dbb->selectrow_array("SELECT imagedata FROM userpicblob2 WHERE ".
213                                          "userid=$pic->{'userid'} AND picid=$picid");
214        } else {
215            $data = $dbr->selectrow_array("SELECT imagedata FROM userpicblob WHERE ".
216                                          "picid=$picid");
217        }
218    }
219
220    return NOT_FOUND unless $data;
221
222    if ($need_cache && open (F, ">$file")) {
223        print F $data;
224        close F;
225    }
226
227    $set_mime->($data);
228    $size = length($data);
229    $send_headers->();
230    $r->print($data);
231    return OK;
232}
233
234sub content
235{
236    my $r = shift;
237    my $uri = $r->uri;
238    return DECLINED if $uri =~ /dev/;
239   
240    $r->content_type("text/html; charset=utf-8");
241    $r->send_http_header();
242    $r->print("$uri; " . $r->header_in("Host"));
243
244    return OK;
245
246}
247
248sub journal_content
249{
250    my $r = shift;
251    my $uri = $r->uri;
252
253    my $dbs = LJ::get_dbs();
254
255    if ($RQ{'vhost'} eq "users" &&
256        $uri eq "/robots.txt")
257    {
258        my $u = { 'user' => $RQ{'user'} };
259        LJ::load_user_props($dbs, $u, "opt_blockrobots");
260        $r->content_type("text/plain");
261        $r->send_http_header();
262        $r->print("User-Agent: *\n");
263        if ($u->{'opt_blockrobots'}) {
264            $r->print("Disallow: /\n");
265        }
266        return OK;
267    }
268
269    my $cgi = new CGI();
270    my $criterr = 0;
271    my $remote = LJ::get_remote($dbs, \$criterr, $cgi);
272
273    # check for faked cookies here, since this is pretty central.
274    if ($criterr) {
275        $r->content_type("text/html");
276        $r->send_http_header();
277        $r->print("Invalid cookies.  Try <a href='$LJ::SITEROOT/logout.bml'>logging out</a> and then logging back in.\n");
278        return OK;
279    }
280
281    my %headers = ();
282    my $opts = {
283        'headers' => \%headers,
284        'args' => $RQ{'args'},
285        'vhost' => $RQ{'vhost'},
286        'env' => \%ENV,
287    };
288
289    my $user = $RQ{'user'};
290    my $html = LJ::make_journal($dbs, $user, $RQ{'mode'},
291                                $remote, $opts);
292
293    my $status = $opts->{'status'} || "200 OK";
294    unless ($opts->{'contenttype'}) {
295        $opts->{'contenttype'} = "text/html";
296        if ($LJ::UNICODE) {
297            $opts->{'contenttype'} .= "; charset=utf-8";
298        }
299    }
300
301    if ($opts->{'badargs'})
302    {
303        $status = "404 Not Found";
304        $html = "<H1>Not Found</H1>Unknown page or arguments.";
305    }
306    elsif ($opts->{'baduser'})
307    {
308        $status = "404 Unknown User";
309        $html = "<H1>Unknown User</H1>There is no user <b>$user</b> at $LJ::SITENAME.";
310    }
311
312    unless ($html) {
313        $html = "<h1>Error</h1><p>User <b>$user</b> has messed up their journal template definition.</p>";
314    }
315   
316    $r->status_line($status);
317    foreach my $hname (keys %headers) {
318        if (ref($headers{$hname}) && ref($headers{$hname}) eq "ARRAY") {
319            foreach (@{$headers{$hname}}) {
320                $r->header_out($hname, $_);
321            }
322        } else {
323            $r->header_out($hname, $headers{$hname});
324        }
325    }
326
327    if ($opts->{'nocontent'}) {
328        $r->send_http_header();
329        return OK;
330    }
331
332    $r->header_out("Content-type", $opts->{'contenttype'});
333    $r->header_out("Cache-Control", "private, proxy-revalidate");
334    $r->header_out("Vary", "Accept-Encoding, Cookie");
335    $r->header_out("Content-length", length($html));
336    $r->send_http_header();
337    $r->print($html);
338
339    return OK;
340
341}
342
3431;
Note: See TracBrowser for help on using the browser.