| [14] | 1 | #!/usr/bin/perl |
|---|
| 2 | |
|---|
| 3 | use DBI; |
|---|
| 4 | use Digest::MD5 qw(md5_hex); |
|---|
| 5 | |
|---|
| 6 | ######################## |
|---|
| 7 | # CONSTANTS |
|---|
| 8 | # |
|---|
| 9 | |
|---|
| 10 | require '/home/lj/cgi-bin/ljconfig.pl'; |
|---|
| 11 | require '/home/lj/cgi-bin/ljlang.pl'; |
|---|
| 12 | require '/home/lj/cgi-bin/ljpoll.pl'; |
|---|
| 13 | |
|---|
| 14 | @LJ::views = qw(lastn friends calendar day); |
|---|
| 15 | %LJ::viewinfo = ( |
|---|
| 16 | "lastn" => { |
|---|
| 17 | "creator" => \&create_view_lastn, |
|---|
| 18 | "des" => "Most Recent Events", |
|---|
| 19 | }, |
|---|
| 20 | "calendar" => { |
|---|
| 21 | "creator" => \&create_view_calendar, |
|---|
| 22 | "des" => "Calendar", |
|---|
| 23 | }, |
|---|
| 24 | "day" => { |
|---|
| 25 | "creator" => \&create_view_day, |
|---|
| 26 | "des" => "Day View", |
|---|
| 27 | }, |
|---|
| 28 | "friends" => { |
|---|
| 29 | "creator" => \&create_view_friends, |
|---|
| 30 | "des" => "Friends View", |
|---|
| 31 | }, |
|---|
| 32 | ); |
|---|
| 33 | |
|---|
| 34 | ## for use in style system's %%cons:.+%% mapping |
|---|
| 35 | %LJ::constant_map = ('siteroot' => $LJ::SITEROOT, |
|---|
| 36 | 'sitename' => $LJ::SITENAME, |
|---|
| 37 | 'img' => $LJ::IMGPREFIX, |
|---|
| 38 | ); |
|---|
| 39 | |
|---|
| 40 | $SIG{'HUP'} = sub { |
|---|
| 41 | print STDERR "HUP caught. Clearing caches.\n"; |
|---|
| 42 | %LJ::CACHE_STYLE = (); |
|---|
| 43 | %LJ::CACHE_PROPS = (); |
|---|
| 44 | $LJ::CACHED_MOODS = 0; |
|---|
| 45 | $LJ::CACHED_MOOD_MAX = 0; |
|---|
| 46 | %LJ::CACHE_MOODS = (); |
|---|
| 47 | %LJ::CACHE_MOOD_THEME = (); |
|---|
| 48 | %LJ::CACHE_USERID = (); |
|---|
| 49 | %LJ::CACHE_USERNAME = (); |
|---|
| 50 | %LJ::CACHE_USERPIC_SIZE = (); |
|---|
| 51 | %LJ::CACHE_CODES = (); |
|---|
| 52 | %LJ::CACHE_USERPROP = (); # {$prop}->{ 'upropid' => ... , 'indexed' => 0|1 }; |
|---|
| 53 | }; |
|---|
| 54 | |
|---|
| 55 | sub send_mail |
|---|
| 56 | { |
|---|
| 57 | my $opt = shift; |
|---|
| 58 | &LJ::send_mail($opt); |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | ## for stupid AOL mail client, wraps a plain-text URL in an anchor tag since AOL |
|---|
| 62 | ## incorrectly renders regular text as HTML. fucking AOL. die. |
|---|
| 63 | sub make_text_link |
|---|
| 64 | { |
|---|
| 65 | my ($url, $email) = @_; |
|---|
| 66 | if ($email =~ /\@aol.com$/i) { |
|---|
| 67 | return "<A HREF=\"$url\">$url</A>"; |
|---|
| 68 | } |
|---|
| 69 | return $url; |
|---|
| 70 | } |
|---|
| 71 | |
|---|
| 72 | sub is_valid_authaction |
|---|
| 73 | { |
|---|
| 74 | &connect_db(); |
|---|
| 75 | my ($aaid, $auth) = map { $dbh->quote($_) } @_; |
|---|
| 76 | my $sth = $dbh->prepare("SELECT aaid, userid, datecreate, authcode, action, arg1 FROM authactions WHERE aaid=$aaid AND authcode=$auth"); |
|---|
| 77 | $sth->execute; |
|---|
| 78 | return $sth->fetchrow_hashref; |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | ## authenticates the user at the remote end and returns a hashref containing: |
|---|
| 82 | ## user, userid |
|---|
| 83 | ## or returns undef if no logged-in remote or errors. |
|---|
| 84 | ## optional argument is arrayref to push errors |
|---|
| 85 | sub get_remote |
|---|
| 86 | { |
|---|
| 87 | my $errors = shift; |
|---|
| 88 | my $cgi = shift; # optional CGI.pm reference |
|---|
| 89 | |
|---|
| 90 | ### are they logged in? |
|---|
| 91 | my $remuser = $cgi ? $cgi->cookie('ljuser') : $BMLClient::COOKIE{"ljuser"}; |
|---|
| 92 | return undef unless ($remuser); |
|---|
| 93 | |
|---|
| 94 | my $hpass = $cgi ? $cgi->cookie('ljhpass') : $BMLClient::COOKIE{"ljhpass"}; |
|---|
| 95 | |
|---|
| 96 | ### does their login password match their login? |
|---|
| 97 | return undef unless ($hpass =~ /^$remuser:(.+)/); |
|---|
| 98 | my $remhpass = $1; |
|---|
| 99 | |
|---|
| 100 | &connect_db(); |
|---|
| 101 | |
|---|
| 102 | ### do they exist? |
|---|
| 103 | my $userid = &get_userid($remuser); |
|---|
| 104 | $userid += 0; |
|---|
| 105 | return undef unless ($userid); |
|---|
| 106 | |
|---|
| 107 | ### is their password correct? |
|---|
| 108 | my $password; |
|---|
| 109 | my $sth = $dbh->prepare("SELECT password FROM user WHERE userid=$userid"); |
|---|
| 110 | $sth->execute; |
|---|
| 111 | ($password) = $sth->fetchrow_array; |
|---|
| 112 | return undef unless (&valid_password($password, { 'hpassword' => $remhpass })); |
|---|
| 113 | |
|---|
| 114 | return { 'user' => $remuser, |
|---|
| 115 | 'userid' => $userid, }; |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | # this is like get_remote, but it only returns who they say they are, |
|---|
| 119 | # not who they really are. so if they're faking out their cookies, |
|---|
| 120 | # they'll fake this out. but this is fast. |
|---|
| 121 | # |
|---|
| 122 | sub get_remote_noauth |
|---|
| 123 | { |
|---|
| 124 | ### are they logged in? |
|---|
| 125 | my $remuser = $BMLClient::COOKIE{"ljuser"}; |
|---|
| 126 | return undef unless ($remuser); |
|---|
| 127 | |
|---|
| 128 | ### does their login password match their login? |
|---|
| 129 | return undef unless ($BMLClient::COOKIE{"ljhpass"} =~ /^$remuser:(.+)/); |
|---|
| 130 | return { 'user' => $remuser, }; |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | sub remote_has_priv { return &LJ::remote_has_priv($dbh, @_); } |
|---|
| 134 | |
|---|
| 135 | sub register_authaction |
|---|
| 136 | { |
|---|
| 137 | &connect_db(); |
|---|
| 138 | my $userid = shift; $userid += 0; |
|---|
| 139 | my $action = $dbh->quote(shift); |
|---|
| 140 | my $arg1 = $dbh->quote(shift); |
|---|
| 141 | |
|---|
| 142 | # make the authcode |
|---|
| 143 | my $authcode = ""; |
|---|
| 144 | my $vchars = "abcdefghijklmnopqrstuvwxyz0123456789"; |
|---|
| 145 | srand(); |
|---|
| 146 | for (1..15) { |
|---|
| 147 | $authcode .= substr($vchars, int(rand()*36), 1); |
|---|
| 148 | } |
|---|
| 149 | my $qauthcode = $dbh->quote($authcode); |
|---|
| 150 | |
|---|
| 151 | my $sth = $dbh->prepare("INSERT INTO authactions (aaid, userid, datecreate, authcode, action, arg1) VALUES (NULL, $userid, NOW(), $qauthcode, $action, $arg1)"); |
|---|
| 152 | $sth->execute; |
|---|
| 153 | |
|---|
| 154 | if ($dbh->err) { |
|---|
| 155 | return 0; |
|---|
| 156 | } else { |
|---|
| 157 | return { 'aaid' => $dbh->{'mysql_insertid'}, |
|---|
| 158 | 'authcode' => $authcode, |
|---|
| 159 | }; |
|---|
| 160 | } |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | |
|---|
| 164 | sub auth_fields |
|---|
| 165 | { |
|---|
| 166 | my $opts = shift; |
|---|
| 167 | my $remote = &get_remote_noauth(); |
|---|
| 168 | my $ret = ""; |
|---|
| 169 | if (!$FORM{'altlogin'} && !$opts->{'user'} && $remote->{'user'}) { |
|---|
| 170 | my $hpass; |
|---|
| 171 | if ($BMLClient::COOKIE{"ljhpass"} =~ /^$remote->{'user'}:(.+)/) { |
|---|
| 172 | $hpass = $1; |
|---|
| 173 | } |
|---|
| 174 | my $alturl = $ENV{'REQUEST_URI'}; |
|---|
| 175 | $alturl .= ($alturl =~ /\?/) ? "&" : "?"; |
|---|
| 176 | $alturl .= "altlogin=1"; |
|---|
| 177 | |
|---|
| 178 | $ret .= "<TR><TD COLSPAN=2>You are currently logged in as <B>$remote->{'user'}</B>.<BR>If this is not you, <A HREF=\"$alturl\">click here</A>.\n"; |
|---|
| 179 | $ret .= "<INPUT TYPE=HIDDEN NAME=user VALUE=\"$remote->{'user'}\">\n"; |
|---|
| 180 | $ret .= "<INPUT TYPE=HIDDEN NAME=hpassword VALUE=\"$hpass\"><BR> \n"; |
|---|
| 181 | $ret .= "</TD></TR>\n"; |
|---|
| 182 | } else { |
|---|
| 183 | $ret .= "<TR><TD>Username:</TD><TD><INPUT TYPE=TEXT NAME=user SIZE=15 MAXLENGTH=15 VALUE=\""; |
|---|
| 184 | my $user = $opts->{'user'}; |
|---|
| 185 | unless ($user || $ENV{'QUERY_STRING'} =~ /=/) { $user=$ENV{'QUERY_STRING'}; } |
|---|
| 186 | $ret .= &BMLUtil::escapeall($user) unless ($FORM{'altlogin'}); |
|---|
| 187 | $ret .= "\"></TD></TR>\n"; |
|---|
| 188 | $ret .= "<TR><TD>Password:</TD><TD>\n"; |
|---|
| 189 | $ret .= "<INPUT TYPE=password NAME=password SIZE=15 MAXLENGTH=30 VALUE=\"" . &ehtml($opts->{'password'}) . "\">"; |
|---|
| 190 | $ret .= "</TD></TR>\n"; |
|---|
| 191 | } |
|---|
| 192 | return $ret; |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | |
|---|
| 196 | sub valid_password { return &LJ::valid_password(@_); } |
|---|
| 197 | sub hash_password { return md5_hex($_[0]); } |
|---|
| 198 | |
|---|
| 199 | |
|---|
| 200 | sub remap_event_links |
|---|
| 201 | { |
|---|
| 202 | my ($eventref, $baseurl) = @_; |
|---|
| 203 | return unless $baseurl; |
|---|
| 204 | $$eventref =~ s/(<IMG\s+[^>]*SRC=)(("(.+?)")|([^\s>]+))/"$1\"" . &abs_url($2, $baseurl). '"'/ieg; |
|---|
| 205 | $$eventref =~ s/(<A\s+[^>]*HREF=)(("(.+?)")|([^\s>]+))/"$1\"" . &abs_url($2, $baseurl). '"'/ieg; |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | sub abs_url |
|---|
| 209 | { |
|---|
| 210 | use URI::URL; |
|---|
| 211 | my ($uri, $base) = @_; |
|---|
| 212 | $uri =~ s/^"//; |
|---|
| 213 | $uri =~ s/"$//; |
|---|
| 214 | return url($uri)->abs($base)->as_string; |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | sub load_user_props |
|---|
| 218 | { |
|---|
| 219 | &connect_db(); |
|---|
| 220 | |
|---|
| 221 | ## user reference |
|---|
| 222 | my ($uref, @props) = @_; |
|---|
| 223 | my $uid = $uref->{'userid'}+0; |
|---|
| 224 | unless ($uid) { |
|---|
| 225 | $uid = LJ::get_userid($dbh, $uref->{'user'}); |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | my $propname_where; |
|---|
| 229 | if (@props) { |
|---|
| 230 | $propname_where = "AND upl.name IN (" . join(",", map { $dbh->quote($_) } @props) . ")"; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | my ($sql, $sth); |
|---|
| 234 | |
|---|
| 235 | # FIXME: right now we read userprops from both tables (indexed and lite). we always have to do this |
|---|
| 236 | # for cases when we're loading all props, but when loading a subset, we might be able to |
|---|
| 237 | # eliminate one query or the other if we cache somewhere the userproplist and which props |
|---|
| 238 | # are in which table. For now, though, this works: |
|---|
| 239 | |
|---|
| 240 | foreach my $table (qw(userprop userproplite)) |
|---|
| 241 | { |
|---|
| 242 | $sql = "SELECT upl.name, up.value FROM $table up, userproplist upl WHERE up.userid=$uid AND up.upropid=upl.upropid $propname_where"; |
|---|
| 243 | $sth = $dbh->prepare($sql); |
|---|
| 244 | $sth->execute; |
|---|
| 245 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 246 | $uref->{$_->{'name'}} = $_->{'value'}; |
|---|
| 247 | } |
|---|
| 248 | $sth->finish; |
|---|
| 249 | } |
|---|
| 250 | } |
|---|
| 251 | |
|---|
| 252 | sub set_userprop |
|---|
| 253 | { |
|---|
| 254 | my ($dbh, $userid, $propname, $value) = @_; |
|---|
| 255 | my $p; |
|---|
| 256 | |
|---|
| 257 | if ($LJ::CACHE_USERPROP{$propname}) { |
|---|
| 258 | $p = $LJ::CACHE_USERPROP{$propname}; |
|---|
| 259 | } else { |
|---|
| 260 | my $qpropname = $dbh->quote($propname); |
|---|
| 261 | $userid += 0; |
|---|
| 262 | my $propid; |
|---|
| 263 | my $sth; |
|---|
| 264 | |
|---|
| 265 | $sth = $dbh->prepare("SELECT upropid, indexed FROM userproplist WHERE name=$qpropname"); |
|---|
| 266 | $sth->execute; |
|---|
| 267 | $p = $sth->fetchrow_hashref; |
|---|
| 268 | return unless ($p); |
|---|
| 269 | $LJ::CACHE_USERPROP{$propname} = $p; |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | my $table = $p->{'indexed'} ? "userprop" : "userproplite"; |
|---|
| 273 | $value = $dbh->quote($value); |
|---|
| 274 | |
|---|
| 275 | $sth = $dbh->prepare("REPLACE INTO $table (userid, upropid, value) VALUES ($userid, $p->{'upropid'}, $value)"); |
|---|
| 276 | $sth->execute; |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | |
|---|
| 280 | sub load_moods |
|---|
| 281 | { |
|---|
| 282 | return if ($LJ::CACHED_MOODS); |
|---|
| 283 | &connect_db(); |
|---|
| 284 | my $sth = $dbh->prepare("SELECT moodid, mood, parentmood FROM moods"); |
|---|
| 285 | $sth->execute; |
|---|
| 286 | while (my ($id, $mood, $parent) = $sth->fetchrow_array) { |
|---|
| 287 | $LJ::CACHE_MOODS{$id} = { 'name' => $mood, 'parent' => $parent }; |
|---|
| 288 | if ($id > $LJ::CACHED_MOOD_MAX) { $LJ::CACHED_MOOD_MAX = $id; } |
|---|
| 289 | } |
|---|
| 290 | $LJ::CACHED_MOODS = 1; |
|---|
| 291 | } |
|---|
| 292 | |
|---|
| 293 | sub load_mood_theme |
|---|
| 294 | { |
|---|
| 295 | my $themeid = shift; |
|---|
| 296 | return if ($LJ::CACHE_MOOD_THEME{$themeid}); |
|---|
| 297 | |
|---|
| 298 | &connect_db(); |
|---|
| 299 | $themeid += 0; |
|---|
| 300 | my $sth = $dbh->prepare("SELECT moodid, picurl, width, height FROM moodthemedata WHERE moodthemeid=$themeid"); |
|---|
| 301 | $sth->execute; |
|---|
| 302 | while (my ($id, $pic, $w, $h) = $sth->fetchrow_array) { |
|---|
| 303 | $LJ::CACHE_MOOD_THEME{$themeid}->{$id} = { 'pic' => $pic, 'w' => $w, 'h' => $h }; |
|---|
| 304 | } |
|---|
| 305 | } |
|---|
| 306 | |
|---|
| 307 | ## |
|---|
| 308 | ## returns 1 and populates %$retref if successful, else returns 0 |
|---|
| 309 | ## |
|---|
| 310 | sub get_mood_picture |
|---|
| 311 | { |
|---|
| 312 | my ($themeid, $moodid, $ref) = @_; |
|---|
| 313 | do |
|---|
| 314 | { |
|---|
| 315 | if ($LJ::CACHE_MOOD_THEME{$themeid}->{$moodid}) { |
|---|
| 316 | %{$ref} = %{$LJ::CACHE_MOOD_THEME{$themeid}->{$moodid}}; |
|---|
| 317 | if ($ref->{'pic'} =~ m!^/!) { |
|---|
| 318 | $ref->{'pic'} =~ s!^/img!!; |
|---|
| 319 | $ref->{'pic'} = $LJ::IMGPREFIX . $ref->{'pic'}; |
|---|
| 320 | } |
|---|
| 321 | $ref->{'moodid'} = $moodid; |
|---|
| 322 | return 1; |
|---|
| 323 | } else { |
|---|
| 324 | $moodid = $LJ::CACHE_MOODS{$moodid}->{'parent'}; |
|---|
| 325 | } |
|---|
| 326 | } |
|---|
| 327 | while ($moodid); |
|---|
| 328 | return 0; |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | |
|---|
| 332 | sub server_down_html |
|---|
| 333 | { |
|---|
| 334 | return &LJ::server_down_html(); |
|---|
| 335 | } |
|---|
| 336 | |
|---|
| 337 | sub make_journal |
|---|
| 338 | { |
|---|
| 339 | &connect_db(); |
|---|
| 340 | return &LJ::make_journal($dbh, @_); |
|---|
| 341 | } |
|---|
| 342 | |
|---|
| 343 | sub load_codes |
|---|
| 344 | { |
|---|
| 345 | my ($req) = $_[0]; |
|---|
| 346 | &connect_db(); |
|---|
| 347 | foreach my $type (keys %{$req}) |
|---|
| 348 | { |
|---|
| 349 | unless ($LJ::CACHE_CODES{$type}) |
|---|
| 350 | { |
|---|
| 351 | $LJ::CACHE_CODES{$type} = []; |
|---|
| 352 | my $qtype = $dbh->quote($type); |
|---|
| 353 | my $sth = $dbh->prepare("SELECT code, item FROM codes WHERE type=$qtype ORDER BY sortorder"); |
|---|
| 354 | $sth->execute; |
|---|
| 355 | while (my ($code, $item) = $sth->fetchrow_array) |
|---|
| 356 | { |
|---|
| 357 | push @{$LJ::CACHE_CODES{$type}}, [ $code, $item ]; |
|---|
| 358 | } |
|---|
| 359 | } |
|---|
| 360 | |
|---|
| 361 | foreach my $it (@{$LJ::CACHE_CODES{$type}}) |
|---|
| 362 | { |
|---|
| 363 | if (ref $req->{$type} eq "HASH") { |
|---|
| 364 | $req->{$type}->{$it->[0]} = $it->[1]; |
|---|
| 365 | } elsif (ref $req->{$type} eq "ARRAY") { |
|---|
| 366 | push @{$req->{$type}}, { 'code' => $it->[0], 'item' => $it->[1] }; |
|---|
| 367 | } |
|---|
| 368 | } |
|---|
| 369 | } |
|---|
| 370 | } |
|---|
| 371 | |
|---|
| 372 | sub get_userid { return &LJ::get_userid($dbh, @_); } |
|---|
| 373 | sub get_username { return &LJ::get_username($dbh, @_); } |
|---|
| 374 | sub load_userpics { return &LJ::load_userpics($dbh, @_); } |
|---|
| 375 | |
|---|
| 376 | sub ago_text |
|---|
| 377 | { |
|---|
| 378 | my $secondsold = shift; |
|---|
| 379 | return "Never." unless ($secondsold); |
|---|
| 380 | my $num; |
|---|
| 381 | my $unit; |
|---|
| 382 | if ($secondsold > 60*60*24*7) { |
|---|
| 383 | $num = int($secondsold / (60*60*24*7)); |
|---|
| 384 | $unit = "week"; |
|---|
| 385 | } elsif ($secondsold > 60*60*24) { |
|---|
| 386 | $num = int($secondsold / (60*60*24)); |
|---|
| 387 | $unit = "day"; |
|---|
| 388 | } elsif ($secondsold > 60*60) { |
|---|
| 389 | $num = int($secondsold / (60*60)); |
|---|
| 390 | $unit = "hour"; |
|---|
| 391 | } elsif ($secondsold > 60) { |
|---|
| 392 | $num = int($secondsold / (60)); |
|---|
| 393 | $unit = "minute"; |
|---|
| 394 | } else { |
|---|
| 395 | $num = $secondsold; |
|---|
| 396 | $unit = "second"; |
|---|
| 397 | } |
|---|
| 398 | return "$num $unit" . ($num==1?"":"s") . " ago"; |
|---|
| 399 | } |
|---|
| 400 | |
|---|
| 401 | ## get the friends id |
|---|
| 402 | sub get_friend_itemids |
|---|
| 403 | { |
|---|
| 404 | my $opts = shift; |
|---|
| 405 | |
|---|
| 406 | my $userid = $opts->{'userid'}+0; |
|---|
| 407 | my $remoteid = $opts->{'remoteid'}+0; |
|---|
| 408 | my @items = (); |
|---|
| 409 | my $itemshow = $opts->{'itemshow'}+0; |
|---|
| 410 | my $skip = $opts->{'skip'}+0; |
|---|
| 411 | my $getitems = $itemshow+$skip; |
|---|
| 412 | my $owners_ref = (ref $opts->{'owners'} eq "HASH") ? $opts->{'owners'} : {}; |
|---|
| 413 | my $filter = $opts->{'filter'}+0; |
|---|
| 414 | |
|---|
| 415 | ### what do your friends think of remote viewer? what security level? |
|---|
| 416 | my %usermask; |
|---|
| 417 | if ($remoteid) |
|---|
| 418 | { |
|---|
| 419 | $sth = $dbh->prepare("SELECT ff.userid, ff.groupmask FROM friends fu, friends ff WHERE fu.userid=$userid AND fu.friendid=ff.userid AND ff.friendid=$remoteid"); |
|---|
| 420 | $sth->execute; |
|---|
| 421 | while (my ($friendid, $mask) = $sth->fetchrow_array) { |
|---|
| 422 | $usermask{$friendid} = $mask; |
|---|
| 423 | } |
|---|
| 424 | } |
|---|
| 425 | |
|---|
| 426 | my $filtersql; |
|---|
| 427 | if ($filter) { |
|---|
| 428 | if ($remoteid == $userid) { |
|---|
| 429 | $filtersql = "AND f.groupmask & $filter"; |
|---|
| 430 | } |
|---|
| 431 | } |
|---|
| 432 | |
|---|
| 433 | $sth = $dbh->prepare("SELECT u.userid, u.timeupdate FROM friends f, user u WHERE f.userid=$userid AND f.friendid=u.userid $filtersql AND u.statusvis='V'"); |
|---|
| 434 | $sth->execute; |
|---|
| 435 | |
|---|
| 436 | my @friends = (); |
|---|
| 437 | while (my ($userid, $update) = $sth->fetchrow_array) { |
|---|
| 438 | push @friends, [ $userid, $update ]; |
|---|
| 439 | } |
|---|
| 440 | @friends = sort { $b->[1] cmp $a->[1] } @friends; |
|---|
| 441 | |
|---|
| 442 | my $loop = 1; |
|---|
| 443 | my $queries = 0; |
|---|
| 444 | my $oldest = ""; |
|---|
| 445 | while ($loop) |
|---|
| 446 | { |
|---|
| 447 | my @ids = (); |
|---|
| 448 | while (scalar(@ids) < 20 && @friends) { |
|---|
| 449 | my $f = shift @friends; |
|---|
| 450 | if ($oldest && $f->[1] lt $oldest) { last; } |
|---|
| 451 | push @ids, $f->[0]; |
|---|
| 452 | } |
|---|
| 453 | last unless (@ids); |
|---|
| 454 | my $in = join(',', @ids); |
|---|
| 455 | |
|---|
| 456 | my $sql; |
|---|
| 457 | if ($remoteid) { |
|---|
| 458 | $sql = "SELECT l.ownerid, h.itemid, l.logtime, l.security, l.allowmask FROM hintlastnview h, log l WHERE h.userid IN ($in) AND h.itemid=l.itemid"; |
|---|
| 459 | } else { |
|---|
| 460 | $sql = "SELECT l.ownerid, h.itemid, l.logtime FROM hintlastnview h, log l WHERE h.userid IN ($in) AND h.itemid=l.itemid AND l.security='public'"; |
|---|
| 461 | } |
|---|
| 462 | if ($oldest) { $sql .= " AND l.logtime > '$oldest'"; } |
|---|
| 463 | |
|---|
| 464 | # this causes MySQL to do use a temporary table and do an extra pass also (use file sort). so, we'll do it in memory here. yay. |
|---|
| 465 | # $sql .= " ORDER BY l.logtime DESC"; |
|---|
| 466 | |
|---|
| 467 | $sth = $dbh->prepare($sql); |
|---|
| 468 | $sth->execute; |
|---|
| 469 | |
|---|
| 470 | my $rows = $sth->rows; |
|---|
| 471 | if ($rows == 0) { last; } |
|---|
| 472 | |
|---|
| 473 | ## see comment above. this is our "ORDER BY l.logtime DESC". pathetic, huh? |
|---|
| 474 | my @hintrows; |
|---|
| 475 | while (my ($owner, $itemid, $logtime, $sec, $allowmask) = $sth->fetchrow_array) |
|---|
| 476 | { |
|---|
| 477 | push @hintrows, [ $owner, $itemid, $logtime, $sec, $allowmask ]; |
|---|
| 478 | } |
|---|
| 479 | $sth->finish; |
|---|
| 480 | @hintrows = sort { $b->[2] cmp $a->[2] } @hintrows; |
|---|
| 481 | |
|---|
| 482 | my $count; |
|---|
| 483 | while (@hintrows) |
|---|
| 484 | { |
|---|
| 485 | my $rec = shift @hintrows; |
|---|
| 486 | my ($owner, $itemid, $logtime, $sec, $allowmask) = @{$rec}; |
|---|
| 487 | |
|---|
| 488 | if ($sec eq "private" && $owner != $remoteid) { next; } |
|---|
| 489 | if ($sec eq "usemask" && $owner != $remoteid && ! (($usermask{$owner}+0) & ($allowmask+0))) { next; } |
|---|
| 490 | push @items, [ $itemid, $logtime, $owner ]; |
|---|
| 491 | $count++; |
|---|
| 492 | if ($count >= $getitems) { last; } |
|---|
| 493 | } |
|---|
| 494 | @items = sort { $b->[1] cmp $a->[1] } @items; |
|---|
| 495 | my $size = scalar(@items); |
|---|
| 496 | if ($size < $getitems) { next; } |
|---|
| 497 | @items = @items[0..($getitems-1)]; |
|---|
| 498 | $oldest = $items[$getitems-1]->[1]; |
|---|
| 499 | } |
|---|
| 500 | |
|---|
| 501 | my $size = scalar(@items); |
|---|
| 502 | |
|---|
| 503 | my @ret; |
|---|
| 504 | my $max = $skip+$itemshow; |
|---|
| 505 | if ($size < $max) { $max = $size; } |
|---|
| 506 | foreach my $it (@items[$skip..($max-1)]) { |
|---|
| 507 | push @ret, $it->[0]; |
|---|
| 508 | $owners_ref->{$it->[2]} = 1; |
|---|
| 509 | } |
|---|
| 510 | return @ret; |
|---|
| 511 | } |
|---|
| 512 | |
|---|
| 513 | |
|---|
| 514 | # do all the current music/mood/weather/whatever stuff |
|---|
| 515 | sub prepare_currents |
|---|
| 516 | { |
|---|
| 517 | my $args = shift; |
|---|
| 518 | |
|---|
| 519 | my %currents = (); |
|---|
| 520 | my $val; |
|---|
| 521 | if ($val = $args->{'props'}->{$args->{'itemid'}}->{'current_music'}) { |
|---|
| 522 | $currents{'Music'} = $val; |
|---|
| 523 | } |
|---|
| 524 | if ($val = $args->{'props'}->{$args->{'itemid'}}->{'current_mood'}) { |
|---|
| 525 | $currents{'Mood'} = $val; |
|---|
| 526 | } |
|---|
| 527 | if ($val = $args->{'props'}->{$args->{'itemid'}}->{'current_moodid'}) { |
|---|
| 528 | my $theme = $args->{'user'}->{'moodthemeid'}; |
|---|
| 529 | &load_mood_theme($theme); |
|---|
| 530 | my %pic; |
|---|
| 531 | if (&get_mood_picture($theme, $val, \%pic)) { |
|---|
| 532 | $currents{'Mood'} = "<IMG SRC=\"$pic{'pic'}\" ALIGN=ABSMIDDLE WIDTH=$pic{'w'} HEIGHT=$pic{'h'} VSPACE=1> $LJ::CACHE_MOODS{$val}->{'name'}"; |
|---|
| 533 | } else { |
|---|
| 534 | $currents{'Mood'} = $LJ::CACHE_MOODS{$val}->{'name'}; |
|---|
| 535 | } |
|---|
| 536 | } |
|---|
| 537 | if (%currents) { |
|---|
| 538 | if ($args->{'vars'}->{$args->{'prefix'}.'_CURRENTS'}) |
|---|
| 539 | { |
|---|
| 540 | ### PREFIX_CURRENTS is defined, so use the correct style vars |
|---|
| 541 | |
|---|
| 542 | my $fvp = { 'currents' => "" }; |
|---|
| 543 | foreach (sort keys %currents) { |
|---|
| 544 | $fvp->{'currents'} .= &fill_var_props($args->{'vars'}, $args->{'prefix'}.'_CURRENT', { |
|---|
| 545 | 'what' => $_, |
|---|
| 546 | 'value' => $currents{$_}, |
|---|
| 547 | }); |
|---|
| 548 | } |
|---|
| 549 | $args->{'event'}->{'currents'} = |
|---|
| 550 | &fill_var_props($args->{'vars'}, $args->{'prefix'}.'_CURRENTS', $fvp); |
|---|
| 551 | } else |
|---|
| 552 | { |
|---|
| 553 | ### PREFIX_CURRENTS is not defined, so just add to %%events%% |
|---|
| 554 | $args->{'event'}->{'event'} .= "<BR> "; |
|---|
| 555 | foreach (sort keys %currents) { |
|---|
| 556 | $args->{'event'}->{'event'} .= "<BR><B>Current $_</B>: " . $currents{$_} . "\n"; |
|---|
| 557 | } |
|---|
| 558 | } |
|---|
| 559 | } |
|---|
| 560 | } |
|---|
| 561 | |
|---|
| 562 | |
|---|
| 563 | |
|---|
| 564 | sub fill_var_props |
|---|
| 565 | { |
|---|
| 566 | my ($vars, $key, $hashref) = @_; |
|---|
| 567 | my $data = $vars->{$key}; |
|---|
| 568 | $data =~ s/%%(?:([\w:]+:))?(\S+?)%%/$1 ? &fvp_transform(lc($1), $vars, $hashref, $2) : $hashref->{$2}/eg; |
|---|
| 569 | return $data; |
|---|
| 570 | } |
|---|
| 571 | |
|---|
| 572 | sub fvp_transform |
|---|
| 573 | { |
|---|
| 574 | my ($transform, $vars, $hashref, $attr) = @_; |
|---|
| 575 | my $ret = $hashref->{$attr}; |
|---|
| 576 | while ($transform =~ s/(\w+):$//) { |
|---|
| 577 | my $trans = $1; |
|---|
| 578 | if ($trans eq "ue") { |
|---|
| 579 | $ret = &eurl($ret); |
|---|
| 580 | } |
|---|
| 581 | elsif ($trans eq "xe") { |
|---|
| 582 | $ret = &exml($ret); |
|---|
| 583 | } |
|---|
| 584 | elsif ($trans eq "lc") { |
|---|
| 585 | $ret = lc($ret); |
|---|
| 586 | } |
|---|
| 587 | elsif ($trans eq "uc") { |
|---|
| 588 | $ret = uc($ret); |
|---|
| 589 | } |
|---|
| 590 | elsif ($trans eq "color") { |
|---|
| 591 | $ret = $vars->{"color-$attr"}; |
|---|
| 592 | } |
|---|
| 593 | elsif ($trans eq "cons") { |
|---|
| 594 | $ret = $LJ::constant_map{$attr}; |
|---|
| 595 | } |
|---|
| 596 | elsif ($trans eq "ad") { |
|---|
| 597 | $ret = "<LJAD $attr>"; |
|---|
| 598 | } |
|---|
| 599 | } |
|---|
| 600 | return $ret; |
|---|
| 601 | } |
|---|
| 602 | |
|---|
| 603 | sub eurl |
|---|
| 604 | { |
|---|
| 605 | my $a = $_[0]; |
|---|
| 606 | $a =~ s/([^a-zA-Z0-9_\,\-.\/\\\: ])/uc sprintf("%%%02x",ord($1))/eg; |
|---|
| 607 | $a =~ tr/ /+/; |
|---|
| 608 | return $a; |
|---|
| 609 | } |
|---|
| 610 | |
|---|
| 611 | ### escape stuff so it can be used in XML attributes or elements |
|---|
| 612 | sub exml |
|---|
| 613 | { |
|---|
| 614 | my $a = shift; |
|---|
| 615 | $a =~ s/\&/&/g; |
|---|
| 616 | $a =~ s/\"/"/g; |
|---|
| 617 | $a =~ s/\'/'/g; |
|---|
| 618 | $a =~ s/</</g; |
|---|
| 619 | $a =~ s/>/>/g; |
|---|
| 620 | return $a; |
|---|
| 621 | } |
|---|
| 622 | |
|---|
| 623 | sub ehtml |
|---|
| 624 | { |
|---|
| 625 | my $a = $_[0]; |
|---|
| 626 | $a =~ s/\&/&/g; |
|---|
| 627 | $a =~ s/\"/"/g; |
|---|
| 628 | $a =~ s/</</g; |
|---|
| 629 | $a =~ s/>/>/g; |
|---|
| 630 | return $a; |
|---|
| 631 | } |
|---|
| 632 | |
|---|
| 633 | # pass this a hashref, and it'll populate it. |
|---|
| 634 | sub get_form_data |
|---|
| 635 | { |
|---|
| 636 | my ($hashref) = shift; |
|---|
| 637 | my $buffer = shift; |
|---|
| 638 | |
|---|
| 639 | if ($ENV{'REQUEST_METHOD'} eq 'POST') { |
|---|
| 640 | read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); |
|---|
| 641 | } else { |
|---|
| 642 | $buffer = $ENV{'QUERY_STRING'} || $ENV{'REDIRECT_QUERY_STRING'}; |
|---|
| 643 | if ($buffer eq "" && $ENV{'REQUEST_URI'} =~ /\?(.+)/) { |
|---|
| 644 | $buffer = $1; |
|---|
| 645 | } |
|---|
| 646 | } |
|---|
| 647 | |
|---|
| 648 | # Split the name-value pairs |
|---|
| 649 | my $pair; |
|---|
| 650 | my @pairs = split(/&/, $buffer); |
|---|
| 651 | my ($name, $value); |
|---|
| 652 | foreach $pair (@pairs) |
|---|
| 653 | { |
|---|
| 654 | ($name, $value) = split(/=/, $pair); |
|---|
| 655 | $value =~ tr/+/ /; |
|---|
| 656 | $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; |
|---|
| 657 | $name =~ tr/+/ /; |
|---|
| 658 | $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; |
|---|
| 659 | $hashref->{$name} .= $hashref->{$name} ? "\0$value" : $value; |
|---|
| 660 | } |
|---|
| 661 | } |
|---|
| 662 | |
|---|
| 663 | ### WTF is this? |
|---|
| 664 | sub modify_time |
|---|
| 665 | { |
|---|
| 666 | my $id = $_[0]; |
|---|
| 667 | return if ($id =~ /[^a-z0-9\-\_]/); |
|---|
| 668 | return (stat("$DATADIR/bin/$id.mod"))[9]; |
|---|
| 669 | } |
|---|
| 670 | |
|---|
| 671 | sub bullet_errors |
|---|
| 672 | { |
|---|
| 673 | my ($errorref) = @_; |
|---|
| 674 | my $ret = "(=BADCONTENT=)\n<UL>\n"; |
|---|
| 675 | foreach (@{$errorref}) |
|---|
| 676 | { |
|---|
| 677 | $ret .= "<LI>$_\n"; |
|---|
| 678 | } |
|---|
| 679 | $ret .= "</UL>\n"; |
|---|
| 680 | return $ret; |
|---|
| 681 | } |
|---|
| 682 | |
|---|
| 683 | sub icq_send |
|---|
| 684 | { |
|---|
| 685 | my ($uin, $msg) = @_; |
|---|
| 686 | if (length($msg) > 450) { $msg = substr($msg, 0, 447) . "..."; } |
|---|
| 687 | return unless ($uin eq "489151" || $uin eq "19639663"); |
|---|
| 688 | my $time = time(); |
|---|
| 689 | my $rand = "0000"; |
|---|
| 690 | my $file; |
|---|
| 691 | $file = "$ICQSPOOL/$time.$rand"; |
|---|
| 692 | while (-e $file) { |
|---|
| 693 | $rand = sprintf("%04d", int(rand()*10000)); |
|---|
| 694 | $file = "$ICQSPOOL/$time.$rand"; |
|---|
| 695 | } |
|---|
| 696 | open (FIL, ">$file"); |
|---|
| 697 | print FIL "send $uin $msg"; |
|---|
| 698 | close FIL; |
|---|
| 699 | } |
|---|
| 700 | |
|---|
| 701 | sub create_password |
|---|
| 702 | { |
|---|
| 703 | my @c = split(/ */, "bcdfghjklmnprstvwxyz"); |
|---|
| 704 | my @v = split(/ */, "aeiou"); |
|---|
| 705 | my $l = int(rand(2)) + 4; |
|---|
| 706 | my $password = ""; |
|---|
| 707 | for(my $i = 1; $i <= $l; $i++) |
|---|
| 708 | { |
|---|
| 709 | $password .= "$c[int(rand(20))]$v[int(rand(5))]"; |
|---|
| 710 | } |
|---|
| 711 | return $password; |
|---|
| 712 | } |
|---|
| 713 | |
|---|
| 714 | sub age |
|---|
| 715 | { |
|---|
| 716 | my ($age) = $_[0]; # seconds |
|---|
| 717 | my $sec = $age; |
|---|
| 718 | my $unit; |
|---|
| 719 | if ($age < 60) |
|---|
| 720 | { |
|---|
| 721 | $unit="sec"; |
|---|
| 722 | } |
|---|
| 723 | elsif ($age < 3600) |
|---|
| 724 | { |
|---|
| 725 | $age = int($age/60); |
|---|
| 726 | $unit=" min"; |
|---|
| 727 | } |
|---|
| 728 | elsif ($age < 3600*24) |
|---|
| 729 | { |
|---|
| 730 | $age = (int($age/3600)); |
|---|
| 731 | $unit="hr"; |
|---|
| 732 | } |
|---|
| 733 | else |
|---|
| 734 | { |
|---|
| 735 | $age = (int($age/(3600*24))); |
|---|
| 736 | $unit = "day"; |
|---|
| 737 | } |
|---|
| 738 | if ($age != 1) |
|---|
| 739 | { |
|---|
| 740 | $unit .= "s"; |
|---|
| 741 | } |
|---|
| 742 | return "$age $unit"; |
|---|
| 743 | } |
|---|
| 744 | |
|---|
| 745 | # XXX DEPRECATED |
|---|
| 746 | sub strip_bad_code |
|---|
| 747 | { |
|---|
| 748 | return &LJ::strip_bad_code(@_); |
|---|
| 749 | } |
|---|
| 750 | |
|---|
| 751 | sub self_link |
|---|
| 752 | { |
|---|
| 753 | my $newvars = shift; |
|---|
| 754 | my $link = $ENV{'REQUEST_URI'}; |
|---|
| 755 | $link =~ s/\?.+//; |
|---|
| 756 | $link .= "?"; |
|---|
| 757 | foreach (keys %$newvars) { |
|---|
| 758 | if (! exists $FORM{$_}) { $FORM{$_} = ""; } |
|---|
| 759 | } |
|---|
| 760 | foreach (sort keys %FORM) { |
|---|
| 761 | if (defined $newvars->{$_} && ! $newvars->{$_}) { next; } |
|---|
| 762 | my $val = $newvars->{$_} || $FORM{$_}; |
|---|
| 763 | next unless $val; |
|---|
| 764 | $link .= &BMLUtil::eurl($_) . "=" . &BMLUtil::eurl($val) . "&"; |
|---|
| 765 | } |
|---|
| 766 | chop $link; |
|---|
| 767 | return $link; |
|---|
| 768 | } |
|---|
| 769 | |
|---|
| 770 | sub make_link |
|---|
| 771 | { |
|---|
| 772 | my $url = shift; |
|---|
| 773 | my $vars = shift; |
|---|
| 774 | my $append = "?"; |
|---|
| 775 | foreach (keys %$vars) { |
|---|
| 776 | next if ($vars->{$_} eq ""); |
|---|
| 777 | $url .= "${append}${_}=$vars->{$_}"; |
|---|
| 778 | $append = "&"; |
|---|
| 779 | } |
|---|
| 780 | return $url; |
|---|
| 781 | } |
|---|
| 782 | |
|---|
| 783 | |
|---|
| 784 | #### UTILITY |
|---|
| 785 | |
|---|
| 786 | sub trim |
|---|
| 787 | { |
|---|
| 788 | my $a = $_[0]; |
|---|
| 789 | $a =~ s/^\s+//; |
|---|
| 790 | $a =~ s/\s+$//; |
|---|
| 791 | return $a; |
|---|
| 792 | } |
|---|
| 793 | |
|---|
| 794 | sub durl |
|---|
| 795 | { |
|---|
| 796 | my ($a) = @_; |
|---|
| 797 | $a =~ tr/+/ /; |
|---|
| 798 | $a =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; |
|---|
| 799 | return $a; |
|---|
| 800 | } |
|---|
| 801 | |
|---|
| 802 | sub can_use_journal { |
|---|
| 803 | &connect_db(); |
|---|
| 804 | return &LJ::can_use_journal($dbh, @_); |
|---|
| 805 | } |
|---|
| 806 | sub get_recent_itemids { |
|---|
| 807 | &connect_db(); |
|---|
| 808 | return &LJ::get_recent_itemids($dbh, @_); |
|---|
| 809 | } |
|---|
| 810 | sub load_log_props { |
|---|
| 811 | &connect_db(); |
|---|
| 812 | return &LJ::load_log_props($dbh, @_); |
|---|
| 813 | } |
|---|
| 814 | sub days_in_month { |
|---|
| 815 | return &LJ::days_in_month(@_); |
|---|
| 816 | } |
|---|
| 817 | |
|---|
| 818 | sub html_select |
|---|
| 819 | { |
|---|
| 820 | return LJ::html_select(@_); |
|---|
| 821 | } |
|---|
| 822 | |
|---|
| 823 | sub html_datetime_decode |
|---|
| 824 | { |
|---|
| 825 | my $opts = shift; |
|---|
| 826 | my $hash = shift; |
|---|
| 827 | my $name = $opts->{'name'}; |
|---|
| 828 | return sprintf("%04d-%02d-%02d %02d:%02d:%02d", |
|---|
| 829 | $hash->{"${name}_yyyy"}, |
|---|
| 830 | $hash->{"${name}_mm"}, |
|---|
| 831 | $hash->{"${name}_dd"}, |
|---|
| 832 | $hash->{"${name}_hh"}, |
|---|
| 833 | $hash->{"${name}_nn"}, |
|---|
| 834 | $hash->{"${name}_ss"}); |
|---|
| 835 | } |
|---|
| 836 | |
|---|
| 837 | sub html_datetime |
|---|
| 838 | { |
|---|
| 839 | my $opts = shift; |
|---|
| 840 | my $lang = $opts->{'lang'} || "EN"; |
|---|
| 841 | my ($yyyy, $mm, $dd, $hh, $nn, $ss); |
|---|
| 842 | my $ret; |
|---|
| 843 | my $name = $opts->{'name'}; |
|---|
| 844 | my $disabled = $opts->{'disabled'} ? "DISABLED" : ""; |
|---|
| 845 | if ($opts->{'default'} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d):(\d\d))/) { |
|---|
| 846 | ($yyyy, $mm, $dd, $hh, $nn, $ss) = ($1 > 0 ? $1 : "", |
|---|
| 847 | $2+0, |
|---|
| 848 | $3 > 0 ? $3+0 : "", |
|---|
| 849 | $4 > 0 ? $4 : "", |
|---|
| 850 | $5 > 0 ? $5 : "", |
|---|
| 851 | $6 > 0 ? $6 : ""); |
|---|
| 852 | } |
|---|
| 853 | $ret .= &html_select({ 'name' => "${name}_mm", 'selected' => $mm, 'disabled' => $opts->{'disabled'} }, |
|---|
| 854 | map { $_, &LJ::Lang::month_long($lang, $_) } (0..12)); |
|---|
| 855 | $ret .= "<INPUT SIZE=2 MAXLENGTH=2 NAME=${name}_dd VALUE=\"$dd\" $disabled>, <INPUT SIZE=4 MAXLENGTH=4 NAME=${name}_yyyy VALUE=\"$yyyy\" $disabled>"; |
|---|
| 856 | unless ($opts->{'notime'}) { |
|---|
| 857 | $ret.= " <INPUT SIZE=2 MAXLENGTH=2 NAME=${name}_hh VALUE=\"$hh\" $disabled>:<INPUT SIZE=2 MAXLENGTH=2 NAME=${name}_nn VALUE=\"$nn\" $disabled>"; |
|---|
| 858 | if ($opts->{'seconds'}) { |
|---|
| 859 | $ret .= "<INPUT SIZE=2 MAXLENGTH=2 NAME=${name}_ss VALUE=\"$ss\" $disabled>"; |
|---|
| 860 | } |
|---|
| 861 | } |
|---|
| 862 | |
|---|
| 863 | return $ret; |
|---|
| 864 | } |
|---|
| 865 | |
|---|
| 866 | sub get_query_string |
|---|
| 867 | { |
|---|
| 868 | my $q = $ENV{'QUERY_STRING'} || $ENV{'REDIRECT_QUERY_STRING'}; |
|---|
| 869 | if ($q eq "" && $ENV{'REQUEST_URI'} =~ /\?(.+)/) { |
|---|
| 870 | $q = $1; |
|---|
| 871 | } |
|---|
| 872 | return $q; |
|---|
| 873 | } |
|---|
| 874 | |
|---|
| 875 | # this is here only for upwards compatability. the good function to use is |
|---|
| 876 | # LJ::get_dbh, which this function now calls. |
|---|
| 877 | sub connect_db |
|---|
| 878 | { |
|---|
| 879 | $dbh = ($BMLPersist::dbh = LJ::get_dbh("master")); |
|---|
| 880 | } |
|---|
| 881 | |
|---|
| 882 | sub parse_vars |
|---|
| 883 | { |
|---|
| 884 | return &LJ::parse_vars(@_); |
|---|
| 885 | } |
|---|
| 886 | |
|---|
| 887 | sub load_user_theme |
|---|
| 888 | { |
|---|
| 889 | &connect_db(); |
|---|
| 890 | return &LJ::load_user_theme(@_); |
|---|
| 891 | } |
|---|
| 892 | |
|---|
| 893 | package LJ; |
|---|
| 894 | |
|---|
| 895 | ### hashref, arrayref |
|---|
| 896 | sub load_userpics |
|---|
| 897 | { |
|---|
| 898 | my ($dbh, $upics, $idlist) = @_; |
|---|
| 899 | my @load_list; |
|---|
| 900 | foreach my $id (@{$idlist}) |
|---|
| 901 | { |
|---|
| 902 | if ($LJ::CACHE_USERPIC_SIZE{$id}) { |
|---|
| 903 | $upics->{$id}->{'width'} = $LJ::CACHE_USERPIC_SIZE{$id}->{'width'}; |
|---|
| 904 | $upics->{$id}->{'height'} = $LJ::CACHE_USERPIC_SIZE{$id}->{'height'}; |
|---|
| 905 | } elsif ($id+0) { |
|---|
| 906 | push @load_list, ($id+0); |
|---|
| 907 | } |
|---|
| 908 | } |
|---|
| 909 | return unless (@load_list); |
|---|
| 910 | my $picid_in = join(",", @load_list); |
|---|
| 911 | my $sth = $dbh->prepare("SELECT picid, width, height FROM userpic WHERE picid IN ($picid_in)"); |
|---|
| 912 | $sth->execute; |
|---|
| 913 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 914 | my $id = $_->{'picid'}; |
|---|
| 915 | undef $_->{'picid'}; |
|---|
| 916 | $upics->{$id} = $_; |
|---|
| 917 | $LJ::CACHE_USERPIC_SIZE{$id}->{'width'} = $_->{'width'}; |
|---|
| 918 | $LJ::CACHE_USERPIC_SIZE{$id}->{'height'} = $_->{'height'}; |
|---|
| 919 | } |
|---|
| 920 | } |
|---|
| 921 | |
|---|
| 922 | sub send_mail |
|---|
| 923 | { |
|---|
| 924 | my $opt = shift; |
|---|
| 925 | open (MAIL, "|$LJ::SENDMAIL"); |
|---|
| 926 | my $toname; |
|---|
| 927 | if ($opt->{'toname'}) { |
|---|
| 928 | $toname = " ($opt->{'toname'})"; |
|---|
| 929 | } |
|---|
| 930 | print MAIL "To: $opt->{'to'}$toname\n"; |
|---|
| 931 | print MAIL "Cc: $opt->{'bcc'}\n" if ($opt->{'cc'}); |
|---|
| 932 | print MAIL "Bcc: $opt->{'bcc'}\n" if ($opt->{'bcc'}); |
|---|
| 933 | print MAIL "From: $opt->{'from'}"; |
|---|
| 934 | if ($opt->{'fromname'}) { |
|---|
| 935 | print MAIL " ($opt->{'fromname'})"; |
|---|
| 936 | } |
|---|
| 937 | print MAIL "\nSubject: $opt->{'subject'}\n\n"; |
|---|
| 938 | print MAIL $opt->{'body'}; |
|---|
| 939 | close MAIL; |
|---|
| 940 | } |
|---|
| 941 | |
|---|
| 942 | sub strip_bad_code |
|---|
| 943 | { |
|---|
| 944 | my $data = shift; |
|---|
| 945 | my $newdata; |
|---|
| 946 | use HTML::TokeParser; |
|---|
| 947 | $p = HTML::TokeParser->new($data); |
|---|
| 948 | |
|---|
| 949 | while (my $token = $p->get_token) |
|---|
| 950 | { |
|---|
| 951 | my $type = $token->[0]; |
|---|
| 952 | if ($type eq "S") { |
|---|
| 953 | if ($token->[1] eq "script") { |
|---|
| 954 | $p->unget_token($token); |
|---|
| 955 | $p->get_tag("/script"); |
|---|
| 956 | } else { |
|---|
| 957 | my $tag = $token->[1]; |
|---|
| 958 | my $hash = $token->[2]; |
|---|
| 959 | delete $hash->{'onabort'}; |
|---|
| 960 | delete $hash->{'onblur'}; |
|---|
| 961 | delete $hash->{'onchange'}; |
|---|
| 962 | delete $hash->{'onclick'}; |
|---|
| 963 | delete $hash->{'onerror'}; |
|---|
| 964 | delete $hash->{'onfocus'}; |
|---|
| 965 | delete $hash->{'onload'}; |
|---|
| 966 | delete $hash->{'onmouseout'}; |
|---|
| 967 | delete $hash->{'onmouseover'}; |
|---|
| 968 | delete $hash->{'onreset'}; |
|---|
| 969 | delete $hash->{'onselect'}; |
|---|
| 970 | delete $hash->{'onsubmit'}; |
|---|
| 971 | delete $hash->{'onunload'}; |
|---|
| 972 | if ($tag eq "a") { |
|---|
| 973 | if ($hash->{'href'} =~ /^\s*javascript:/) { $hash->{'href'} = "about:"; } |
|---|
| 974 | } elsif ($tag eq "meta") { |
|---|
| 975 | if ($hash->{'content'} =~ /javascript:/) { delete $hash->{'content'}; } |
|---|
| 976 | } elsif ($tag eq "img") { |
|---|
| 977 | if ($hash->{'src'} =~ /javascript:/) { delete $hash->{'src'}; } |
|---|
| 978 | if ($hash->{'dynsrc'} =~ /javascript:/) { delete $hash->{'dynsrc'}; } |
|---|
| 979 | if ($hash->{'lowsrc'} =~ /javascript:/) { delete $hash->{'lowsrc'}; } |
|---|
| 980 | } |
|---|
| 981 | $newdata .= "<" . $tag; |
|---|
| 982 | foreach (keys %$hash) { |
|---|
| 983 | $newdata .= " $_=\"$hash->{$_}\""; |
|---|
| 984 | } |
|---|
| 985 | $newdata .= ">"; |
|---|
| 986 | } |
|---|
| 987 | } |
|---|
| 988 | elsif ($type eq "E") { |
|---|
| 989 | $newdata .= "</" . $token->[1] . ">"; |
|---|
| 990 | } |
|---|
| 991 | elsif ($type eq "T" || $type eq "D") { |
|---|
| 992 | $newdata .= $token->[1]; |
|---|
| 993 | } |
|---|
| 994 | elsif ($type eq "C") { |
|---|
| 995 | # ignore comments |
|---|
| 996 | } |
|---|
| 997 | elsif ($type eq "PI") { |
|---|
| 998 | $newdata .= "<?$token->[1]>"; |
|---|
| 999 | } |
|---|
| 1000 | else { |
|---|
| 1001 | $newdata .= "<!-- OTHER: " . $type . "-->\n"; |
|---|
| 1002 | } |
|---|
| 1003 | } # end while |
|---|
| 1004 | $$data = $newdata; |
|---|
| 1005 | } |
|---|
| 1006 | |
|---|
| 1007 | #sub strip_bad_code |
|---|
| 1008 | #{ |
|---|
| 1009 | # my $data = shift; |
|---|
| 1010 | # require '/home/lj/cgi-bin/cleanhtml.pl'; |
|---|
| 1011 | # &LJ::CleanHTML::clean($data, { |
|---|
| 1012 | # 'mode' => 'allow', |
|---|
| 1013 | # 'keepcomments' => 1, |
|---|
| 1014 | # }); |
|---|
| 1015 | #} |
|---|
| 1016 | |
|---|
| 1017 | %acct_name = ("paid" => "Paid Account", |
|---|
| 1018 | "off" => "Free Account", |
|---|
| 1019 | "early" => "Early Adopter", |
|---|
| 1020 | "on" => "Permanent Account"); |
|---|
| 1021 | |
|---|
| 1022 | sub load_user_theme |
|---|
| 1023 | { |
|---|
| 1024 | # hashref, hashref |
|---|
| 1025 | my ($dbh, $user, $u, $vars) = @_; |
|---|
| 1026 | my $sth; |
|---|
| 1027 | my $quser = $dbh->quote($user); |
|---|
| 1028 | |
|---|
| 1029 | if ($u->{'_contesttheme'}) { |
|---|
| 1030 | my $qnum = $dbh->quote($u->{'_contesttheme'}); |
|---|
| 1031 | $sth = $dbh->prepare("SELECT name AS 'coltype', value AS 'color' FROM contest1data WHERE contestid=$qnum"); |
|---|
| 1032 | } elsif ($u->{'themeid'} == 0) { |
|---|
| 1033 | $sth = $dbh->prepare("SELECT coltype, color FROM themecustom WHERE user=$quser"); |
|---|
| 1034 | } else { |
|---|
| 1035 | my $qtid = $dbh->quote($u->{'themeid'}); |
|---|
| 1036 | $sth = $dbh->prepare("SELECT coltype, color FROM themedata WHERE themeid=$qtid"); |
|---|
| 1037 | } |
|---|
| 1038 | $sth->execute; |
|---|
| 1039 | $vars->{"color-$_->{'coltype'}"} = $_->{'color'} while ($_ = $sth->fetchrow_hashref); |
|---|
| 1040 | } |
|---|
| 1041 | |
|---|
| 1042 | sub parse_vars |
|---|
| 1043 | { |
|---|
| 1044 | my ($dataref, $hashref) = @_; |
|---|
| 1045 | my @data = split(/\n/, $$dataref); |
|---|
| 1046 | my $curitem = ""; |
|---|
| 1047 | |
|---|
| 1048 | foreach (@data) |
|---|
| 1049 | { |
|---|
| 1050 | $_ .= "\n"; |
|---|
| 1051 | s/\r//g; |
|---|
| 1052 | if ($curitem eq "" && /^([A-Z0-9\_]+)=>([^\n\r]*)/) |
|---|
| 1053 | { |
|---|
| 1054 | $hashref->{$1} = $2; |
|---|
| 1055 | } |
|---|
| 1056 | elsif ($curitem eq "" && /^([A-Z0-9\_]+)<=\s*$/) |
|---|
| 1057 | { |
|---|
| 1058 | $curitem = $1; |
|---|
| 1059 | $hashref->{$curitem} = ""; |
|---|
| 1060 | } |
|---|
| 1061 | elsif ($curitem && /^<=$curitem\s*$/) |
|---|
| 1062 | { |
|---|
| 1063 | chop $hashref->{$curitem}; # remove the false newline |
|---|
| 1064 | $curitem = ""; |
|---|
| 1065 | } |
|---|
| 1066 | else |
|---|
| 1067 | { |
|---|
| 1068 | $hashref->{$curitem} .= $_ if ($curitem =~ /\S/); |
|---|
| 1069 | } |
|---|
| 1070 | } |
|---|
| 1071 | } |
|---|
| 1072 | |
|---|
| 1073 | sub server_down_html |
|---|
| 1074 | { |
|---|
| 1075 | return "<B>$LJ::SERVER_DOWN_SUBJECT</B><BR>$LJ::SERVER_DOWN_MESSAGE"; |
|---|
| 1076 | } |
|---|
| 1077 | |
|---|
| 1078 | ## |
|---|
| 1079 | ## loads a style and takes into account caching (don't reload a system style |
|---|
| 1080 | ## until 60 seconds) |
|---|
| 1081 | ## |
|---|
| 1082 | sub load_style_fast |
|---|
| 1083 | { |
|---|
| 1084 | ### styleid -- numeric, primary key |
|---|
| 1085 | ### dataref -- pointer where to store data |
|---|
| 1086 | ### typeref -- optional pointer where to store style type (undef for none) |
|---|
| 1087 | ### nocache -- flag to say don't cache |
|---|
| 1088 | |
|---|
| 1089 | my ($dbh, $styleid, $dataref, $typeref, $nocache) = @_; |
|---|
| 1090 | $styleid += 0; |
|---|
| 1091 | my $now = time(); |
|---|
| 1092 | |
|---|
| 1093 | if ((defined $LJ::CACHE_STYLE{$styleid}) && |
|---|
| 1094 | ($LJ::CACHE_STYLE{$styleid}->{'lastpull'} > ($now-300)) && |
|---|
| 1095 | (! $nocache) |
|---|
| 1096 | ) |
|---|
| 1097 | { |
|---|
| 1098 | $$dataref = $LJ::CACHE_STYLE{$styleid}->{'data'}; |
|---|
| 1099 | if (ref $typeref eq "SCALAR") { $$typeref = $LJ::CACHE_STYLE{$styleid}->{'type'}; } |
|---|
| 1100 | } |
|---|
| 1101 | else |
|---|
| 1102 | { |
|---|
| 1103 | $sth = $dbh->prepare("SELECT formatdata, type, opt_cache FROM style WHERE styleid=$styleid"); |
|---|
| 1104 | $sth->execute; |
|---|
| 1105 | my ($data, $type, $cache) = $sth->fetchrow_array; |
|---|
| 1106 | if ($cache eq "Y") { |
|---|
| 1107 | $LJ::CACHE_STYLE{$styleid} = { 'lastpull' => $now, |
|---|
| 1108 | 'data' => $data, |
|---|
| 1109 | 'type' => $type, |
|---|
| 1110 | }; |
|---|
| 1111 | } |
|---|
| 1112 | $$dataref = $data; |
|---|
| 1113 | if (ref $typeref eq "SCALAR") { $$typeref = $type; } |
|---|
| 1114 | } |
|---|
| 1115 | } |
|---|
| 1116 | |
|---|
| 1117 | sub make_journal |
|---|
| 1118 | { |
|---|
| 1119 | my ($dbh, $user, $view, $remote, $opts) = @_; |
|---|
| 1120 | |
|---|
| 1121 | if ($LJ::SERVER_DOWN) { |
|---|
| 1122 | if ($opts->{'vhost'} eq "customview") { |
|---|
| 1123 | return "<!-- LJ down for maintenance -->"; |
|---|
| 1124 | } |
|---|
| 1125 | return &server_down_html(); |
|---|
| 1126 | } |
|---|
| 1127 | |
|---|
| 1128 | my ($styleid); |
|---|
| 1129 | if ($opts->{'styleid'}) { |
|---|
| 1130 | $styleid = $opts->{'styleid'}+0; |
|---|
| 1131 | } else { |
|---|
| 1132 | $view ||= "lastn"; # default view when none specified explicitly in URLs |
|---|
| 1133 | if ($LJ::viewinfo{$view}) { |
|---|
| 1134 | $styleid = -1; # to get past the return, then checked later for -1 and fixed, once user is loaded. |
|---|
| 1135 | $view = $view; |
|---|
| 1136 | } else { |
|---|
| 1137 | $opts->{'badargs'} = 1; |
|---|
| 1138 | } |
|---|
| 1139 | } |
|---|
| 1140 | return "" unless ($styleid); |
|---|
| 1141 | |
|---|
| 1142 | my $quser = $dbh->quote($user); |
|---|
| 1143 | my $u; |
|---|
| 1144 | if ($opts->{'u'}) { |
|---|
| 1145 | $u = $opts->{'u'}; |
|---|
| 1146 | } else { |
|---|
| 1147 | $sth = $dbh->prepare("SELECT * FROM user WHERE user=$quser"); |
|---|
| 1148 | $sth->execute; |
|---|
| 1149 | $u = $sth->fetchrow_hashref; |
|---|
| 1150 | } |
|---|
| 1151 | |
|---|
| 1152 | unless ($u) |
|---|
| 1153 | { |
|---|
| 1154 | $opts->{'baduser'} = 1; |
|---|
| 1155 | return "<H1>Error</H1>No such user <B>$user</B>"; |
|---|
| 1156 | } |
|---|
| 1157 | |
|---|
| 1158 | if ($styleid == -1) { |
|---|
| 1159 | $styleid = $u->{"${view}_style"}; |
|---|
| 1160 | } |
|---|
| 1161 | |
|---|
| 1162 | ## temporary, for contest1 themes |
|---|
| 1163 | $u->{'_contesttheme'} = $opts->{'contesttheme'}; |
|---|
| 1164 | |
|---|
| 1165 | if ($LJ::USER_VHOSTS && $opts->{'vhost'} eq "users" && $u->{'paidfeatures'} eq "off") |
|---|
| 1166 | { |
|---|
| 1167 | return "<B>Notice</B><BR>Addresses like <TT>http://<I>username</I>.$LJ::USER_DOMAIN</TT> only work for users with <A HREF=\"$LJ::SITEROOT/paidaccounts/\">paid accounts</A>. The journal you're trying to view is available here:<UL><FONT FACE=\"Verdana,Arial\"><B><A HREF=\"$LJ::SITEROOT/users/$user/\">$LJ::SITEROOT/users/$user/</A></B></FONT></UL>"; |
|---|
| 1168 | } |
|---|
| 1169 | if ($opts->{'vhost'} eq "customview" && $u->{'paidfeatures'} eq "off") |
|---|
| 1170 | { |
|---|
| 1171 | return "<B>Notice</B><BR>Only users with <A HREF=\"$LJ::SITEROOT/paidaccounts/\">paid accounts</A> can create and embed styles."; |
|---|
| 1172 | } |
|---|
| 1173 | if ($opts->{'vhost'} eq "community" && $u->{'journaltype'} ne "C") { |
|---|
| 1174 | return "<B>Notice</B><BR>This account isn't a community journal."; |
|---|
| 1175 | } |
|---|
| 1176 | |
|---|
| 1177 | return "<H1>Error</H1>Journal has been deleted. If you are <B>$user</B>, you have a period of 30 days to decide to undelete your journal." if ($u->{'statusvis'} eq "D"); |
|---|
| 1178 | return "<H1>Error</H1>This journal has been suspended." if ($u->{'statusvis'} eq "S"); |
|---|
| 1179 | |
|---|
| 1180 | my %vars = (); |
|---|
| 1181 | # load the base style |
|---|
| 1182 | my $basevars = ""; |
|---|
| 1183 | &load_style_fast($dbh, $styleid, \$basevars, \$view); |
|---|
| 1184 | |
|---|
| 1185 | # load the overrides |
|---|
| 1186 | my $overrides = ""; |
|---|
| 1187 | if ($opts->{'nooverride'}==0 && $u->{'useoverrides'} eq "Y") |
|---|
| 1188 | { |
|---|
| 1189 | $sth = $dbh->prepare("SELECT override FROM overrides WHERE user=$quser"); |
|---|
| 1190 | $sth->execute; |
|---|
| 1191 | ($overrides) = $sth->fetchrow_array; |
|---|
| 1192 | } |
|---|
| 1193 | |
|---|
| 1194 | # populate the variable hash |
|---|
| 1195 | &parse_vars(\$basevars, \%vars); |
|---|
| 1196 | &parse_vars(\$overrides, \%vars); |
|---|
| 1197 | &load_user_theme($dbh, $user, $u, \%vars); |
|---|
| 1198 | |
|---|
| 1199 | # kinda free some memory |
|---|
| 1200 | $basevars = ""; |
|---|
| 1201 | $overrides = ""; |
|---|
| 1202 | |
|---|
| 1203 | # instruct some function to make this specific view type |
|---|
| 1204 | return "" unless (defined $LJ::viewinfo{$view}->{'creator'}); |
|---|
| 1205 | my $ret = ""; |
|---|
| 1206 | |
|---|
| 1207 | # call the view creator w/ the buffer to fill and the construction variables |
|---|
| 1208 | &{$LJ::viewinfo{$view}->{'creator'}}(\$ret, $u, \%vars, $remote, $opts); |
|---|
| 1209 | |
|---|
| 1210 | # remove bad stuff |
|---|
| 1211 | unless ($opts->{'trusted_html'}) { |
|---|
| 1212 | &strip_bad_code(\$ret); |
|---|
| 1213 | } |
|---|
| 1214 | |
|---|
| 1215 | # return it... |
|---|
| 1216 | return $ret; |
|---|
| 1217 | } |
|---|
| 1218 | |
|---|
| 1219 | |
|---|
| 1220 | sub html_select |
|---|
| 1221 | { |
|---|
| 1222 | my $opts = shift; |
|---|
| 1223 | my @items = @_; |
|---|
| 1224 | my $disabled = $opts->{'disabled'} ? " DISABLED" : ""; |
|---|
| 1225 | my $ret; |
|---|
| 1226 | $ret .= "<select"; |
|---|
| 1227 | if ($opts->{'name'}) { $ret .= " name=\"$opts->{'name'}\""; } |
|---|
| 1228 | $ret .= "$disabled>"; |
|---|
| 1229 | while (my ($value, $text) = splice(@items, 0, 2)) { |
|---|
| 1230 | my $sel = ""; |
|---|
| 1231 | if ($value eq $opts->{'selected'}) { $sel = " selected"; } |
|---|
| 1232 | $ret .= "<option value=\"$value\"$sel>$text"; |
|---|
| 1233 | } |
|---|
| 1234 | $ret .= "</select>"; |
|---|
| 1235 | return $ret; |
|---|
| 1236 | } |
|---|
| 1237 | |
|---|
| 1238 | sub html_check |
|---|
| 1239 | { |
|---|
| 1240 | my $opts = shift; |
|---|
| 1241 | |
|---|
| 1242 | my $disabled = $opts->{'disabled'} ? " DISABLED" : ""; |
|---|
| 1243 | my $ret; |
|---|
| 1244 | $ret .= "<input type=checkbox "; |
|---|
| 1245 | if ($opts->{'selected'}) { $ret .= " checked"; } |
|---|
| 1246 | if ($opts->{'name'}) { $ret .= " name=\"$opts->{'name'}\""; } |
|---|
| 1247 | if ($opts->{'value'}) { $ret .= " value=\"$opts->{'value'}\""; } |
|---|
| 1248 | $ret .= "$disabled>"; |
|---|
| 1249 | return $ret; |
|---|
| 1250 | } |
|---|
| 1251 | |
|---|
| 1252 | sub html_text |
|---|
| 1253 | { |
|---|
| 1254 | my $opts = shift; |
|---|
| 1255 | |
|---|
| 1256 | my $disabled = $opts->{'disabled'} ? " DISABLED" : ""; |
|---|
| 1257 | my $ret; |
|---|
| 1258 | $ret .= "<input type=text"; |
|---|
| 1259 | if ($opts->{'size'}) { $ret .= " size=\"$opts->{'size'}\""; } |
|---|
| 1260 | if ($opts->{'maxlength'}) { $ret .= " maxlength=\"$opts->{'maxlength'}\""; } |
|---|
| 1261 | if ($opts->{'name'}) { $ret .= " name=\"" . &ehtml($opts->{'name'}) . "\""; } |
|---|
| 1262 | if ($opts->{'value'}) { $ret .= " value=\"" . &ehtml($opts->{'value'}) . "\""; } |
|---|
| 1263 | $ret .= "$disabled>"; |
|---|
| 1264 | return $ret; |
|---|
| 1265 | } |
|---|
| 1266 | |
|---|
| 1267 | # |
|---|
| 1268 | # returns the canonical username given, or blank if the username is not well-formed |
|---|
| 1269 | # |
|---|
| 1270 | sub canonical_username |
|---|
| 1271 | { |
|---|
| 1272 | my $user = shift; |
|---|
| 1273 | if ($user =~ /^[\w\-]{1,15}$/) { |
|---|
| 1274 | $user = lc($user); |
|---|
| 1275 | $user =~ s/-/_/g; |
|---|
| 1276 | return $user; |
|---|
| 1277 | } |
|---|
| 1278 | return ""; # not a good username. |
|---|
| 1279 | } |
|---|
| 1280 | |
|---|
| 1281 | sub decode_url_string |
|---|
| 1282 | { |
|---|
| 1283 | my $buffer = shift; # input scalarref |
|---|
| 1284 | my $hashref = shift; # output hash |
|---|
| 1285 | |
|---|
| 1286 | my $pair; |
|---|
| 1287 | my @pairs = split(/&/, $$buffer); |
|---|
| 1288 | my ($name, $value); |
|---|
| 1289 | foreach $pair (@pairs) |
|---|
| 1290 | { |
|---|
| 1291 | ($name, $value) = split(/=/, $pair); |
|---|
| 1292 | $value =~ tr/+/ /; |
|---|
| 1293 | $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; |
|---|
| 1294 | $name =~ tr/+/ /; |
|---|
| 1295 | $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; |
|---|
| 1296 | $hashref->{$name} .= $hashref->{$name} ? "\0$value" : $value; |
|---|
| 1297 | } |
|---|
| 1298 | } |
|---|
| 1299 | |
|---|
| 1300 | # called by nearly all the other functions |
|---|
| 1301 | sub get_dbh |
|---|
| 1302 | { |
|---|
| 1303 | my $type = shift; # 'master' or 'slave' |
|---|
| 1304 | my $dbh; |
|---|
| 1305 | |
|---|
| 1306 | ## already have a dbh of this type open? |
|---|
| 1307 | if (ref $LJ::DBCACHE{$type}) { |
|---|
| 1308 | $dbh = $LJ::DBCACHE{$type}; |
|---|
| 1309 | |
|---|
| 1310 | # make sure connection is still good. |
|---|
| 1311 | my $sth = $dbh->prepare("SELECT CONNECTION_ID()"); # mysql specific |
|---|
| 1312 | $sth->execute; |
|---|
| 1313 | my ($id) = $sth->fetchrow_array; |
|---|
| 1314 | if ($id) { return $dbh; } |
|---|
| 1315 | undef $dbh; |
|---|
| 1316 | undef $LJ::DBCACHE{$type}; |
|---|
| 1317 | } |
|---|
| 1318 | |
|---|
| 1319 | ### if we don't have a dbh cached already, which one would we try to connect to? |
|---|
| 1320 | my $key; |
|---|
| 1321 | if ($type eq "slave") { |
|---|
| 1322 | my $ct = $LJ::DBINFO{'slavecount'}; |
|---|
| 1323 | if ($ct) { |
|---|
| 1324 | $key = "slave" . int(rand($ct)+1); |
|---|
| 1325 | } else { |
|---|
| 1326 | $key = "master"; |
|---|
| 1327 | } |
|---|
| 1328 | } else { |
|---|
| 1329 | $key = "master"; |
|---|
| 1330 | } |
|---|
| 1331 | |
|---|
| 1332 | $dbh = DBI->connect("DBI:mysql:livejournal:$LJ::DBINFO{$key}->{'host'}", |
|---|
| 1333 | $LJ::DBINFO{$key}->{'user'}, |
|---|
| 1334 | $LJ::DBINFO{$key}->{'pass'}, |
|---|
| 1335 | { |
|---|
| 1336 | PrintError => 0, |
|---|
| 1337 | }); |
|---|
| 1338 | |
|---|
| 1339 | # save a reference to the database handle for later |
|---|
| 1340 | $LJ::DBCACHE{$type} = $dbh; |
|---|
| 1341 | |
|---|
| 1342 | return $dbh; |
|---|
| 1343 | } |
|---|
| 1344 | |
|---|
| 1345 | ## turns a date (yyyy-mm-dd) into links to year calendar, month view, and day view, given |
|---|
| 1346 | ## also a user object (hashref) |
|---|
| 1347 | sub date_to_view_links |
|---|
| 1348 | { |
|---|
| 1349 | my ($u, $date) = @_; |
|---|
| 1350 | |
|---|
| 1351 | return unless ($date =~ /(\d\d\d\d)-(\d\d)-(\d\d)/); |
|---|
| 1352 | my ($y, $m, $d) = ($1, $2, $3); |
|---|
| 1353 | my ($nm, $nd) = ($m+0, $d+0); # numeric, without leading zeros |
|---|
| 1354 | my $user = $u->{'user'}; |
|---|
| 1355 | |
|---|
| 1356 | my $ret; |
|---|
| 1357 | $ret .= "<a href=\"$LJ::SITEROOT/users/$user/calendar/$y\">$y</a>-"; |
|---|
| 1358 | $ret .= "<a href=\"$LJ::SITEROOT/view/?type=month&user=$user&y=$y&m=$nm\">$m</a>-"; |
|---|
| 1359 | $ret .= "<a href=\"$LJ::SITEROOT/users/$user/day/$y/$m/$d\">$d</a>"; |
|---|
| 1360 | return $ret; |
|---|
| 1361 | } |
|---|
| 1362 | |
|---|
| 1363 | sub item_link |
|---|
| 1364 | { |
|---|
| 1365 | my ($u, $itemid) = @_; |
|---|
| 1366 | return "$LJ::SITEROOT/talkread.bml?itemid=$itemid"; |
|---|
| 1367 | } |
|---|
| 1368 | |
|---|
| 1369 | sub make_graphviz_dot_file |
|---|
| 1370 | { |
|---|
| 1371 | my $dbh = shift; |
|---|
| 1372 | my $user = shift; |
|---|
| 1373 | |
|---|
| 1374 | my $quser = $dbh->quote($user); |
|---|
| 1375 | my $sth; |
|---|
| 1376 | my $ret; |
|---|
| 1377 | |
|---|
| 1378 | $sth = $dbh->prepare("SELECT *, UNIX_TIMESTAMP()-UNIX_TIMESTAMP(timeupdate) AS 'secondsold' FROM user WHERE user=$quser"); |
|---|
| 1379 | $sth->execute; |
|---|
| 1380 | my $u = $sth->fetchrow_hashref; |
|---|
| 1381 | |
|---|
| 1382 | unless ($u) { |
|---|
| 1383 | return ""; |
|---|
| 1384 | } |
|---|
| 1385 | |
|---|
| 1386 | $ret .= "digraph G {\n"; |
|---|
| 1387 | $ret .= " node [URL=\"$LJ::SITEROOT/userinfo.bml?user=\\N\"]\n"; |
|---|
| 1388 | $ret .= " node [fontsize=10, color=lightgray, style=filled]\n"; |
|---|
| 1389 | $ret .= " \"$user\" [color=yellow, style=filled]\n"; |
|---|
| 1390 | |
|---|
| 1391 | my @friends = (); |
|---|
| 1392 | $sth = $dbh->prepare("SELECT friendid FROM friends WHERE userid=$u->{'userid'} AND userid<>friendid"); |
|---|
| 1393 | $sth->execute; |
|---|
| 1394 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 1395 | push @friends, $_->{'friendid'}; |
|---|
| 1396 | } |
|---|
| 1397 | |
|---|
| 1398 | my $friendsin = join(", ", map { $dbh->quote($_); } ($u->{'userid'}, @friends)); |
|---|
| 1399 | my $sql = "SELECT uu.user, uf.user AS 'friend' FROM friends f, user uu, user uf WHERE f.userid=uu.userid AND f.friendid=uf.userid AND f.userid<>f.friendid AND uu.statusvis='V' AND uf.statusvis='V' AND (f.friendid=$u->{'userid'} OR (f.userid IN ($friendsin) AND f.friendid IN ($friendsin)))"; |
|---|
| 1400 | $sth = $dbh->prepare($sql); |
|---|
| 1401 | $sth->execute; |
|---|
| 1402 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 1403 | $ret .= " \"$_->{'user'}\"->\"$_->{'friend'}\"\n"; |
|---|
| 1404 | $mark{$_->{'user'}}++; |
|---|
| 1405 | $mark{$_->{'friend'}}++; |
|---|
| 1406 | } |
|---|
| 1407 | |
|---|
| 1408 | $ret .= "}\n"; |
|---|
| 1409 | |
|---|
| 1410 | return $ret; |
|---|
| 1411 | } |
|---|
| 1412 | |
|---|
| 1413 | sub expand_embedded |
|---|
| 1414 | { |
|---|
| 1415 | my $dbh = shift; |
|---|
| 1416 | my $itemid = shift; |
|---|
| 1417 | my $remote = shift; |
|---|
| 1418 | my $eventref = shift; |
|---|
| 1419 | |
|---|
| 1420 | &LJ::Poll::show_polls($dbh, $itemid, $remote, $eventref); |
|---|
| 1421 | } |
|---|
| 1422 | |
|---|
| 1423 | sub make_remote |
|---|
| 1424 | { |
|---|
| 1425 | my $user = shift; |
|---|
| 1426 | my $userid = shift; |
|---|
| 1427 | if ($userid && $userid =~ /^\d+$/) { |
|---|
| 1428 | return { 'user' => $user, |
|---|
| 1429 | 'userid' => $userid, }; |
|---|
| 1430 | } |
|---|
| 1431 | return undef; |
|---|
| 1432 | } |
|---|
| 1433 | |
|---|
| 1434 | sub escapeall |
|---|
| 1435 | { |
|---|
| 1436 | my $a = $_[0]; |
|---|
| 1437 | |
|---|
| 1438 | ### escape HTML |
|---|
| 1439 | $a =~ s/\&/&/g; |
|---|
| 1440 | $a =~ s/\"/"/g; |
|---|
| 1441 | $a =~ s/</</g; |
|---|
| 1442 | $a =~ s/>/>/g; |
|---|
| 1443 | |
|---|
| 1444 | ### and escape BML |
|---|
| 1445 | $a =~ s/\(=/\(=/g; |
|---|
| 1446 | $a =~ s/=\)/=\)/g; |
|---|
| 1447 | return $a; |
|---|
| 1448 | } |
|---|
| 1449 | |
|---|
| 1450 | sub load_user |
|---|
| 1451 | { |
|---|
| 1452 | my $dbh = shift; |
|---|
| 1453 | my $user = shift; |
|---|
| 1454 | my $quser = $dbh->quote($user); |
|---|
| 1455 | my $sth = $dbh->prepare("SELECT * FROM user WHERE user=$quser"); |
|---|
| 1456 | $sth->execute; |
|---|
| 1457 | my $u = $sth->fetchrow_hashref; |
|---|
| 1458 | $sth->finish; |
|---|
| 1459 | return $u; |
|---|
| 1460 | } |
|---|
| 1461 | |
|---|
| 1462 | sub load_moods |
|---|
| 1463 | { |
|---|
| 1464 | return if ($LJ::CACHED_MOODS); |
|---|
| 1465 | my $dbh = shift; |
|---|
| 1466 | my $sth = $dbh->prepare("SELECT moodid, mood, parentmood FROM moods"); |
|---|
| 1467 | $sth->execute; |
|---|
| 1468 | while (my ($id, $mood, $parent) = $sth->fetchrow_array) { |
|---|
| 1469 | $LJ::CACHE_MOODS{$id} = { 'name' => $mood, 'parent' => $parent }; |
|---|
| 1470 | if ($id > $LJ::CACHED_MOOD_MAX) { $LJ::CACHED_MOOD_MAX = $id; } |
|---|
| 1471 | } |
|---|
| 1472 | $LJ::CACHED_MOODS = 1; |
|---|
| 1473 | } |
|---|
| 1474 | |
|---|
| 1475 | sub query_buffer_add |
|---|
| 1476 | { |
|---|
| 1477 | my ($dbh, $table, $query) = @_; |
|---|
| 1478 | |
|---|
| 1479 | if ($LJ::BUFFER_QUERIES) |
|---|
| 1480 | { |
|---|
| 1481 | # if this is a high load site, you'll want to batch queries up and send them at once. |
|---|
| 1482 | |
|---|
| 1483 | my $table = $dbh->quote($table); |
|---|
| 1484 | my $query = $dbh->quote($query); |
|---|
| 1485 | $dbh->do("INSERT INTO querybuffer (qbid, tablename, instime, query) VALUES (NULL, $table, NOW(), $query)"); |
|---|
| 1486 | } |
|---|
| 1487 | else |
|---|
| 1488 | { |
|---|
| 1489 | # low load sites can skip this, and just have queries go through immediately. |
|---|
| 1490 | |
|---|
| 1491 | $dbh->do($query); |
|---|
| 1492 | } |
|---|
| 1493 | } |
|---|
| 1494 | |
|---|
| 1495 | sub query_buffer_flush |
|---|
| 1496 | { |
|---|
| 1497 | my ($dbh, $table) = @_; |
|---|
| 1498 | return -1 unless ($table); |
|---|
| 1499 | return -1 if ($table =~ /[^\w]/); |
|---|
| 1500 | |
|---|
| 1501 | $dbh->do("LOCK TABLES $table WRITE, querybuffer WRITE"); |
|---|
| 1502 | |
|---|
| 1503 | my $count = 0; |
|---|
| 1504 | my $max = 0; |
|---|
| 1505 | my $qtable = $dbh->quote($table); |
|---|
| 1506 | $sth = $dbh->prepare("SELECT qbid, query FROM querybuffer WHERE tablename=$qtable ORDER BY qbid"); |
|---|
| 1507 | if ($dbh->err) { $dbh->do("UNLOCK TABLES"); die $dbh->errstr; } |
|---|
| 1508 | $sth->execute; |
|---|
| 1509 | if ($dbh->err) { $dbh->do("UNLOCK TABLES"); die $dbh->errstr; } |
|---|
| 1510 | while (my ($id, $query) = $sth->fetchrow_array) |
|---|
| 1511 | { |
|---|
| 1512 | $dbh->do($query); |
|---|
| 1513 | $count++; |
|---|
| 1514 | $max = $id; |
|---|
| 1515 | } |
|---|
| 1516 | $sth->finish; |
|---|
| 1517 | |
|---|
| 1518 | $dbh->do("DELETE FROM querybuffer WHERE tablename=$qtable"); |
|---|
| 1519 | if ($dbh->err) { $dbh->do("UNLOCK TABLES"); die $dbh->errstr; } |
|---|
| 1520 | |
|---|
| 1521 | $dbh->do("UNLOCK TABLES"); |
|---|
| 1522 | return $count; |
|---|
| 1523 | } |
|---|
| 1524 | |
|---|
| 1525 | sub journal_base |
|---|
| 1526 | { |
|---|
| 1527 | my ($user, $vhost) = @_; |
|---|
| 1528 | if ($vhost eq "users") { |
|---|
| 1529 | my $he_user = $user; |
|---|
| 1530 | $he_user =~ s/_/-/g; |
|---|
| 1531 | return "http://$he_user.$LJ::USER_DOMAIN"; |
|---|
| 1532 | } elsif ($vhost eq "tilde") { |
|---|
| 1533 | return "$LJ::SITEROOT/~$user"; |
|---|
| 1534 | } elsif ($vhost eq "community") { |
|---|
| 1535 | return "$LJ::SITEROOT/community/$user"; |
|---|
| 1536 | } else { |
|---|
| 1537 | return "$LJ::SITEROOT/users/$user"; |
|---|
| 1538 | } |
|---|
| 1539 | } |
|---|
| 1540 | |
|---|
| 1541 | # check to see if the given remote user has a certain privledge |
|---|
| 1542 | sub remote_has_priv |
|---|
| 1543 | { |
|---|
| 1544 | my $dbh = shift; |
|---|
| 1545 | my $remote = shift; |
|---|
| 1546 | my $privcode = shift; # required. priv code to check for. |
|---|
| 1547 | my $ref = shift; # optional, arrayref or hashref to populate |
|---|
| 1548 | |
|---|
| 1549 | return 0 unless ($remote); |
|---|
| 1550 | |
|---|
| 1551 | ### authentication done. time to authorize... |
|---|
| 1552 | |
|---|
| 1553 | my $qprivcode = $dbh->quote($privcode); |
|---|
| 1554 | my $sth = $dbh->prepare("SELECT pm.arg FROM priv_map pm, priv_list pl WHERE pm.prlid=pl.prlid AND pl.privcode=$qprivcode AND pm.userid=$remote->{'userid'}"); |
|---|
| 1555 | $sth->execute; |
|---|
| 1556 | |
|---|
| 1557 | my $match = 0; |
|---|
| 1558 | if (ref $ref eq "ARRAY") { @$ref = (); } |
|---|
| 1559 | if (ref $ref eq "HASH") { %$ref = (); } |
|---|
| 1560 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 1561 | $match++; |
|---|
| 1562 | if (ref $ref eq "ARRAY") { push @$ref, $_->{'arg'}; } |
|---|
| 1563 | if (ref $ref eq "HASH") { $ref->{$_->{'arg'}} = 1; } |
|---|
| 1564 | } |
|---|
| 1565 | return $match; |
|---|
| 1566 | } |
|---|
| 1567 | |
|---|
| 1568 | ## get a userid from a username (returns 0 if invalid user) |
|---|
| 1569 | sub get_userid |
|---|
| 1570 | { |
|---|
| 1571 | my $dbh = shift; |
|---|
| 1572 | my $user = shift; |
|---|
| 1573 | my $userid; |
|---|
| 1574 | if ($CACHE_USERID{$user}) { return $CACHE_USERID{$user}; } |
|---|
| 1575 | |
|---|
| 1576 | my $quser = $dbh->quote($user); |
|---|
| 1577 | my $sth = $dbh->prepare("SELECT userid FROM user WHERE user=$quser"); |
|---|
| 1578 | $sth->execute; |
|---|
| 1579 | ($userid) = $sth->fetchrow_array; |
|---|
| 1580 | if ($userid) { $CACHE_USERID{$user} = $userid; } |
|---|
| 1581 | return ($userid+0); |
|---|
| 1582 | } |
|---|
| 1583 | |
|---|
| 1584 | ## get a username from a userid (returns undef if invalid user) |
|---|
| 1585 | sub get_username |
|---|
| 1586 | { |
|---|
| 1587 | my $dbh = shift; |
|---|
| 1588 | my $userid = shift; |
|---|
| 1589 | my $user; |
|---|
| 1590 | $userid += 0; |
|---|
| 1591 | if ($CACHE_USERNAME{$userid}) { return $CACHE_USERNAME{$userid}; } |
|---|
| 1592 | |
|---|
| 1593 | my $sth = $dbh->prepare("SELECT user FROM user WHERE userid=$userid"); |
|---|
| 1594 | $sth->execute; |
|---|
| 1595 | ($user) = $sth->fetchrow_array; |
|---|
| 1596 | if ($user) { $CACHE_USERNAME{$userid} = $user; } |
|---|
| 1597 | return ($user); |
|---|
| 1598 | } |
|---|
| 1599 | |
|---|
| 1600 | sub get_itemid_near |
|---|
| 1601 | { |
|---|
| 1602 | my $dbh = shift; |
|---|
| 1603 | my $ownerid = shift; |
|---|
| 1604 | my $date = shift; |
|---|
| 1605 | my $after_before = shift; |
|---|
| 1606 | return 0 unless ($date =~ /^(\d{4})-(\d{2})-\d{2} \d{2}:\d{2}:\d{2}$/); |
|---|
| 1607 | my ($year, $month) = ($1, $2); |
|---|
| 1608 | |
|---|
| 1609 | my ($op, $inc, $func); |
|---|
| 1610 | if ($after_before eq "after") { |
|---|
| 1611 | ($op, $inc, $func) = (">", 1, "MIN"); |
|---|
| 1612 | } elsif ($after_before eq "before") { |
|---|
| 1613 | ($op, $inc, $func) = ("<", -1, "MAX"); |
|---|
| 1614 | } else { |
|---|
| 1615 | return 0; |
|---|
| 1616 | } |
|---|
| 1617 | |
|---|
| 1618 | my $qeventtime = $dbh->quote($date); |
|---|
| 1619 | |
|---|
| 1620 | my $item = 0; |
|---|
| 1621 | my $tries = 0; |
|---|
| 1622 | while ($item==0 && $tries<2) |
|---|
| 1623 | { |
|---|
| 1624 | my $sql = "SELECT $func(itemid) FROM log WHERE ownerid=$ownerid AND year=$year AND month=$month AND eventtime $op $qeventtime"; |
|---|
| 1625 | my $sth = $dbh->prepare($sql); |
|---|
| 1626 | $sth->execute; |
|---|
| 1627 | ($item) = $sth->fetchrow_array; |
|---|
| 1628 | |
|---|
| 1629 | unless ($item) { |
|---|
| 1630 | $tries++; |
|---|
| 1631 | $month += $inc; |
|---|
| 1632 | if ($month == 13) { $month = 1; $year++; } |
|---|
| 1633 | if ($month == 0) { $month = 12; $year--; } |
|---|
| 1634 | } |
|---|
| 1635 | } |
|---|
| 1636 | return ($item+0); |
|---|
| 1637 | } |
|---|
| 1638 | |
|---|
| 1639 | sub get_itemid_after { return get_itemid_near(@_, "after"); } |
|---|
| 1640 | sub get_itemid_before { return get_itemid_near(@_, "before"); } |
|---|
| 1641 | |
|---|
| 1642 | sub mysql_time |
|---|
| 1643 | { |
|---|
| 1644 | my $time = shift; |
|---|
| 1645 | $time ||= time(); |
|---|
| 1646 | my @ltime = localtime($time); |
|---|
| 1647 | return sprintf("%04d-%02d-%02d %02d:%02d:%02d", |
|---|
| 1648 | $ltime[5]+1900, |
|---|
| 1649 | $ltime[4]+1, |
|---|
| 1650 | $ltime[3], |
|---|
| 1651 | $ltime[2], |
|---|
| 1652 | $ltime[1], |
|---|
| 1653 | $ltime[0]); |
|---|
| 1654 | } |
|---|
| 1655 | |
|---|
| 1656 | sub get_keyword_id |
|---|
| 1657 | { |
|---|
| 1658 | my $dbh = shift; |
|---|
| 1659 | my $kw = shift; |
|---|
| 1660 | unless ($kw =~ /\S/) { return 0; } |
|---|
| 1661 | my $qkw = $dbh->quote($kw); |
|---|
| 1662 | |
|---|
| 1663 | my $sth = $dbh->prepare("SELECT kwid FROM keywords WHERE keyword=$qkw"); |
|---|
| 1664 | $sth->execute; |
|---|
| 1665 | my ($kwid) = $sth->fetchrow_array; |
|---|
| 1666 | unless ($kwid) { |
|---|
| 1667 | $sth = $dbh->prepare("INSERT INTO keywords (kwid, keyword) VALUES (NULL, $qkw)"); |
|---|
| 1668 | $sth->execute; |
|---|
| 1669 | $kwid = $dbh->{'mysql_insertid'}; |
|---|
| 1670 | } |
|---|
| 1671 | return $kwid; |
|---|
| 1672 | } |
|---|
| 1673 | |
|---|
| 1674 | sub trim |
|---|
| 1675 | { |
|---|
| 1676 | my $a = $_[0]; |
|---|
| 1677 | $a =~ s/^\s+//; |
|---|
| 1678 | $a =~ s/\s+$//; |
|---|
| 1679 | return $a; |
|---|
| 1680 | } |
|---|
| 1681 | |
|---|
| 1682 | # returns true if $formref->{'password'} matches cleartext password or if |
|---|
| 1683 | # $formref->{'hpassword'} is the hash of the cleartext password |
|---|
| 1684 | sub valid_password |
|---|
| 1685 | { |
|---|
| 1686 | my ($clearpass, $formref) = @_; |
|---|
| 1687 | if ($formref->{'password'} && $formref->{'password'} eq $clearpass) |
|---|
| 1688 | { |
|---|
| 1689 | return 1; |
|---|
| 1690 | } |
|---|
| 1691 | if ($formref->{'hpassword'} && lc($formref->{'hpassword'}) eq &hash_password($clearpass)) |
|---|
| 1692 | { |
|---|
| 1693 | return 1; |
|---|
| 1694 | } |
|---|
| 1695 | return 0; |
|---|
| 1696 | } |
|---|
| 1697 | |
|---|
| 1698 | sub delete_user |
|---|
| 1699 | { |
|---|
| 1700 | my $dbh = shift; |
|---|
| 1701 | my $user = shift; |
|---|
| 1702 | my $quser = $dbh->quote($user); |
|---|
| 1703 | my $sth; |
|---|
| 1704 | $sth = $dbh->prepare("SELECT user, userid FROM user WHERE user=$quser"); |
|---|
| 1705 | my $u = $sth->fetchrow_hashref; |
|---|
| 1706 | unless ($u) { return; } |
|---|
| 1707 | |
|---|
| 1708 | ### so many issues. |
|---|
| 1709 | } |
|---|
| 1710 | |
|---|
| 1711 | sub hash_password |
|---|
| 1712 | { |
|---|
| 1713 | return Digest::MD5::md5_hex($_[0]); |
|---|
| 1714 | } |
|---|
| 1715 | |
|---|
| 1716 | sub can_use_journal |
|---|
| 1717 | { |
|---|
| 1718 | my ($dbh, $posterid, $reqownername, $res) = @_; |
|---|
| 1719 | my $qreqownername = $dbh->quote($reqownername); |
|---|
| 1720 | my $qposterid = $posterid+0; |
|---|
| 1721 | |
|---|
| 1722 | ## find the journal owner's userid |
|---|
| 1723 | my $sth = $dbh->prepare("SELECT userid FROM user WHERE user=$qreqownername"); |
|---|
| 1724 | $sth->execute; |
|---|
| 1725 | my ($ownerid) = $sth->fetchrow_array; |
|---|
| 1726 | unless ($ownerid) { |
|---|
| 1727 | $res->{'errmsg'} = "User \"$reqownername\" does not exist."; |
|---|
| 1728 | return 0; |
|---|
| 1729 | } |
|---|
| 1730 | |
|---|
| 1731 | ## check if user has access |
|---|
| 1732 | $sth = $dbh->prepare("SELECT COUNT(*) AS 'count' FROM logaccess WHERE ownerid=$ownerid AND posterid=$qposterid"); |
|---|
| 1733 | |
|---|
| 1734 | $sth->execute; |
|---|
| 1735 | my $row = $sth->fetchrow_hashref; |
|---|
| 1736 | if ($row && $row->{'count'}==1) { |
|---|
| 1737 | $res->{'ownerid'} = $ownerid; |
|---|
| 1738 | return 1; |
|---|
| 1739 | } else { |
|---|
| 1740 | $res->{'errmsg'} = "You do not have access to post to this journal."; |
|---|
| 1741 | return 0; |
|---|
| 1742 | } |
|---|
| 1743 | } |
|---|
| 1744 | |
|---|
| 1745 | ## internal function to most efficiently retrieve the last 'n' items |
|---|
| 1746 | ## for either the lastn or friends view |
|---|
| 1747 | sub get_recent_itemids |
|---|
| 1748 | { |
|---|
| 1749 | my $dbh = shift; |
|---|
| 1750 | my ($opts) = shift; |
|---|
| 1751 | |
|---|
| 1752 | my @itemids = (); |
|---|
| 1753 | my $userid = $opts->{'userid'}+0; |
|---|
| 1754 | my $view = $opts->{'view'}; |
|---|
| 1755 | my $remid = $opts->{'remoteid'}+0; |
|---|
| 1756 | |
|---|
| 1757 | my $max_hints = 0; |
|---|
| 1758 | my $sort_key = "eventtime"; |
|---|
| 1759 | if ($view eq "lastn") { $max_hints = $LJ::MAX_HINTS_LASTN; } |
|---|
| 1760 | if ($view eq "friends") { |
|---|
| 1761 | $max_hints = $LJ::MAX_HINTS_FRIENDS; |
|---|
| 1762 | $sort_key = "logtime"; |
|---|
| 1763 | } |
|---|
| 1764 | unless ($max_hints) { return @itemids; } |
|---|
| 1765 | |
|---|
| 1766 | my $skip = $opts->{'skip'}+0; |
|---|
| 1767 | my $itemshow = $opts->{'itemshow'}+0; |
|---|
| 1768 | if ($itemshow > $max_hints) { $itemshow = $max_hints; } |
|---|
| 1769 | my $maxskip = $max_hints - $itemshow; |
|---|
| 1770 | if ($skip < 0) { $skip = 0; } |
|---|
| 1771 | if ($skip > $maxskip) { $skip = $maxskip; } |
|---|
| 1772 | my $itemload = $itemshow+$skip; |
|---|
| 1773 | |
|---|
| 1774 | ### get all the known hints, right off the bat. |
|---|
| 1775 | |
|---|
| 1776 | $sth = $dbh->prepare("SELECT hintid, itemid FROM hint${view}view WHERE userid=$userid"); |
|---|
| 1777 | $sth->execute; |
|---|
| 1778 | my %iteminf; |
|---|
| 1779 | my $numhints = 0; |
|---|
| 1780 | while ($_ = $sth->fetchrow_arrayref) { |
|---|
| 1781 | $numhints++; |
|---|
| 1782 | $iteminf{$_->[1]} = { 'hintid' => $_->[0] }; |
|---|
| 1783 | } |
|---|
| 1784 | if ($numhints > $max_hints * 4) { |
|---|
| 1785 | my @extra = sort { $b->{'hintid'} <=> $a->{'hintid'} } values %iteminf; |
|---|
| 1786 | my $minextra = $extra[$max_hints]->{'hintid'}; |
|---|
| 1787 | $dbh->do("DELETE FROM hint${view}view WHERE userid=$userid AND hintid<=$minextra"); |
|---|
| 1788 | foreach my $itemid (keys %iteminf) { |
|---|
| 1789 | if ($iteminf{$itemid}->{'hintid'} <= $minextra) { |
|---|
| 1790 | delete $iteminf{$itemid}; |
|---|
| 1791 | } |
|---|
| 1792 | } |
|---|
| 1793 | |
|---|
| 1794 | } |
|---|
| 1795 | |
|---|
| 1796 | if (%iteminf) |
|---|
| 1797 | { |
|---|
| 1798 | my %gmask_from; # group mask of remote user from context of userid in key |
|---|
| 1799 | my $itemid_in = join(",", keys %iteminf); |
|---|
| 1800 | |
|---|
| 1801 | if ($remid) { |
|---|
| 1802 | if ($view eq "lastn") |
|---|
| 1803 | { |
|---|
| 1804 | ## then we need to load the group mask for this friend |
|---|
| 1805 | $sth = $dbh->prepare("SELECT groupmask FROM friends WHERE userid=$userid AND friendid=$remid"); |
|---|
| 1806 | $sth->execute; |
|---|
| 1807 | my ($mask) = $sth->fetchrow_array; |
|---|
| 1808 | $gmask_from{$userid} = $mask; |
|---|
| 1809 | } |
|---|
| 1810 | } |
|---|
| 1811 | |
|---|
| 1812 | $sth = $dbh->prepare("SELECT itemid, security, allowmask, $sort_key FROM log WHERE itemid IN ($itemid_in)"); |
|---|
| 1813 | $sth->execute; |
|---|
| 1814 | while (my $li = $sth->fetchrow_hashref) |
|---|
| 1815 | { |
|---|
| 1816 | my $this_ownerid = $li->{'ownerid'} || $userid; |
|---|
| 1817 | |
|---|
| 1818 | if ($li->{'security'} eq "public" || |
|---|
| 1819 | ($li->{'security'} eq "usemask" && |
|---|
| 1820 | (($li->{'allowmask'} + 0) & $gmask_from{$this_ownerid})) || |
|---|
| 1821 | ($remid && $this_ownerid == $remid)) |
|---|
| 1822 | { |
|---|
| 1823 | push @itemids, { 'hintid' => $iteminf{$li->{'itemid'}}->{'hintid'}, |
|---|
| 1824 | 'itemid' => $li->{'itemid'}, |
|---|
| 1825 | 'ownerid' => $this_ownerid, |
|---|
| 1826 | $sort_key => $li->{$sort_key}, |
|---|
| 1827 | }; |
|---|
| 1828 | } |
|---|
| 1829 | } |
|---|
| 1830 | } |
|---|
| 1831 | |
|---|
| 1832 | %iteminf = (); # free some memory (like perl would care!) |
|---|
| 1833 | |
|---|
| 1834 | @itemids = sort { $b->{$sort_key} cmp $a->{$sort_key} } @itemids; |
|---|
| 1835 | |
|---|
| 1836 | my $hintcount = scalar(@itemids); |
|---|
| 1837 | |
|---|
| 1838 | if ($hintcount >= $itemload) |
|---|
| 1839 | { |
|---|
| 1840 | # we can delete some items from the hints table. |
|---|
| 1841 | if ($hintcount > $max_hints) { |
|---|
| 1842 | my @remove = splice (@itemids, $max_hints, ($hintcount-$max_hints)); |
|---|
| 1843 | $hintcount = scalar(@itemids); |
|---|
| 1844 | if (@remove) { |
|---|
| 1845 | my $sql = "REPLACE INTO batchdelete (what, itsid) VALUES "; |
|---|
| 1846 | $sql .= join(",", map { "('hint${view}', $_->{'hintid'})" } @remove); |
|---|
| 1847 | $dbh->do($sql); |
|---|
| 1848 | |
|---|
| 1849 | # my $removein = join(",", map { $_->{'hintid'} } @remove); |
|---|
| 1850 | # $dbh->do("DELETE FROM hint${view}view WHERE hintid IN ($removein)"); |
|---|
| 1851 | } |
|---|
| 1852 | } |
|---|
| 1853 | } |
|---|
| 1854 | elsif (! $opts->{'dont_add_hints'}) |
|---|
| 1855 | { |
|---|
| 1856 | ## this hints table was too small. populate it again. |
|---|
| 1857 | |
|---|
| 1858 | #print "Not enough in hint table! hintcount ($hintcount) < itemload ($itemload)\n"; |
|---|
| 1859 | |
|---|
| 1860 | if ($view eq "lastn") |
|---|
| 1861 | { |
|---|
| 1862 | my $sql = " |
|---|
| 1863 | REPLACE INTO hintlastnview (hintid, userid, itemid) |
|---|
| 1864 | SELECT NULL, $userid, l.itemid |
|---|
| 1865 | FROM log l |
|---|
| 1866 | WHERE l.ownerid=$userid |
|---|
| 1867 | ORDER BY l.eventtime DESC, l.logtime DESC |
|---|
| 1868 | LIMIT $max_hints |
|---|
| 1869 | "; |
|---|
| 1870 | |
|---|
| 1871 | # FUCK IT! This kills MySQL! Maybe later. |
|---|
| 1872 | # $dbh->do($sql); |
|---|
| 1873 | } |
|---|
| 1874 | |
|---|
| 1875 | ## call ourselves recursively, now that we've populated the hints table |
|---|
| 1876 | ## however, we set this flag so we don't recurse again. this may be true |
|---|
| 1877 | ## for new journals that don't yet have $max_hints entries in them |
|---|
| 1878 | |
|---|
| 1879 | $opts->{'dont_add_hints'} = 1; |
|---|
| 1880 | return &get_recent_itemids($dbh, $opts); |
|---|
| 1881 | } |
|---|
| 1882 | |
|---|
| 1883 | ### remove the ones we're skipping |
|---|
| 1884 | if ($skip) { |
|---|
| 1885 | splice (@itemids, 0, $skip); |
|---|
| 1886 | } |
|---|
| 1887 | if (@itemids > $itemshow) { |
|---|
| 1888 | splice (@itemids, $itemshow, (scalar(@itemids)-$itemshow)); |
|---|
| 1889 | } |
|---|
| 1890 | |
|---|
| 1891 | ## change the list of hashrefs to a list of integers (don't need other info now) |
|---|
| 1892 | if (ref $opts->{'owners'} eq "HASH") { |
|---|
| 1893 | grep { $opts->{'owners'}->{$_->{'ownerid'}}++ } @itemids; |
|---|
| 1894 | } |
|---|
| 1895 | |
|---|
| 1896 | @itemids = map { $_->{'itemid'} } @itemids; |
|---|
| 1897 | return @itemids; |
|---|
| 1898 | } |
|---|
| 1899 | |
|---|
| 1900 | sub load_log_props |
|---|
| 1901 | { |
|---|
| 1902 | my ($dbh, $listref, $hashref) = @_; |
|---|
| 1903 | my $itemin = join(", ", map { $_+0; } @{$listref}); |
|---|
| 1904 | unless ($itemin) { return ; } |
|---|
| 1905 | unless (ref $hashref eq "HASH") { return; } |
|---|
| 1906 | |
|---|
| 1907 | my $sth = $dbh->prepare("SELECT p.itemid, l.name, p.value FROM logprop p, logproplist l WHERE p.propid=l.propid AND p.itemid IN ($itemin)"); |
|---|
| 1908 | $sth->execute; |
|---|
| 1909 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 1910 | $hashref->{$_->{'itemid'}}->{$_->{'name'}} = $_->{'value'}; |
|---|
| 1911 | } |
|---|
| 1912 | $sth->finish; |
|---|
| 1913 | } |
|---|
| 1914 | |
|---|
| 1915 | sub load_talk_props |
|---|
| 1916 | { |
|---|
| 1917 | my ($dbh, $listref, $hashref) = @_; |
|---|
| 1918 | my $itemin = join(", ", map { $_+0; } @{$listref}); |
|---|
| 1919 | unless ($itemin) { return ; } |
|---|
| 1920 | unless (ref $hashref eq "HASH") { return; } |
|---|
| 1921 | |
|---|
| 1922 | my $sth = $dbh->prepare("SELECT tp.talkid, tpl.name, tp.value FROM talkproplist tpl, talkprop tp WHERE tp.tpropid=tpl.tpropid AND tp.talkid IN ($itemin)"); |
|---|
| 1923 | $sth->execute; |
|---|
| 1924 | while (my ($id, $name, $val) = $sth->fetchrow_array) { |
|---|
| 1925 | $hashref->{$id}->{$name} = $val; |
|---|
| 1926 | } |
|---|
| 1927 | $sth->finish; |
|---|
| 1928 | } |
|---|
| 1929 | |
|---|
| 1930 | |
|---|
| 1931 | sub eurl |
|---|
| 1932 | { |
|---|
| 1933 | my $a = $_[0]; |
|---|
| 1934 | $a =~ s/([^a-zA-Z0-9_\,\-.\/\\\: ])/uc sprintf("%%%02x",ord($1))/eg; |
|---|
| 1935 | $a =~ tr/ /+/; |
|---|
| 1936 | return $a; |
|---|
| 1937 | } |
|---|
| 1938 | |
|---|
| 1939 | ### escape stuff so it can be used in XML attributes or elements |
|---|
| 1940 | sub exml |
|---|
| 1941 | { |
|---|
| 1942 | my $a = shift; |
|---|
| 1943 | $a =~ s/\&/&/g; |
|---|
| 1944 | $a =~ s/\"/"/g; |
|---|
| 1945 | $a =~ s/\'/'/g; |
|---|
| 1946 | $a =~ s/</</g; |
|---|
| 1947 | $a =~ s/>/>/g; |
|---|
| 1948 | return $a; |
|---|
| 1949 | } |
|---|
| 1950 | |
|---|
| 1951 | sub ehtml |
|---|
| 1952 | { |
|---|
| 1953 | my $a = $_[0]; |
|---|
| 1954 | $a =~ s/\&/&/g; |
|---|
| 1955 | $a =~ s/\"/"/g; |
|---|
| 1956 | $a =~ s/</</g; |
|---|
| 1957 | $a =~ s/>/>/g; |
|---|
| 1958 | return $a; |
|---|
| 1959 | } |
|---|
| 1960 | |
|---|
| 1961 | sub days_in_month |
|---|
| 1962 | { |
|---|
| 1963 | my ($month, $year) = @_; |
|---|
| 1964 | if ($month == 2) |
|---|
| 1965 | { |
|---|
| 1966 | if ($year % 4 == 0) |
|---|
| 1967 | { |
|---|
| 1968 | # years divisible by 400 are leap years |
|---|
| 1969 | return 29 if ($year % 400 == 0); |
|---|
| 1970 | |
|---|
| 1971 | # if they're divisible by 100, they aren't. |
|---|
| 1972 | return 28 if ($year % 100 == 0); |
|---|
| 1973 | |
|---|
| 1974 | # otherwise, if divisible by 4, they are. |
|---|
| 1975 | return 29; |
|---|
| 1976 | } |
|---|
| 1977 | } |
|---|
| 1978 | return ((31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$month-1]); |
|---|
| 1979 | } |
|---|
| 1980 | |
|---|
| 1981 | sub populate_web_menu { |
|---|
| 1982 | my ($res, $menu, $numref) = @_; |
|---|
| 1983 | my $mn = $$numref; # menu number |
|---|
| 1984 | my $mi = 0; # menu item |
|---|
| 1985 | foreach my $it (@$menu) { |
|---|
| 1986 | $mi++; |
|---|
| 1987 | $res->{"menu_${mn}_${mi}_text"} = $it->{'text'}; |
|---|
| 1988 | if ($it->{'text'} eq "-") { next; } |
|---|
| 1989 | if ($it->{'sub'}) { |
|---|
| 1990 | $$numref++; |
|---|
| 1991 | $res->{"menu_${mn}_${mi}_sub"} = $$numref; |
|---|
| 1992 | &populate_web_menu($res, $it->{'sub'}, $numref); |
|---|
| 1993 | next; |
|---|
| 1994 | |
|---|
| 1995 | } |
|---|
| 1996 | $res->{"menu_${mn}_${mi}_url"} = $it->{'url'}; |
|---|
| 1997 | } |
|---|
| 1998 | $res->{"menu_${mn}_count"} = $mi; |
|---|
| 1999 | } |
|---|
| 2000 | |
|---|
| 2001 | |
|---|
| 2002 | #### |
|---|
| 2003 | ### delete an itemid. if $quick is specified, that means items are being deleted en-masse |
|---|
| 2004 | ## and the batch deleter will take care of some of the stuff, so this doesn't have to |
|---|
| 2005 | # |
|---|
| 2006 | sub delete_item |
|---|
| 2007 | { |
|---|
| 2008 | my ($dbh, $ownerid, $itemid, $quick) = @_; |
|---|
| 2009 | my $sth; |
|---|
| 2010 | $ownerid += 0; |
|---|
| 2011 | $itemid += 0; |
|---|
| 2012 | |
|---|
| 2013 | $dbh->do("DELETE FROM hintlastnview WHERE itemid=$itemid") unless ($quick); |
|---|
| 2014 | $dbh->do("DELETE FROM memorable WHERE itemid=$itemid"); |
|---|
| 2015 | $dbh->do("UPDATE user SET lastitemid=0 WHERE userid=$ownerid AND lastitemid=$itemid") unless ($quick); |
|---|
| 2016 | $dbh->do("DELETE FROM log WHERE itemid=$itemid"); |
|---|
| 2017 | $dbh->do("DELETE FROM logtext WHERE itemid=$itemid"); |
|---|
| 2018 | $dbh->do("DELETE FROM logsubject WHERE itemid=$itemid"); |
|---|
| 2019 | $dbh->do("DELETE FROM logprop WHERE itemid=$itemid"); |
|---|
| 2020 | $dbh->do("DELETE FROM logsec WHERE ownerid=$ownerid AND itemid=$itemid"); |
|---|
| 2021 | |
|---|
| 2022 | my @talkids = (); |
|---|
| 2023 | $sth = $dbh->prepare("SELECT talkid FROM talk WHERE nodetype='L' AND nodeid=$itemid"); |
|---|
| 2024 | $sth->execute; |
|---|
| 2025 | while (my ($tid) = $sth->fetchrow_array) { |
|---|
| 2026 | push @talkids, $tid; |
|---|
| 2027 | } |
|---|
| 2028 | if (@talkids) { |
|---|
| 2029 | my $in = join(",", @talkids); |
|---|
| 2030 | $dbh->do("DELETE FROM talk WHERE talkid IN ($in)"); |
|---|
| 2031 | $dbh->do("DELETE FROM talktext WHERE talkid IN ($in)"); |
|---|
| 2032 | $dbh->do("DELETE FROM talkprop WHERE talkid IN ($in)"); |
|---|
| 2033 | } |
|---|
| 2034 | |
|---|
| 2035 | } |
|---|
| 2036 | |
|---|
| 2037 | 1; |
|---|