mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2012-05-01 06:31 am

[dw-free] Remove miscperl dependency by moving it local

[commit: http://hg.dwscoalition.org/dw-free/rev/7ed5651ee848]

Remove miscperl dependency by moving it local

Patch by [staff profile] mark.

Files modified:
  • cgi-bin/DBI/Role.pm
  • cgi-bin/HTMLCleaner.pm
  • cgi-bin/S2/Color.pm
  • cvs/multicvs.conf
--------------------------------------------------------------------------------
diff -r 2a10b7c4ccd1 -r 7ed5651ee848 cgi-bin/DBI/Role.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DBI/Role.pm	Tue May 01 06:32:50 2012 +0000
@@ -0,0 +1,441 @@
+#
+# This code was forked from the LiveJournal project owned and operated
+# by Live Journal, Inc. The code has been modified and expanded by
+# Dreamwidth Studios, LLC. These files were originally licensed under
+# the terms of the license supplied by Live Journal, Inc, which can
+# currently be found at:
+#
+# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
+#
+# In accordance with the original license, this code and all its
+# modifications are provided under the GNU General Public License.
+# A copy of that license can be found in the LICENSE file included as
+# part of this distribution.
+#
+
+package DBI::Role;
+
+use 5.006;
+use strict;
+use warnings;
+BEGIN {
+    $DBI::Role::HAVE_HIRES = eval "use Time::HiRes (); 1;";
+}
+
+our $VERSION = '1.00';
+
+# $self contains:
+#
+#  DBINFO --- hashref.  keys = scalar roles, one of which must be 'master'.
+#             values contain DSN info, and 'role' => { 'role' => weight, 'role2' => weight }
+#
+#  DEFAULT_DB -- scalar string.  default db name if none in DSN hashref in DBINFO
+#
+#  DBREQCACHE -- cleared by clear_req_cache() on each request.
+#                fdsn -> dbh
+#
+#  DBCACHE -- role -> fdsn, or
+#             fdsn -> dbh
+#
+#  DBCACHE_UNTIL -- role -> unixtime
+#
+#  DB_USED_AT -- fdsn -> unixtime
+#
+#  DB_DEAD_UNTIL -- fdsn -> unixtime
+#
+#  TIME_CHECK -- if true, time between localhost and db are checked every TIME_CHECK
+#                seconds
+#
+#  TIME_REPORT -- coderef to pass dsn and dbtime to after a TIME_CHECK occurence
+#
+
+sub new
+{
+    my ($class, $args) = @_;
+    my $self = {};
+    $self->{'DBINFO'} = $args->{'sources'};
+    $self->{'TIMEOUT'} = $args->{'timeout'};
+    $self->{'DEFAULT_DB'} = $args->{'default_db'};
+    $self->{'TIME_CHECK'} = $args->{'time_check'};
+    $self->{'TIME_LASTCHECK'} = {};  # dsn -> last check time
+    $self->{'TIME_REPORT'} = $args->{'time_report'};
+    bless $self, ref $class || $class;
+    return $self;
+}
+
+sub set_sources
+{
+    my ($self, $newval) = @_;
+    $self->{'DBINFO'} = $newval;
+    $self;
+}
+
+sub clear_req_cache
+{
+    my $self = shift;
+    $self->{'DBREQCACHE'} = {};
+}
+
+sub disconnect_all
+{
+    my ($self, $opts) = @_;
+    my %except;
+
+    if ($opts && $opts->{except} &&
+             ref $opts->{except} eq 'ARRAY') {
+        $except{$_} = 1 foreach @{$opts->{except}};
+    }
+
+    foreach my $cache (qw(DBREQCACHE DBCACHE)) {
+        next unless ref $self->{$cache} eq "HASH";
+        foreach my $key (keys %{$self->{$cache}}) {
+            next if $except{$key};
+            my $v = $self->{$cache}->{$key};
+            next unless ref $v eq "DBI::db";
+            $v->disconnect;
+            delete $self->{$cache}->{$key};
+        }
+    }
+    $self->{'DBCACHE'} = {};
+    $self->{'DBREQCACHE'} = {};
+}
+
+sub same_cached_handle
+{
+    my $self = shift;
+    my ($role_a, $role_b) = @_;
+    return
+        defined $self->{'DBCACHE'}->{$role_a} &&
+        defined $self->{'DBCACHE'}->{$role_b} &&
+        $self->{'DBCACHE'}->{$role_a} eq $self->{'DBCACHE'}->{$role_b};
+}
+
+sub flush_cache
+{
+    my $self = shift;
+    foreach (keys %{$self->{'DBCACHE'}}) {
+        my $v = $self->{'DBCACHE'}->{$_};
+        next unless ref $v;
+        $v->disconnect;
+    }
+    $self->{'DBCACHE'} = {};
+    $self->{'DBREQCACHE'} = {};
+}
+
+# old interface.  does nothing now.
+sub trigger_weight_reload
+{
+    my $self = shift;
+    return $self;
+}
+
+sub use_diff_db
+{
+    my $self = shift;
+    my ($role1, $role2) = @_;
+
+    return 0 if $role1 eq $role2;
+
+    # this is implied:  (makes logic below more readable by forcing it)
+    $self->{'DBINFO'}->{'master'}->{'role'}->{'master'} = 1;
+
+    foreach (keys %{$self->{'DBINFO'}}) {
+        next if /^_/;
+        next unless ref $self->{'DBINFO'}->{$_} eq "HASH";
+        if ($self->{'DBINFO'}->{$_}->{'role'}->{$role1} &&
+            $self->{'DBINFO'}->{$_}->{'role'}->{$role2}) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
+sub get_dbh
+{
+    my $self = shift;
+    my $opts = ref $_[0] eq "HASH" ? shift : {};
+
+    my @roles = @_;
+    my $role = shift @roles;
+    return undef unless $role;
+
+    my $now = time();
+
+    # if 'nocache' flag is passed, clear caches now so we won't return
+    # a cached database handle later
+    $self->clear_req_cache if $opts->{'nocache'};
+
+    # otherwise, see if we have a role -> full DSN mapping already
+    my ($fdsn, $dbh);
+    if ($role eq "master") {
+        $fdsn = make_dbh_fdsn($self, $self->{'DBINFO'}->{'master'});
+    } else {
+        if ($self->{'DBCACHE'}->{$role} && ! $opts->{'unshared'}) {
+            $fdsn = $self->{'DBCACHE'}->{$role};
+            if ($now > $self->{'DBCACHE_UNTIL'}->{$role}) {
+                # this role -> DSN mapping is too old.  invalidate,
+                # and while we're at it, clean up any connections we have
+                # that are too idle.
+                undef $fdsn;
+
+                foreach (keys %{$self->{'DB_USED_AT'}}) {
+                    next if $self->{'DB_USED_AT'}->{$_} > $now - 60;
+                    delete $self->{'DB_USED_AT'}->{$_};
+                    delete $self->{'DBCACHE'}->{$_};
+                }
+            }
+        }
+    }
+
+    if ($fdsn) {
+        $dbh = $self->get_dbh_conn( $opts, $fdsn, $role );
+        return $dbh if $dbh;
+        delete $self->{'DBCACHE'}->{$role};  # guess it was bogus
+    }
+    return undef if $role eq "master";  # no hope now
+
+    # time to randomly weightedly select one.
+    my @applicable;
+    my $total_weight;
+    foreach (keys %{$self->{'DBINFO'}}) {
+        next if /^_/;
+        next unless ref $self->{'DBINFO'}->{$_} eq "HASH";
+        my $weight = $self->{'DBINFO'}->{$_}->{'role'}->{$role};
+        next unless $weight;
+        push @applicable, [ $self->{'DBINFO'}->{$_}, $weight ];
+        $total_weight += $weight;
+    }
+
+    while (@applicable) {
+        my $rand = rand($total_weight);
+        my ($i, $t) = (0, 0);
+        for (; $i<@applicable; $i++) {
+            $t += $applicable[$i]->[1];
+            last if $t > $rand;
+        }
+        my $fdsn = make_dbh_fdsn($self, $applicable[$i]->[0]);
+        $dbh = $self->get_dbh_conn( $opts, $fdsn );
+        if ($dbh) {
+            $self->{'DBCACHE'}->{$role} = $fdsn;
+            $self->{'DBCACHE_UNTIL'}->{$role} = $now + 5 + int(rand(10));
+            return $dbh;
+        }
+
+        # otherwise, discard that one.
+        $total_weight -= $applicable[$i]->[1];
+        splice(@applicable, $i, 1);
+    }
+
+    # try others
+    return get_dbh($self, $opts, @roles);
+}
+
+sub make_dbh_fdsn
+{
+    my $self = shift;
+    my $db = shift;   # hashref with DSN info
+    return $db->{'_fdsn'} if $db->{'_fdsn'};  # already made?
+
+    my $fdsn = "DBI:mysql";  # join("|",$dsn,$user,$pass) (because no refs as hash keys)
+    $db->{'dbname'} ||= $self->{'DEFAULT_DB'} if $self->{'DEFAULT_DB'};
+    $fdsn .= ":$db->{'dbname'}";
+    $fdsn .= ";host=$db->{'host'}" if $db->{'host'};
+    $fdsn .= ";port=$db->{'port'}" if $db->{'port'};
+    $fdsn .= ";mysql_socket=$db->{'sock'}" if $db->{'sock'};
+    $fdsn .= "|$db->{'user'}|$db->{'pass'}";
+
+    $db->{'_fdsn'} = $fdsn;
+    return $fdsn;
+}
+
+sub get_dbh_conn
+{
+    my $self = shift;
+    my $opts = ref $_[0] eq "HASH" ? shift : {};
+    my $fdsn = shift;
+    my $role = shift;  # optional.
+    my $now = time();
+
+    my $retdb = sub {
+        my $db = shift;
+        $self->{'DBREQCACHE'}->{$fdsn} = $db;
+        $self->{'DB_USED_AT'}->{$fdsn} = $now;
+        return $db;
+    };
+
+    # have we already created or verified a handle this request for this DSN?
+    return $retdb->($self->{'DBREQCACHE'}->{$fdsn})
+        if $self->{'DBREQCACHE'}->{$fdsn} && ! $opts->{'unshared'};
+
+    # check to see if we recently tried to connect to that dead server
+    return undef if $self->{'DB_DEAD_UNTIL'}->{$fdsn} && $now < $self->{'DB_DEAD_UNTIL'}->{$fdsn};
+
+    # if not, we'll try to find one we used sometime in this process lifetime
+    my $dbh = $self->{'DBCACHE'}->{$fdsn};
+
+    # if it exists, verify it's still alive and return it.  (but not
+    # if we're wanting an unshared connection)
+    if ($dbh && ! $opts->{'unshared'}) {
+        return $retdb->($dbh) unless connection_bad($dbh, $opts);
+        undef $dbh;
+        undef $self->{'DBCACHE'}->{$fdsn};
+    }
+
+    # time to make one!
+    my ($dsn, $user, $pass) = split(/\|/, $fdsn);
+    my $timeout = $self->{'TIMEOUT'} || 2;
+    if (ref $timeout eq "CODE") {
+        $timeout = $timeout->($dsn, $user, $pass, $role);
+    }
+    $dsn .= ";mysql_connect_timeout=$timeout" if $timeout;
+
+    my $loop = 1;
+    my $tries = $DBI::Role::HAVE_HIRES ? 8 : 2;
+    while ($loop) {
+        $loop = 0;
+
+        my $connection_opts;
+        if ( $opts->{'connection_opts'} ) {
+            $connection_opts = $opts->{'connection_opts'};
+        } else {
+            $connection_opts = {
+                PrintError => 0,
+                AutoCommit => 1,
+            };
+        }
+
+        $dbh = DBI->connect( $dsn, $user, $pass, $connection_opts );
+
+        $dbh->{private_role} = $role if $dbh;
+
+        # if max connections, try again shortly.
+        if (! $dbh && $DBI::err == 1040 && $tries) {
+            $tries--;
+            $loop = 1;
+            if ($DBI::Role::HAVE_HIRES) {
+                Time::HiRes::usleep(250_000);
+            } else {
+                sleep 1;
+            }
+            next;
+        }
+
+        # if lost connection to server (had prior connection?) error
+        # (MySQL server has gone away)
+        if (! $dbh && $DBI::err == 2013 && $tries) {
+            $tries--;
+            $loop = 1;
+            next;
+        }
+    }
+
+    my $DBI_err = $DBI::err || 0;
+    if ($DBI_err && $DBI::Role::VERBOSE) {
+        $role ||= "";
+        my $str = $DBI::errstr || "(no DBI::errstr)";
+        print STDERR "DBI::Role connect error $DBI_err for role '$role': dsn='$dsn', user='$user': $str\n";
+    }
+
+
+    # check replication/busy processes... see if we should not use
+    # this one
+    undef $dbh if connection_bad($dbh, $opts);
+    # mark server as dead if dead.  won't try to reconnect again for 5 seconds.
+    if ($dbh) {
+        # default wait_timeout is 60 seconds.
+        $dbh->do("SET SESSION wait_timeout = 600");
+        
+        # if this is an unshared connection, we don't want to put it
+        # in the cache for somebody else to use later. (which happens below)
+        return $dbh if $opts->{'unshared'};
+
+        $self->{'DB_USED_AT'}->{$fdsn} = $now;
+        if ($self->{'TIME_CHECK'} && ref $self->{'TIME_REPORT'} eq "CODE") {
+            my $now = time();
+            $self->{'TIME_LASTCHECK'}->{$dsn} ||= 0;  # avoid warnings
+            if ($self->{'TIME_LASTCHECK'}->{$dsn} < $now - $self->{'TIME_CHECK'}) {
+                $self->{'TIME_LASTCHECK'}->{$dsn} = $now;
+                my $db_time = $dbh->selectrow_array("SELECT UNIX_TIMESTAMP()");
+                $self->{'TIME_REPORT'}->($dsn, $db_time, $now);
+            }
+        }
+    } else {
+        # mark the database as dead for a bit, unless it was just because of max connections
+        $self->{'DB_DEAD_UNTIL'}->{$fdsn} = $now + 5
+            unless $DBI_err == 1040 || $DBI_err == 2013;
+
+    }
+
+    return $self->{'DBREQCACHE'}->{$fdsn} = $self->{'DBCACHE'}->{$fdsn} = $dbh;
+}
+
+sub connection_bad {
+    my ($dbh, $opts) = @_;
+
+    return 1 unless $dbh;
+
+    my $ss = eval {
+        $dbh->selectrow_hashref("SHOW SLAVE STATUS");
+    };
+
+    # if there was an error, and it wasn't a permission problem (1227)
+    # then treat this connection as bogus
+    if ($dbh->err && $dbh->err != 1227) {
+        return 1;
+    }
+
+    # connection is good if $ss is undef (not a slave)
+    return 0 unless $ss;
+
+    # otherwise, it's okay if not MySQL 4
+    return 0 if ! $ss->{'Master_Log_File'} || ! $ss->{'Relay_Master_Log_File'};
+
+    # all good if within 100 k
+    if ($opts->{'max_repl_lag'}) {
+        # MySQL 4.0 uses Exec_master_log_pos, 5.0 uses Exec_Master_Log_Pos
+        my $emlp = $ss->{'Exec_master_log_pos'} || $ss->{'Exec_Master_Log_Pos'} || undef;
+        return 0 if
+            $ss->{'Master_Log_File'} eq $ss->{'Relay_Master_Log_File'} &&
+            ($ss->{'Read_Master_Log_Pos'} - $emlp) < $opts->{'max_repl_lag'};
+
+
+        # guess we're behind
+        return 1;
+    } else {
+        # default to assuming it's good
+        return 0;
+    }
+}
+
+1;
+__END__
+
+=head1 NAME
+
+DBI::Role - Get DBI cached handles by role, with weighting & failover.
+
+=head1 SYNOPSIS
+
+  use DBI::Role;
+  my $DBIRole = new DBI::Role {
+    'sources' => \%DBINFO,
+    'default_db' => "somedbname", # opt.
+  };
+  my $dbh = $DBIRole->get_dbh("master");
+
+=head1 DESCRIPTION
+
+To be written.
+
+=head2 EXPORT
+
+None by default.
+
+=head1 AUTHOR
+
+Brad Fitzparick, E<lt>brad@danga.comE<gt>
+
+=head1 SEE ALSO
+
+L<DBI>.
+
diff -r 2a10b7c4ccd1 -r 7ed5651ee848 cgi-bin/HTMLCleaner.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/HTMLCleaner.pm	Tue May 01 06:32:50 2012 +0000
@@ -0,0 +1,228 @@
+#!/usr/bin/perl
+#
+# This code was forked from the LiveJournal project owned and operated
+# by Live Journal, Inc. The code has been modified and expanded by
+# Dreamwidth Studios, LLC. These files were originally licensed under
+# the terms of the license supplied by Live Journal, Inc, which can
+# currently be found at:
+#
+# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
+#
+# In accordance with the original license, this code and all its
+# modifications are provided under the GNU General Public License.
+# A copy of that license can be found in the LICENSE file included as
+# part of this distribution.
+#
+
+package HTMLCleaner;
+
+use strict;
+use base 'HTML::Parser';
+use CSS::Cleaner;
+
+sub new {
+    my ($class, %opts) = @_;
+
+    my $p = new HTML::Parser(
+        'api_version'   => 3,
+        'start_h'       => [ \&start, 'self, tagname, attr, attrseq, text' ],
+        'end_h'         => [ \&end, 'self, tagname' ],
+        'text_h'        => [ \&text, 'self, text' ],
+        'declaration_h' => [ \&decl, 'self, tokens' ],
+    );
+
+    $p->{'output'}               = $opts{'output'} || sub {};
+    $p->{'cleaner'}              = CSS::Cleaner->new;
+    $p->{'valid_stylesheet'}     = $opts{'valid_stylesheet'} || sub { 1 };
+    $p->{'allow_password_input'} = $opts{'allow_password_input'} || 0;
+
+    $p->utf8_mode(1);
+
+    $p->{'eat_tag'} = { map { $_ => 1 }
+               qw(script object iframe applet embed param) };
+
+    ## Enabling tag 'iframe' if need
+    delete $p->{'eat_tag'}->{'iframe'} if $opts{'enable_iframe'};
+
+    bless $p, $class;
+}
+
+my %bad_attr = (map { $_ => 1 }
+                qw(datasrc datafld));
+
+my @eating;  # push tagname whenever we start eating a tag
+
+sub start {
+    my ($self, $tagname, $attr, $seq, $text) = @_;
+    $tagname =~ s/<//;
+
+    my $slashclose = 0;  # xml-style
+    if ($tagname =~ s!/(.*)!!) {
+        if (length($1)) { push @eating, "$tagname/$1"; } # basically halt parsing
+        else { $slashclose = 1; }
+    }
+
+    my @allowed_tags = ('lj-embed');
+
+    push @eating, $tagname
+        if ( $self->{'eat_tag'}->{$tagname} && ! grep { lc $tagname eq $_ } @allowed_tags )
+        || $tagname =~ /^(?:g|fb):/;
+
+    return if @eating;
+
+    my $clean_res = eval {
+        my $cleantag = $tagname;
+        $cleantag =~ s/^.*://s;
+        $cleantag =~ s/[^\w]//g;
+        no strict 'subs';
+        my $meth = "CLEAN_$cleantag";
+        my $code = $self->can($meth)
+            or return 1;  # don't clean, if no element-specific cleaner method
+        return $code->($self, $seq, $attr);
+    };
+    return if !$@ && !$clean_res;
+
+    my $ret = "<$tagname";
+    foreach (@$seq) {
+        if ($_ eq "/") { $slashclose = 1; next; }
+        next if $bad_attr{lc($_)};
+        next if /^on/i;
+        next if /(?:^=)|[\x0b\x0d]/;
+
+        if ($_ eq "style") {
+            $attr->{$_} = $self->{cleaner}->clean_property($attr->{$_});
+        }
+
+        if ($tagname eq 'input' && $_ eq 'type'  && $attr->{'type'} =~ /^password$/i && !$self->{'allow_password_input'}) {
+            delete $attr->{'type'};
+        }
+
+        my $nospace = $attr->{$_};
+        $nospace =~ s/[\s\0]//g;
+
+        # IE is brain-dead and lets javascript:, vbscript:, and about: have spaces mixed in
+        if ($nospace =~ /(?:(?:(?:vb|java)script)|about):/i) {
+            delete $attr->{$_};
+        }
+        $ret .= " $_=\"" . ehtml($attr->{$_}) . "\"";
+    }
+    $ret .= " /" if $slashclose;
+    $ret .= ">";
+
+    if ($tagname eq "style") {
+        $self->{'_eating_style'} = 1;
+        $self->{'_style_contents'} = "";
+    }
+
+    $self->{'output'}->($ret);
+}
+
+sub CLEAN_meta {
+    my ($self, $seq, $attr) = @_;
+
+    # don't allow refresh because it can refresh to javascript URLs
+    # don't allow content-type because they can set charset to utf-7
+    # why do we even allow meta tags?
+    my $equiv = lc $attr->{"http-equiv"};
+    if ($equiv) {
+        $equiv =~ s/[\s\x0b]//;
+        return 0 if $equiv =~ /refresh|content-type|link|set-cookie/;
+    }
+    return 1;
+}
+
+sub CLEAN_link {
+    my ($self, $seq, $attr) = @_;
+
+    if ($attr->{rel} =~ /\bstylesheet\b/i) {
+        my $href = $attr->{href};
+        return 0 unless $href =~ m!^https?://([^/]+?)(/.*)$!;
+        my ($host, $path) = ($1, $2);
+
+        my $rv = $self->{'valid_stylesheet'}->($href, $host, $path);
+        if ($rv == 1) {
+            return 1;
+        }
+        if ($rv) {
+            $attr->{href} = $rv;
+            return 1;
+        }
+        return 0;
+    }
+
+    # Allow blank <link> tags through so RSS S2 styles can work again without the 'rel="alternate"' hack
+    return 1 if (keys( %$attr ) == 0);
+
+    return 1 if $attr->{rel} =~ /^(?:service|openid)\.\w+$/;
+    my %okay = map { $_ => 1 } (qw(icon shortcut alternate next prev index made start search top help up author edituri file-list previous home contents bookmark chapter section subsection appendix glossary copyright child));
+    return 1 if $okay{lc($attr->{rel})};
+
+    # Allow link tags with only an href tag. This is an implied rel="alternate"
+    return 1 if (exists( $attr->{href} ) and (keys( %$attr ) == 1));
+
+    # Allow combinations of rel attributes through as long as all of them are valid, most notably "shortcut icon"
+    return 1 unless grep { !$okay{$_} } split( /\s+/, $attr->{rel} );
+
+    # unknown link tag
+    return 0;
+}
+
+sub end {
+    my ($self, $tagname) = @_;
+    if (@eating) {
+        pop @eating if $eating[-1] eq $tagname;
+        return;
+    }
+
+    if ($self->{'_eating_style'}) {
+        $self->{'_eating_style'} = 0;
+        $self->{'output'}->($self->{cleaner}->clean($self->{'_style_contents'}));
+    }
+
+    $self->{'output'}->("</$tagname>");
+}
+
+sub text {
+    my ($self, $text) = @_;
+    return if @eating;
+
+    if ($self->{'_eating_style'}) {
+        $self->{'_style_contents'} .= $text;
+        return;
+    }
+
+    # this string is magic [hack].  (See $out_straight in
+    # cgi-bin/LJ/S2.pm) callers can print "<!-- -->" to HTML::Parser
+    # just to make it flush, since HTML::Parser has no
+    # ->flush_outstanding text tag.
+    return if $text eq "<!-- -->";
+
+    # the parser gives us back text whenever it's confused
+    # on really broken input.  sadly, IE parses really broken
+    # input, so let's escape anything going out this way.
+    $self->{'output'}->(eangles($text));
+}
+
+sub decl {
+    my ($self, $tokens) = @_;
+    $self->{'output'}->("<!" . join(" ", map { eangles($_) } @$tokens) . ">");
+}
+
+sub eangles {
+    my $a = shift;
+    $a =~ s/</&lt;/g;
+    $a =~ s/>/&gt;/g;
+    return $a;
+}
+
+sub ehtml {
+    my $a = shift;
+    $a =~ s/\&/&amp;/g;
+    $a =~ s/\"/&quot;/g;
+    $a =~ s/\'/&\#39;/g;
+    $a =~ s/</&lt;/g;
+    $a =~ s/>/&gt;/g;
+    return $a;
+}
+
+1;
diff -r 2a10b7c4ccd1 -r 7ed5651ee848 cgi-bin/S2/Color.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/S2/Color.pm	Tue May 01 06:32:50 2012 +0000
@@ -0,0 +1,176 @@
+#
+# This code was forked from the LiveJournal project owned and operated
+# by Live Journal, Inc. The code has been modified and expanded by
+# Dreamwidth Studios, LLC. These files were originally licensed under
+# the terms of the license supplied by Live Journal, Inc, which can
+# currently be found at:
+#
+# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
+#
+# In accordance with the original license, this code and all its
+# modifications are provided under the GNU General Public License.
+# A copy of that license can be found in the LICENSE file included as
+# part of this distribution.
+#
+
+package S2::Color;
+use strict;
+
+# This is a helper package, useful for creating color lightening/darkening
+# functions in core layers.
+#
+
+# rgb to hsv
+# r, g, b = [0, 255]
+# h, s, v = [0, 1), [0, 1], [0, 1]
+sub rgb_to_hsv
+{
+    my ($r, $g, $b) = map { $_ / 255 } @_;
+    my ($h, $s, $v);
+
+    my ($max, $min) = ($r, $r);
+    foreach ($g, $b) {
+        $max = $_ if $_ > $max;
+        $min = $_ if $_ < $min;
+    }
+    return (0, 0, 0) if $max == 0;
+
+    $v = $max;
+
+    my $delta = $max - $min;
+
+    $s = $delta / $max;
+    return (0, $s, $v) unless $delta;
+
+    if ($r == $max) {
+        $h = ($g - $b) / $delta;
+    } elsif ($g == $max) {
+        $h = 2 + ($b - $r) / $delta;
+    } else {
+        $h = 4 + ($r - $g) / $delta;
+    }
+
+    $h = ($h * 60) % 360 / 360;
+    
+    return ($h, $s, $v);
+}
+
+# hsv to rgb
+# h, s, v = [0, 1), [0, 1], [0, 1]
+# r, g, b = [0, 255], [0, 255], [0, 255]
+sub hsv_to_rgb
+{
+    my ($H, $S, $V) = @_;
+
+    if ($S == 0) {
+        $V *= 255;
+        return ($V, $V, $V);
+    }
+
+    $H *= 6;
+    my $I = POSIX::floor($H);
+
+    my $F = $H - $I;
+    my $P = $V * (1 - $S);
+    my $Q = $V * (1 - $S * $F);
+    my $T = $V * (1 - $S * (1 - $F));
+
+    foreach ($V, $T, $P, $Q) {
+        $_ = int($_ * 255 + 0.5);
+    }
+
+    return ($V, $T, $P) if $I == 0;
+    return ($Q, $V, $P) if $I == 1;
+    return ($P, $V, $T) if $I == 2;
+    return ($P, $Q, $V) if $I == 3;
+    return ($T, $P, $V) if $I == 4;
+
+    return ($V, $P, $Q);
+}
+
+# rgb to hsv
+# r, g, b = [0, 255], [0, 255], [0, 255]
+# returns: (h, s, l) = [0, 1), [0, 1], [0, 1]
+sub rgb_to_hsl
+{
+    # convert rgb to 0-1
+    my ($R, $G, $B) = map { $_ / 255 } @_;
+
+    # get min/max of {r, g, b}
+    my ($max, $min) = ($R, $R);
+    foreach ($G, $B) {
+        $max = $_ if $_ > $max;
+        $min = $_ if $_ < $min;
+    }
+
+    # is gray?
+    my $delta = $max - $min;
+    if ($delta == 0) {
+        return (0, 0, $max);
+    }
+
+    my ($H, $S);
+    my $L = ($max + $min) / 2;
+    
+    if ($L < 0.5) {
+        $S = $delta / ($max + $min);
+    } else {
+        $S = $delta / (2.0 - $max - $min);
+    }
+
+    if ($R == $max) {
+        $H = ($G - $B) / $delta;
+    } elsif ($G == $max) {
+        $H = 2 + ($B - $R) / $delta;
+    } elsif ($B == $max) {
+        $H = 4 + ($R - $G) / $delta;
+    }
+
+    $H *= 60;
+    $H += 360.0 if $H < 0.0;
+    $H -= 360.0 if $H >= 360.0;
+    $H /= 360.0;
+
+    return ($H, $S, $L);
+
+}
+
+# h, s, l = [0,1), [0,1], [0,1]
+# returns: rgb: [0,255], [0,255], [0,255]
+sub hsl_to_rgb {
+    my ($H, $S, $L) = @_;
+
+    # gray.
+    if ($S < 0.0000000000001) {
+        my $gv = int(255 * $L + 0.5);
+        return ($gv, $gv, $gv);
+    }
+
+    my ($t1, $t2);
+    if ($L < 0.5) {
+        $t2 = $L * (1.0 + $S);
+    } else {
+        $t2 = $L + $S - $L * $S;
+    }
+    $t1 = 2.0 * $L - $t2;
+
+    my $fromhue = sub {
+        my $hue = shift;
+        if ($hue < 0) { $hue += 1.0; }
+        if ($hue > 1) { $hue -= 1.0; }
+
+        if (6.0 * $hue < 1) {
+            return $t1 + ($t2 - $t1) * $hue * 6.0;
+        } elsif (2.0 * $hue < 1) {
+            return $t2;
+        } elsif (3.0 * $hue < 2.0) {
+            return ($t1 + ($t2 - $t1)*((2.0/3.0)-$hue)*6.0);
+        } else {
+            return $t1;
+        }
+    };
+
+    return map { int(255 * $fromhue->($_) + 0.5) } ($H + 1.0/3.0, $H, $H - 1.0/3.0);
+}
+
+1;
diff -r 2a10b7c4ccd1 -r 7ed5651ee848 cvs/multicvs.conf
--- a/cvs/multicvs.conf	Tue May 01 06:27:29 2012 +0000
+++ b/cvs/multicvs.conf	Tue May 01 06:32:50 2012 +0000
@@ -26,7 +26,6 @@
 GIT(Data-ObjectDriver)    = git://github.com/saymedia/data-objectdriver.git
 #SVN(openid)               = http://code.livejournal.org/svn/openid/trunk/
 SVN(ddlockd)              = http://code.livejournal.org/svn/ddlockd/trunk/
-SVN(miscperl)             = http://code.livejournal.org/svn/miscperl/trunk/
 SVN(LJ-UserSearch)        = http://code.livejournal.org/svn/LJ-UserSearch/trunk/
 SVN(TheSchwartz-Worker-SendEmail) = http://code.sixapart.com/svn/TheSchwartz-Worker-SendEmail/trunk/
 SVN(hubbub)               = http://pubsubhubbub.googlecode.com/svn/trunk/publisher_clients/
@@ -51,11 +50,6 @@
 js/                                           htdocs/js
 js/ImageRegionSelect/image-region-select.js   htdocs/js/image-region-select.js
 
-miscperl/DBI/Role.pm                          cgi-bin/DBI/Role.pm
-miscperl/HTMLCleaner.pm                       cgi-bin/HTMLCleaner.pm
-miscperl/S2/Color.pm                          cgi-bin/S2/Color.pm
-miscperl/Danga-Daemon/Daemon.pm               cgi-bin/Danga/Daemon.pm
-
 s2/                                           src/s2
 s2/doc/docbook                                doc/raw/s2
 
--------------------------------------------------------------------------------

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org