#!/usr/bin/perl -T -w # # Good Sex For Mutants CGI # # Copyright 2003-2005 Deekoo L., all rights lefts and other directions reserved. # This thing is licensed under the GNU GPL, version 2. # use GDBM_File; use Fcntl ':flock'; use Math::Trig qw(great_circle_distance deg2rad pi); # $distance = great_circle_distance($theta0, $phi0, $theta1, $phi1, [, $rho]); my $DATAPATH = '/home/deekoo/var/gsfm'; my $STATICDATAPATH = '/home/deekoo/lib/gsfm'; my $BODYATTRIBS = 'bgcolor="#000000" text="#9966cc" link="#ffffff" '. 'vlink="#cccccc" alink="ff0000" style="background-repeat: no-repeat; '. 'background: url(../../images/eye_of_rlyeh.jpg) right bottom;"'; my $STYLESHEET=''; my (@MINOR_ERRORS, $PASSWORD, $USER_ID, @attribute_descs, @attribute_headers, $query); use CGI; $query = new CGI; $action = $query->param('action'); # DEBUG STUFF - figure out why latlongs are changing. $ENV{PATH} = '/bin:/usr/bin'; my $time = localtime(time()); $time=~s/ /_/gs; $time=~s/:/./gs; $thingy=`tar cfz /home/deekoo/var/gsfm-backup/$time.tar.gz $DATAPATH 2>/dev/null`; $thingy.=''; # And silence a warning. # END DEBUG STUFF if (!defined($action)) { &fatalerror("Cannot build fnordage. No action specified.\n"); } elsif ($action eq 'create') { &do_create(); } elsif ($action eq 'login') { &do_login(); } elsif ($action eq 'search') { &search(); } elsif ($action eq 'set_other_attributes') { &set_other_attributes(); } elsif ($action eq 'show_other_attributes') { &show_other_attributes(); } elsif ($action eq 'set_your_attributes') { &set_your_attributes(); } elsif ($action eq 'set_your_profile') { &fatalerror("XXX set_profile unwritten.\n"); } elsif ($action eq 'show_your_attributes') { &show_your_attributes(); #} elsif ($action eq 'show_your_profile') { # &show_your_profile(); #} elsif ($action eq 'show_your_location') { # &show_your_location(); } elsif ($action eq 'view_profile') { &view_profile(); } else { &fatalerror("No wife, no horse, no moustache, no code for action $action.\n"); } exit(0); #sub show_your_location() #{ # print "Content-type: text/html\012\015\012\015
\n"; # print "",$PROFILES{"desc_$other"},"\n"; } else { print "
[No profile set]
\n"; } if (exists($PROFILES{"long_$USER_ID"}) && exists($PROFILES{"long_$other"})) { my $distrad = great_circle_distance( deg2rad($PROFILES{"long_$USER_ID"}), pi/2-deg2rad($PROFILES{"lat_$USER_ID"}), deg2rad($PROFILES{"long_$other"}), pi/2-deg2rad($PROFILES{"lat_$other"})); my $distnm = $distrad/0.00029088820866572159; my $distsm = $distnm*1.15077945; my $distkm = $distsm*1.609344; print "Distance: $distsm miles, or $distkm kilometers, or ", $distkm*1000/1024, " kibometers. (A kibometer is 1024 meters.)"; } else { print "Cannot calculate distance - you and/or $other don't have a location specified.
\n"; } &show_command_footer(); print "\n"; } sub consider_match() { # No need to consider people with no self-description whatsoever. exists($ATTRIBUTES_DB{$_[0]}) or return; my @their_attributes = split(/,/s, $ATTRIBUTES_DB{$_[0]}); my @their_other_attributes = split(/,/s, $OTHER_ATTRIBUTES_DB{"_$_[0]"}); my @their_other_attributes_forbidden = split(/,/s, $OTHER_ATTRIBUTES_DB{"f_$_[0]"}); my @their_other_attributes_required = split(/,/s, $OTHER_ATTRIBUTES_DB{"r_$_[0]"}); my $x = 0; my $their_forbidden_unclear = 0; my $forbidden_unclear = 0; my $forbidden_absent = 0; my $required_unclear = 0; my $required_present = 0; # my $desirable_absent = 0; # my $desirable_present = 0; # my $undesirable_absent = 0; # my $undesirable_present = 0; my $desirability = 0; my $your_mandatory_score = 0; my $your_mandatory_uncertainty = 0; my $your_desirability = 0; while ($x < $#attribute_descs) { $their_attributes[$x] = 0 if !defined($their_attributes[$x]); $their_other_attributes[$x] = 0 if !defined($their_other_attributes[$x]); $your_attributes[$x] = 0 if !defined($your_attributes[$x]); $other_attributes[$x] = 0 if !defined($other_attributes[$x]); if ($other_attributes_forbidden[$x]) { if ($their_attributes[$x]==1) { # They have something the user doesn't want. return; } elsif ($their_attributes[$x] == 0) { $forbidden_unclear++; } else { # It's a trinary; 0, 1, and -1 should be the only allowed # entries. # Assume they don't have it. $forbidden_absent++; } } elsif ($other_attributes_required[$x]) { # We'll assume that the user doesn't both require and forbid something. # Users who find a way to deserve unreliability. if ($their_attributes[$x] == -1) { return; } elsif ($their_attributes[$x] == 1) { $required_present++; } else { $required_unclear++; } } else { # It's only desirable/non. $desirability += $their_attributes[$x] * $other_attributes[$x]; # if ($other_attributes[$x] == 1) { # if ($their_attributes[$x]==1) { # $desirable_present++; # } elsif ($their_attributes[$x]==-1 { # $desirable_absent++; # } # } elsif ($other_attributes[$x] == -1) { # if ($their_attributes[$x]==1) { # $undesirable_present++; # } elsif ($their_attributes[$x]==-1) { # $undesirable_absent++; # } # } } if ($their_other_attributes_forbidden[$x]) { if ($your_attributes[$x]==1) { return; # They don't want you. } elsif ($your_attributes[$x]==0) { $your_mandatory_uncertainty++; } else { $your_mandatory_score++; } } elsif ($their_other_attributes_required[$x]) { if ($your_attributes[$x]==-1) { return; } elsif ($your_attributes[$x]==0) { $your_mandatory_uncertainty++; } else { $your_mandatory_score++; } } else { $your_desirability += $your_attributes[$x] * $their_other_attributes[$x]; } $x++; } $MANDATORY_SCORE{$_[0]} = $forbidden_absent + $required_present; $YOUR_MANDATORY_SCORE{$_[0]} = $your_mandatory_score; $MANDATORY_UNCERTAINTY{$_[0]} = $forbidden_unclear+$required_unclear; $YOUR_MANDATORY_UNCERTAINTY{$_[0]} = $your_mandatory_uncertainty; $DESIRABILITY{$_[0]} = $desirability; $YOUR_DESIRABILITY{$_[0]} = $your_desirability; } sub score($) { # Returns the score # XXX does not yet factor in distance. return ( $MANDATORY_SCORE{$_[0]}**2 - $MANDATORY_UNCERTAINTY{$_[0]}**2) + $DESIRABILITY{$_[0]} + ( $YOUR_MANDATORY_SCORE{$_[0]}**2 - $YOUR_MANDATORY_UNCERTAINTY{$_[0]}**2) + $YOUR_DESIRABILITY{$_[0]}; } #sub lat($) #{ # if ($_[0] < 0) { # return (0-$_[0]).'S'; # } # return "$_[0]N"; #} # #sub long($) #{ # if ($_[0] < 0) { # return (0-$_[0]).'W'; # } # return "$_[0]E"; #} sub arc_string($$$) { my ($deg,$pos,$neg) = @_; my $ideg = int($deg); my $ret = abs($ideg); if ($deg != $ideg) { my $minsec = abs($deg-$ideg); my $mins = int(sprintf("%.6g",$minsec*60)); $ret .= " ".$mins."'"; if ($mins!=($minsec*60)) { my $secs = int(sprintf("%.1g",(($minsec*60)-$mins)*60)); $ret .= " ".$secs."''" if $secs; } } if ($deg>0) { $ret .= $_[1]; } elsif ($deg<0) { $ret .= $_[2]; } return $ret; } sub long_string($) { return arc_string($_[0],'E','W'); } sub lat_string($) { return arc_string($_[0],'N','S'); } sub latlong_and_distance($) { my $other = $_[0]; my $ret=''; if (exists($PROFILES{"lat_$other"})) { $ret .= lat_string($PROFILES{"lat_$other"}). ' '. long_string($PROFILES{"long_$other"}); } else { return '[unknown]'; } if (exists($PROFILES{"lat_$USER_ID"})) { my $distrad = great_circle_distance( deg2rad($PROFILES{"long_$USER_ID"}), pi/2-deg2rad($PROFILES{"lat_$USER_ID"}), deg2rad($PROFILES{"long_$other"}), pi/2-deg2rad($PROFILES{"lat_$other"})); my $distnm = $distrad/0.00029088820866572159; my $distsm = int($distnm*1.15077945); $ret .= "(Searching using your existing preference settings.)
\n"; my @keys = keys(%ATTRIBUTES_DB); my $key; my $x; for $key (@keys) { &consider_match($key); } &sort_and_show_matches(); print "\n"; dbmclose(%ATTRIBUTES_DB); &unlock_data_file("attributes.db"); dbmclose(%OTHER_ATTRIBUTES_DB); &unlock_data_file("other-attributes.db"); } sub option_requirement_count() { my $x=0; my $ret = 0; while ($x<$#other_attributes) { if ($other_attributes[$x]) { $ret++; } $x++; } return $ret; } sub sanity_check_profile() { my $opt = &option_requirement_count(); $your_profile{'minopt'} = 0 if !defined($your_profile{'minopt'}); if ($your_profile{'minopt'}>$opt) { push(@MINOR_ERRORS, "Minimum optional desirability exceeds your optional desirability range.\n Fixing."); $your_profile{'minopt'} = $opt; } if ($your_profile{'desc'}=~s/[/gs) { push(@MINOR_ERRORS,"There were < characters in your description. They have been removed.\n"); } if (length($your_profile{'desc'})>20000) { push(@MINOR_ERRORS, "Your profile was too long. It has been truncated. (Removed section:". substr($your_profile{'desc'},20000).")
\n"); substr($your_profile{'desc'}, 20000) = ''; } } sub save_your_attributes($) { &write_lock_data_file('attributes.db'); dbmopen(%YOUR_ATTRIBUTES_DB, "$DATAPATH/attributes.db", 0666) or &fatalerror("Unable to dbmopen(attributes.db)\n"); $YOUR_ATTRIBUTES_DB{$USER_ID} = join(',',@your_attributes); dbmclose(%YOUR_ATTRIBUTES_DB); &unlock_data_file('attributes.db'); &save_your_profile(); } sub save_your_profile() { &write_lock_data_file('profiles.db'); dbmopen(%PROFILES, "$DATAPATH/profiles.db", 0666) or &fatalerror("Unable to dbmopen(profiles.db)\n"); &sanity_check_profile(); $PROFILES{"maxunc_$USER_ID"} = $your_profile{'maxunc'} if defined($your_profile{'maxunc'}); $PROFILES{"minopt_$USER_ID"} = $your_profile{'minopt'} if defined($your_profile{'minopt'}); $PROFILES{"lat_$USER_ID"} = $your_profile{'lat'} if (defined($your_profile{'lat'}) && $your_profile{'lat'} ne ''); $PROFILES{"long_$USER_ID"} = $your_profile{'long'} if (defined($your_profile{'long'}) && $your_profile{'long'} ne ''); $PROFILES{"desc_$USER_ID"} = $your_profile{'desc'} if defined($your_profile{'desc'}); $PROFILES{"maxdist_$USER_ID"} = $your_profile{'maxdist'} if (defined($your_profile{'maxdist'}) && $your_profile{'maxdist'} ne ''); dbmclose(%PROFILES); &unlock_data_file('profiles.db'); } sub parse_arc($$$) { return undef unless defined $_[0]; my ($latin,$posch,$negch) = @_; my $sign = 1; if ($latin=~s/$negch//sig) { return undef if $latin=~/$posch/is; $sign = -1; } else { $latin=~s/$posch//sig; } if ($latin=~/^([0-9]+)$/s) { return $latin*$sign; } elsif ($latin =~/^(-[0-9]+)$/s) { return $latin*$sign; } if ($latin=~/^([0-9]+)([^0-9]+)(.*?)$/s) { my $degrees = $1; my $tail=$3; $tail=~s/^\s+//si; $tail=~s/\s+$//si; if ($tail=~/([0-9]+)'$/s) { $degrees += $1*(1/60); } elsif ($tail=~/([0-9]+)'[^0-9]*([0-9]+)(''|")/si) { my $minutes = $1; my $seconds = $2; return ($degrees + ($minutes*(1/60)) + ($seconds*(1/3600))) * $sign; } return $degrees * $sign; } return undef; } sub parse_latitude($) { return parse_arc($_[0],'N','S'); } sub parse_longitude($) { return parse_arc($_[0],'E','W'); } sub set_your_attributes($) { &check_login(); my @params = $query->param(); my $param; for $param (@params) { if ($param=~/^option_([0-9]+)$/s) { my $num = $1; $your_attributes[$num] = $query->param($param); } } $your_profile{'maxunc'} = $query->param('profile_maxunc'); $your_profile{'minopt'} = $query->param('profile_minopt'); $your_profile{'desc'} = $query->param('profile_desc'); $your_profile{'lat'} = parse_latitude($query->param('profile_lat')); $your_profile{'long'} = parse_longitude($query->param('profile_long')); $your_profile{'maxdist'} = $query->param('profile_maxdist'); &save_your_attributes(); &success("Your attributes have been updated.\n"); } sub show_your_attribute($) { print "$attribute_descs[$_[0]]:
"; } sub write_lock_data_file() { open("LOCK_$_[0]","<$DATAPATH/$_[0]") or &fatalerror("Could not open $_[0] for locking\n"); flock("LOCK_$_[0]", &LOCK_EX) or &fatalerror("Could not lock $_[0]\n"); } sub read_lock_data_file() { open("LOCK_$_[0]","<$DATAPATH/$_[0]") or &fatalerror("Could not open $_[0] for locking\n"); flock("LOCK_$_[0]", &LOCK_SH) or &fatalerror("Could not lock $_[0]\n"); } sub unlock_data_file($) { flock("LOCK_$_[0]", &LOCK_UN) or &fatalerror("Silly error unlocking $_[0]\n"); } sub load_your_attributes() { &read_lock_data_file('attributes.db'); dbmopen(%YOUR_ATTRIBUTE_DB,"$DATAPATH/attributes.db", 0666) or &fatalerror("Could not open attributes.db\n"); if (exists($YOUR_ATTRIBUTE_DB{$USER_ID})) { @your_attributes = split(/,/, $YOUR_ATTRIBUTE_DB{$USER_ID}); } dbmclose(%YOUR_ATTRIBUTE_DB); &unlock_data_file('attributes.db'); } sub textarea_clean() { my $ret = $_[0]; $ret=~s/[/gs; return $ret; } sub show_your_attributes() { &check_login(); &load_attribute_descs(); &load_your_attributes(); &open_profile_db(); print "Content-type: text/html\015\012\015\012"; print "Good Sex For Mutants: You $STYLESHEET