Compare commits
1 Commits
tags/MOZBO
...
src
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
258dc9fead |
@@ -1,251 +0,0 @@
|
||||
################################
|
||||
# Bugzilla Module #
|
||||
################################
|
||||
|
||||
package BotModules::Bugzilla;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# there is a minor error in this module: bugsHistory->$target->$bug is
|
||||
# accessed even when bugsHistory->$target doesn't yet exist. XXX
|
||||
|
||||
# This is ported straight from techbot, so some of the code is a little convoluted. So sue me. I was lazy.
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['bugsURI', 1, 1, 'http://bugzilla.mozilla.org/'],
|
||||
['bugsDWIMQueryDefault', 1, 1, 'short_desc_type=substring&short_desc='],
|
||||
['bugsHistory', 0, 0, {}],
|
||||
['backoffTime', 1, 1, 120],
|
||||
['ignoreCommentsTo', 1, 1, ['techbot1']],
|
||||
['ignoreCommentsFrom', 1, 1, ['|']],
|
||||
['skipPrefixFor', 1, 1, []],
|
||||
['mutes', 1, 1, ''], # "channel channel channel"
|
||||
);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my %commands = (
|
||||
'' => 'The Bugzilla module provides an interface to the bugzilla bug database. It will spot anyone mentioning bugs, too, and report on what they are. For example if someone says \'I think that\'s a dup of bug 5693, the :hover thing\', then this module will display information about bug 5693.',
|
||||
'bug' => 'Fetches a summary of bugs from bugzilla. Expert syntax: \'bugzilla [bugnumber[,]]*[&bugzillaparameter=value]*\', bug_status: UNCONFIRMED|NEW|ASSIGNED|REOPENED; *type*=substring|; bugtype: include|exclude; order: Assignee|; chfield[from|to|value] short_desc\' long_desc\' status_whiteboard\' bug_file_loc\' keywords\'; \'_type; email[|type][1|2] [reporter|qa_contact|assigned_to|cc]',
|
||||
'bug-total' => 'Same as bug (which see) but only displays the total line.',
|
||||
'bugs' => 'A simple DWIM search. Not very clever. ;-) Syntax: \'<query string> bugs\' e.g. \'mozbot bugs\'.'
|
||||
);
|
||||
if ($self->isAdmin($event)) {
|
||||
$commands{'mute'} = 'Disable watching for bug numbers in a channel. Syntax: mute bugzilla in <channel>';
|
||||
$commands{'unmute'} = 'Enable watching for bug numbers in a channel. Syntax: unmute bugzilla in <channel>';
|
||||
}
|
||||
return \%commands;
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*(?:please\s+)?(?:(?:could\s+you\s+)?(?:please\s+)?show\s+me\s+|what\s+is\s+|what's\s+)?bug(?:\s*id)?s?[#\s]+([0-9].*?|&.+?)(?:\s+please)?[?!.]*\s*$/osi) {
|
||||
my $target = $event->{'target'};
|
||||
my $bug = $1;
|
||||
$self->FetchBug($event, $bug, 'bugs', 0, 0);
|
||||
$self->{'bugsHistory'}->{$target}->{$bug} = time() if $bug =~ /^[0-9]+$/os;
|
||||
} elsif ($message =~ /^\s*bug-?total\s+(.+?)\s*$/osi) {
|
||||
$self->FetchBug($event, $1, 'total', 0, 0);
|
||||
} elsif ($self->isAdmin($event)) {
|
||||
if ($message =~ /^\s*mute\s+bugzilla\s+in\s+(\S+?)\s*$/osi) {
|
||||
$self->{'mutes'} .= " $1";
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Watching for bug numbers disabled in channel $1.");
|
||||
} elsif ($message =~ /^\s*unmute\s+bugzilla\s+in\s+(\S+)\s*$/osi) {
|
||||
my %mutedChannels = map { $_ => 1 } split(/ /o, $self->{'mutes'});
|
||||
delete($mutedChannels{$1}); # get rid of any mentions of that channel
|
||||
$self->{'mutes'} = join(' ', keys(%mutedChannels));
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Watching for bug numbers reenabled in channel $1.");
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub CheckForBugs {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
my $bug;
|
||||
my $skipURI;
|
||||
if ($message =~ /^(?:.*[]\s,.;:\\\/=?!()<>{}[-])?bug[\s#]*([0-9]+)(?:[]\s,.;:\\\/=?!()<>{}[-].*)?$/osi) {
|
||||
$bug = $1;
|
||||
$skipURI = 0;
|
||||
} elsif ($message =~ /\Q$self->{'bugsURI'}\Eshow_bug.cgi\?id=([0-9]+)(?:[^0-9&].*)?$/si) {
|
||||
$bug = $1;
|
||||
$skipURI = 1;
|
||||
}
|
||||
if (($bug) and ((not $event->{'channel'}) or ($self->{'mutes'} !~ /^(.*\s|)\Q$event->{'channel'}\E(|\s.*)$/si)) and
|
||||
(not $self->ignoringCommentsFrom($event->{'from'})) and (not $self->ignoringCommentsTo($message))) {
|
||||
$self->debug("Noticed someone mention bug $bug -- investigating...");
|
||||
my $last = 0;
|
||||
$last = $self->{'bugsHistory'}->{$event->{'target'}}->{$bug} if defined($self->{'bugsHistory'}->{$event->{'target'}}->{$bug});
|
||||
if ((time()-$last) > $self->{'backoffTime'}) {
|
||||
$self->FetchBug($event, $bug, 'bugs', $skipURI, 1);
|
||||
}
|
||||
$self->{'bugsHistory'}->{$event->{'target'}}->{$bug} = time();
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub Heard {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
unless ($self->CheckForBugs($event, $message)) {
|
||||
return $self->SUPER::Heard(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Baffled {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*(...+?)\s+bugs\s*$/osi) {
|
||||
my $target = $event->{'target'};
|
||||
$self->FetchBug($event, $1, 'dwim', 0, 0);
|
||||
} else {
|
||||
return $self->SUPER::Baffled(@_);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub Felt {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
unless ($self->CheckForBugs($event, $message)) {
|
||||
return $self->SUPER::Felt(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Saw {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
unless ($self->CheckForBugs($event, $message)) {
|
||||
return $self->SUPER::Saw(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub FetchBug {
|
||||
my $self = shift;
|
||||
my ($event, $bugParams, $type, $skipURI, $skipZaroo) = @_;
|
||||
my $uri;
|
||||
if ($type eq 'dwim') {
|
||||
# XXX should escape query string
|
||||
$uri = "$self->{'bugsURI'}buglist.cgi?$self->{'bugsDWIMQueryDefault'}".join(',',split(' ',$bugParams));
|
||||
$type = 'bugs';
|
||||
} else {
|
||||
$uri = "$self->{'bugsURI'}buglist.cgi?bug_id=".join(',',split(' ',$bugParams));
|
||||
}
|
||||
$self->getURI($event, $uri, 'bugs', $type, $skipURI, $skipZaroo);
|
||||
}
|
||||
|
||||
sub GotURI {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $output, $type, $subtype, $skipURI, $skipZaroo) = @_;
|
||||
if ($type eq 'bugs') {
|
||||
|
||||
my $lots;
|
||||
my @qp;
|
||||
|
||||
# magicness
|
||||
{ no warnings; # this can go _very_ wrong easily
|
||||
|
||||
$lots = ($output !~ /<FORM\s+METHOD=POST\s+ACTION="long_list.cgi">/osi); # if we got truncated, then this will be missing
|
||||
$output =~ s/<\/TABLE><TABLE .+?<\/A><\/TH>//gosi;
|
||||
(undef, $output) = split(/Summary<\/A><\/TH>/osi, $output);
|
||||
($output, undef) = split(/<\/TABLE>/osi, $output);
|
||||
$output =~ s/[\n\r]//gosi;
|
||||
@qp = split(/<TR VALIGN=TOP ALIGN=LEFT CLASS=[-A-Za-z0-9]+ ><TD>/osi, $output); }
|
||||
|
||||
# loop through output, constructing output string
|
||||
my @output;
|
||||
unless (@qp) {
|
||||
unless ($skipZaroo) {
|
||||
@output = ('Zarro boogs found.');
|
||||
} else {
|
||||
@output = ();
|
||||
}
|
||||
} else {
|
||||
if ($lots) {
|
||||
@output = ('Way too many bugs found. I gave up so as to not run out of memory. Try to narrow your search or something!');
|
||||
$subtype = 'lots';
|
||||
} elsif ($#qp > 1) {
|
||||
@output = ($#qp.' bugs found.'); # @qp will contain one more item than there are bugs
|
||||
if ((@qp > 5) and ($event->{'channel'}) and ($subtype ne 'total')) {
|
||||
$output[0] .= ' Five shown, please message me for the complete list.';
|
||||
@qp = @qp[0..4];
|
||||
}
|
||||
}
|
||||
if ($subtype eq 'bugs') {
|
||||
local $" = ', ';
|
||||
foreach (@qp) {
|
||||
if ($_) {
|
||||
# more magic
|
||||
if (my @d = m|<A HREF="show_bug.cgi\?id=([0-9]+)">\1</A> <td class=severity><nobr>(.*?)</nobr><td class=priority><nobr>(.*?)</nobr><td class=platform><nobr>(.*?)</nobr><td class=owner><nobr>(.*?)</nobr><td class=status><nobr>(.*?)</nobr><td class=resolution><nobr>(.*?)</nobr><td class=summary>(.*)|osi) {
|
||||
# bugid severity priority platform owner status resolution subject
|
||||
my $bugid = shift @d;
|
||||
if ($skipURI) {
|
||||
push(@output, $self->unescapeXML("Bug $bugid: @d"));
|
||||
} else {
|
||||
push(@output, $self->unescapeXML("Bug $self->{'bugsURI'}show_bug.cgi?id=$bugid @d"));
|
||||
}
|
||||
$output[$#output] =~ s/, (?:, )+/, /gosi;
|
||||
$self->{'bugsHistory'}->{$event->{'target'}}->{$d[0]} = time();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $prefix;
|
||||
if (grep {$_ eq $event->{'from'}} @{$self->{'skipPrefixFor'}}) {
|
||||
# they don't want to have the report prefixed with their name
|
||||
$prefix = '';
|
||||
} else {
|
||||
$prefix = "$event->{'from'}: ";
|
||||
}
|
||||
|
||||
# now send out the output
|
||||
foreach (@output) {
|
||||
$self->say($event, "$prefix$_");
|
||||
}
|
||||
|
||||
} else {
|
||||
return $self->SUPER::GotURI(@_);
|
||||
}
|
||||
}
|
||||
|
||||
sub ignoringCommentsTo {
|
||||
my $self = shift;
|
||||
my ($who) = @_;
|
||||
foreach (@{$self->{'ignoreCommentsTo'}}) {
|
||||
return 1 if $who =~ /^(?:.*[]\s,.;:\\\/=?!()<>{}[-])?\Q$_\E(?:[]\s,.;:\\\/=?!()<>{}[-].*)?$/is;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub ignoringCommentsFrom {
|
||||
my $self = shift;
|
||||
my ($who) = @_;
|
||||
foreach (@{$self->{'ignoreCommentsFrom'}}) {
|
||||
return 1 if $_ eq $who;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
Unless otherwise stated, the contents of these file are subject to
|
||||
the Mozilla Public License Version 1.1 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may
|
||||
obtain a copy of the License at http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS
|
||||
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
rights and limitations under the License.
|
||||
|
||||
The Original Code is the Bugzilla Bug Tracking System.
|
||||
|
||||
The Initial Developer of the Original Code is Netscape
|
||||
Communications Corporation. Portions created by Netscape are
|
||||
Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
Reserved.
|
||||
|
||||
Contributor(s): Harrison Page <harrison@netscape.com>
|
||||
Terry Weissman <terry@mozilla.org>
|
||||
Risto Kotalampi <risto@kotalampi.com>
|
||||
Josh Soref <timeless@mac.com>
|
||||
Ian Hickson <mozbot@hixie.ch>
|
||||
Zach Lipton <zach@zachlipton.com>
|
||||
Jake Steenhagen <jake@acutex.net>
|
||||
mental <xor@ivwnet.com>
|
||||
@@ -1,246 +0,0 @@
|
||||
################################
|
||||
# FTP Module #
|
||||
################################
|
||||
|
||||
package BotModules::FTP;
|
||||
use vars qw(@ISA);
|
||||
use Net::FTP;
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['host', 1, 1, 'ftp.mozilla.org'],
|
||||
['path', 1, 1, '/pub/mozilla/nightly/latest'],
|
||||
['updateDelay', 1, 1, 600],
|
||||
['preferredLineLength', 1, 1, 80],
|
||||
['data', 0, 0, {}], # data -> file -> datetime stamp
|
||||
['mutes', 1, 1, ''], # "channel channel channel"
|
||||
);
|
||||
}
|
||||
|
||||
# Schedule - called when bot connects to a server, to install any schedulers
|
||||
# use $self->schedule($event, $delay, $times, $data)
|
||||
# where $times is 1 for a single event, -1 for recurring events,
|
||||
# and a +ve number for an event that occurs that many times.
|
||||
sub Schedule {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
$self->schedule($event, \$self->{'updateDelay'}, -1, 'ftp');
|
||||
$self->SUPER::Schedule($event);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my %commands = (
|
||||
'' => "This module monitors the FTP site 'ftp://$self->{'host'}$self->{'path'}/' and reports new files as they appear.",
|
||||
'ftp' => 'On its own, lists the currently available files. With a suffix, does a substring search and reports all files matching that pattern. Syntax: \'ftp [pattern]\'',
|
||||
);
|
||||
if ($self->isAdmin($event)) {
|
||||
$commands{'mute'} = 'Disable reporting of new files in a channel. Syntax: mute ftp in <channel>';
|
||||
$commands{'unmute'} = 'Enable reporting of new files in a channel. Syntax: unmute ftp in <channel>';
|
||||
}
|
||||
return \%commands;
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*ftp(?:\s+(\S+?))?\s*\?*\s*$/osi) {
|
||||
$self->spawnChild($event, \&ftp_check, [$self, $self->{'path'}, $self->{'host'}], 'ftp', [$event, $1]);
|
||||
} elsif ($self->isAdmin($event)) {
|
||||
if ($message =~ /^\s*mute\s+ftp\s+in\s+(\S+?)\s*$/osi) {
|
||||
$self->{'mutes'} .= " $1";
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Reporting of new files disabled in channel $1.");
|
||||
} elsif ($message =~ /^\s*unmute\s+ftp\s+in\s+(\S+)\s*$/osi) {
|
||||
my %mutedChannels = map { $_ => 1 } split(/ /o, $self->{'mutes'});
|
||||
delete($mutedChannels{$1}); # get rid of any mentions of that channel
|
||||
$self->{'mutes'} = join(' ', keys(%mutedChannels));
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Reporting of new files reenabled in channel $1.");
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
}
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my ($event, @data) = @_;
|
||||
if ($data[0] eq 'ftp') {
|
||||
$self->spawnChild($event, \&ftp_check, [$self, $self->{'path'}, $self->{'host'}], 'ftp', [undef]);
|
||||
} else {
|
||||
$self->SUPER::Scheduled($event, @data);
|
||||
}
|
||||
}
|
||||
|
||||
# ChildCompleted - Called when a child process has quit
|
||||
sub ChildCompleted {
|
||||
my $self = shift;
|
||||
my ($event, $type, $output, @data) = @_;
|
||||
if ($type eq 'ftp') {
|
||||
my @output = split(/\n/os, $output);
|
||||
if (shift(@output)) {
|
||||
my @new = ();
|
||||
while (@output) {
|
||||
my ($file, $stamp) = (shift(@output), shift(@output));
|
||||
if ((defined($self->{'data'}->{$file})) and ($self->{'data'}->{$file} < $stamp)) {
|
||||
push(@new, $file);
|
||||
}
|
||||
$self->{'data'}->{$file} = $stamp;
|
||||
}
|
||||
if ((defined($self->{'_ready'})) and (scalar(@new))) {
|
||||
my $s = scalar(@new) > 1 ? 's' : '';
|
||||
@output = $self->prettyPrint($self->{'preferredLineLength'},
|
||||
"New file$s in ftp://$self->{'host'}$self->{'path'}/ : ",
|
||||
'', ' ', @new);
|
||||
foreach my $channel (@{$self->{'channels'}}) {
|
||||
unless ($self->{'mutes'} =~ /^(.*\s|)\Q$channel\E(|\s.*)$/si) {
|
||||
$event->{'target'} = $channel;
|
||||
foreach (@output) {
|
||||
$self->say($event, $_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->{'_ready'} = 1;
|
||||
if ($data[0]) {
|
||||
$self->ftp_stamp($event, $data[1]);
|
||||
}
|
||||
} else {
|
||||
if ($data[0]) {
|
||||
$self->say($event, "I could not contact $self->{'host'}, sorry.");
|
||||
}
|
||||
$self->tellAdmin($event, "Dude, I'm having a problem with FTP. Could you prod $self->{'host'} for me please? Or fix my config? Cheers.");
|
||||
}
|
||||
} else {
|
||||
$self->SUPER::ChildCompleted($event, $type, $output, @data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
# The following is directly from the original techbot (mozbot 1.5), written by timeless.
|
||||
# The only changes I made were to port it to the mozbot2 architecture. Those changes
|
||||
# are commented.
|
||||
|
||||
sub day_str {
|
||||
my (@stamp,$ahr,$amn,$asc);
|
||||
($asc, $amn, $ahr, @stamp)=gmtime($_[3]);
|
||||
$asc = "0$asc" if $asc < 10; # \
|
||||
$amn = "0$amn" if $amn < 10; # -- added these to zero-pad output
|
||||
$ahr = "0$ahr" if $ahr < 10; # /
|
||||
return "$_[4] ($ahr:$amn:$asc) " # added extra space to neaten output
|
||||
if ($stamp[0]==$_[0] && $stamp[1]==$_[1] && $stamp[2]==$_[2]);
|
||||
}
|
||||
|
||||
sub ftp_stamp {
|
||||
|
||||
# It seems that the original wanted ($to, $cmd, $rest) as the arguments.
|
||||
# However, it doesn't use $to except at the end (which we replace) and
|
||||
# it doesn't use $cmd at all. This is lucky for us, since the first
|
||||
# argument of methods is always the object ref.
|
||||
my $self = $_[0];
|
||||
# This function also expects to be able to use a global (!) variable
|
||||
# called %latestbuilds. We grandfather that by making a lexically scoped
|
||||
# copy of one of our object fields.
|
||||
my %latestbuilds = %{$self->{'data'}};
|
||||
# We have to keep a copy of $event around for when we send out the
|
||||
# output, of course. So let's use the second argument for that:
|
||||
my $event = $_[1];
|
||||
# Finally, we have to work around a serious bug in the original version,
|
||||
# which assumed any pattern input was valid regexp. [XXX use eval]
|
||||
$_[2] = defined($_[2]) ? quotemeta($_[2]) : 0;
|
||||
# In summary, call this function like this:
|
||||
# $self->ftp_stamp($event, $pattern);
|
||||
|
||||
|
||||
my @day=gmtime(time); my @tm=@day[0..2]; @day=@day[3..5];
|
||||
my (@filestamp, $filelist, $ahr,$amn,$asc);
|
||||
if ($_[2]){ # this code's output is *VERY* ugly. But I just took it as is, so deal with it. Patches welcome.
|
||||
foreach my $filename (keys %latestbuilds){
|
||||
my @ltm=gmtime($latestbuilds{$filename});
|
||||
$filelist.="$filename [".($ltm[5]+1900).'-'.($ltm[4]+1)."-$ltm[3] $ltm[2]:$ltm[1]:$ltm[0]]"
|
||||
if $filename=~/$_[2]/;
|
||||
}
|
||||
$filelist=$filelist||'<nothing matched>';
|
||||
$filelist="Files matching re:$_[2] [gmt] $filelist";
|
||||
}else{
|
||||
foreach my $filename (keys %latestbuilds){
|
||||
$filelist.=day_str(@day[0..2],$latestbuilds{$filename},$filename);
|
||||
}
|
||||
if ($filelist){
|
||||
$filelist="Files from today [gmt] $filelist";
|
||||
} else {
|
||||
foreach my $filename (keys %latestbuilds){
|
||||
@day=gmtime(time-86400); @day=@day[3..5];
|
||||
$filelist.=day_str(@day[0..2],$latestbuilds{$filename},$filename);
|
||||
}
|
||||
$filelist="Files from yesterday [gmt] $filelist"|| # next line changed from " to \' and added missing '>'
|
||||
'<No files in the past two days by gmt, try \'ftp .\' for a complete filelist>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Append the current time for those not in GMT time zones
|
||||
my @time;
|
||||
foreach (@tm) {
|
||||
# zero pad the time
|
||||
$_ = "0$_" if $_ < 10;
|
||||
# switch digits around (@tm is in reverse order)
|
||||
unshift(@time, $_);
|
||||
}
|
||||
# output
|
||||
local $";
|
||||
$" = ':';
|
||||
$filelist .= " time now: @time";
|
||||
# Ok, now we want to send out the results (held in $filelist).
|
||||
$self->say($event, $filelist);
|
||||
}
|
||||
|
||||
|
||||
sub ftp_check {
|
||||
|
||||
# ok, this function has been hacked for the new architecture.
|
||||
# ftp_check is called in a spawned child.
|
||||
# It returns the output in a fixed format back to the parent
|
||||
# process. The format is
|
||||
# 1
|
||||
# file
|
||||
# timestamp
|
||||
# file
|
||||
# timestamp
|
||||
# if it fails, the '1' will be missing (no output).
|
||||
# It should be passed the following arguments:
|
||||
# [$self, $path, $server]
|
||||
my $self = $_[0];
|
||||
my $output = '';
|
||||
|
||||
my $buf='';
|
||||
my $mdtms;
|
||||
my $ftpserver=$_[2];
|
||||
my $ftp = new Net::FTP($ftpserver, Debug => 0, Passive => 1);
|
||||
if ($ftp){
|
||||
$output .= "1\n"; # how we find out if it worked or not
|
||||
if ($ftp->login('anonymous','mozbot@localhost')){
|
||||
$ftp->cwd($_[1]); # path used to be hardcoded
|
||||
for my $f ($ftp->ls){
|
||||
$mdtms=$ftp->mdtm($f);
|
||||
$output .= "$f\n$mdtms\n"; # output to pipe instead of irc
|
||||
}
|
||||
$ftp->quit;
|
||||
};
|
||||
}
|
||||
|
||||
# now send out the buffered output
|
||||
return $output;
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
################################
|
||||
# Filter Module #
|
||||
################################
|
||||
|
||||
# The canonical filters should be installed on your path somewhere.
|
||||
# You can get the source from these from your local distributor.
|
||||
|
||||
package BotModules::Filter;
|
||||
use vars qw(@ISA);
|
||||
use IPC::Open2;
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
my @Filters = (
|
||||
'b1ff',
|
||||
'chef',
|
||||
'cockney',
|
||||
'eleet',
|
||||
'jethro',
|
||||
'jibberish',
|
||||
'jive',
|
||||
'kraut',
|
||||
'nyc',
|
||||
'rasterman',
|
||||
'upside-down',
|
||||
);
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my $reply = {
|
||||
'' => 'This module is an interface to the text filter applications.',
|
||||
};
|
||||
foreach (@Filters) {
|
||||
$reply->{$_} = "Pass the text through the $_ filter. Syntax: $_ <text>";
|
||||
}
|
||||
if ($self->isAdmin($event)) {
|
||||
$reply->{'filtersay'} = "Pass text through a filter and send it to a channel. Syntax: filtersay <filter> <channel> <text>";
|
||||
}
|
||||
return $reply;
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
foreach (@Filters) {
|
||||
if ($message =~ /^\s*\Q$_\E\s+(.+?)\s*$/si) {
|
||||
$self->spawnChild($event, sub { return $self->Filter(@_); }, [$_, $1], 'filter', []);
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
} elsif (($message =~ /^\s*filtersay\s+\Q$_\E\s+(\S+)\s+(.+?)\s*$/si) and ($self->isAdmin($event))) {
|
||||
$self->spawnChild($event, sub { return $self->Filter(@_); }, [$_, $2], 'filter', [$1]);
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
}
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
|
||||
sub Filter {
|
||||
my $self = shift;
|
||||
my($filter, $text) = @_;
|
||||
my $reader;
|
||||
my $writer;
|
||||
local $/ = undef;
|
||||
my $pid = open2($reader, $writer, $filter);
|
||||
print $writer $text;
|
||||
close($writer);
|
||||
my $reply = <$reader>;
|
||||
close($reader);
|
||||
waitpid($pid, 0);
|
||||
return $reply;
|
||||
}
|
||||
|
||||
# ChildCompleted - Called when a child process has quit
|
||||
sub ChildCompleted {
|
||||
my $self = shift;
|
||||
my ($event, $type, $output, @data) = @_;
|
||||
if ($type eq 'filter') {
|
||||
local $event->{'target'} = $data[0] if defined($data[0]);
|
||||
$self->say($event, $output);
|
||||
} else {
|
||||
return $self->SUPER::ChildCompleted(@_);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
# $Id: Flood.bm,v 1.1 2001-08-14 16:53:30 jake%acutex.net Exp $
|
||||
###########################
|
||||
# Flood Protection module #
|
||||
###########################
|
||||
|
||||
package BotModules::Flood;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
|
||||
foreach my $chan (@{$self->{'channels'}}) {
|
||||
$self->registerVariables( ["join_$chan", 0, 0, []] );
|
||||
}
|
||||
$self->registerVariables(
|
||||
['numberOfJoins', 1, 1, '7'],
|
||||
['secondsToTrigger', 1, 1, '2'],
|
||||
['minutesToProtect', 1, 1, '5'],
|
||||
);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'This module will help control "join flood" attacks on IRC',
|
||||
};
|
||||
}
|
||||
|
||||
# Set - called to set a variable to a particular value.
|
||||
sub Set {
|
||||
my $self = shift;
|
||||
my ($event, $variable, $value) = @_;
|
||||
# If changing the setting for numberOfJoins make sure
|
||||
# that the arrays are empty. Otherwise, reducing the
|
||||
# numberOfJoins value would not work properly.
|
||||
if ($variable eq 'numberOfJoins') {
|
||||
foreach my $chan (@{$self->{'channels'}}) {
|
||||
@{$self->{"join_$chan"}} = ();
|
||||
}
|
||||
}
|
||||
# now actually do the setting of the variable
|
||||
return $self->SUPER::Set($event, $variable, $value);
|
||||
}
|
||||
|
||||
sub JoinedChannel {
|
||||
my $self = shift;
|
||||
my ($event, $channel) = @_;
|
||||
$self->registerVariables( ["join_$channel", 0, 0, []] );
|
||||
return $self->SUPER::JoinedChannel($event, $channel); # call inherited method
|
||||
}
|
||||
|
||||
sub SpottedJoin {
|
||||
my $self = shift;
|
||||
my ($event, $channel, $who) = @_;
|
||||
# If numberOfJoins or secondsToTrigger is not a positive Integer, don't do anything
|
||||
if ($self->{'numberOfJoins'} !~ m/^[1-9][0-9]*$/o || $self->{'secondsToTrigger'} !~ m/^[1-9][0-9]*$/o) {
|
||||
# We didn't do anything, so don't pretend like we did :)
|
||||
return $self->SUPER::SpottedJoin($event, $channel, $who);
|
||||
}
|
||||
# Here we have the 'join_times' array to push and shift to/from
|
||||
push(@{$self->{"join_$channel"}}, time());
|
||||
if (scalar(@{$self->{"join_$channel"}}) >= $self->{'numberOfJoins'}) {
|
||||
my $oldest = shift(@{$self->{"join_$channel"}});
|
||||
my $timechange = time() - $oldest;
|
||||
if ($self->{'secondsToTrigger'} >= $timechange) {
|
||||
# We have just seen many joins happen very quickly. This channel should
|
||||
# have its mode set to +i until an op can figure out what went wrong.
|
||||
$self->mode($event, $event->{'channel'}, '+i');
|
||||
my $extra_text = "";
|
||||
# If minutesToProtect is a positive integer we should set mode -i after
|
||||
# that number of minutes has passed.
|
||||
if ($self->{'minutesToProtect'} =~ m/^[1-9][0-9]*$/o) {
|
||||
my $seconds = $self->{'minutesToProtect'} * 60;
|
||||
my @mode = ('mode', $event->{'channel'}, '-i');
|
||||
$self->schedule($event, $seconds, 1, @mode);
|
||||
$extra_text = "I'll set it -i in $self->{'minutesToProtect'} minutes";
|
||||
}
|
||||
$self->say($event, "I just saw a lot of joins happen very quickly. Because of " .
|
||||
"that I set this channel's mode to be Invite Only... $extra_text");
|
||||
}
|
||||
}
|
||||
# By returning 0 we ensure that a join won't be processed more than once.
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my ($event, @data) = @_;
|
||||
|
||||
my $what = shift(@data);
|
||||
if ($what eq 'mode') {
|
||||
$self->mode($event, @data);
|
||||
} else {
|
||||
# Call the inherited event
|
||||
return $self->SUPER::Schedule(@_);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
################################
|
||||
# Fortune Cookie Module #
|
||||
################################
|
||||
|
||||
package BotModules::FortuneCookies;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'A module to get random fortune cookies.',
|
||||
'fortune' => 'Same as \'cookie\', which see.',
|
||||
'cookie' => 'To get a fortune cookie, just tell me \'cookie\'. To set a new fortune cookie, see \'new\' (or \'add\'). To find out how many cookies are left, use \'cookie status\'.',
|
||||
'new' => 'To set a new fortune cookie, say \'new cookie\' followed by the text, e.g. \'new cookie: you will have a nice day\' or whatever. The string %from% will be replaced by the name of whoever requests the cookie.',
|
||||
'add' => 'To add a new fortune cookie, say \'add cookie\' followed by the text, e.g. \'add cookie: you will have a nice day\' or whatever. The string %from% will be replaced by the name of whoever requests the cookie.',
|
||||
'fetch' => 'The command \'fetch cookies from <uri>\' will add each line in <uri> to the cookie list. Cookie lists must start with one line that reads \'FORTUNE COOKIE FILE\'. Blank lines and lines starting with a hash (\'#\') are ignored.',
|
||||
};
|
||||
}
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['cookies', 1, 1, ['The sun will rise in the east today, indicating nothing in particular.']],
|
||||
['cookiesIndex', 1, 1, 0],
|
||||
['cookiesLeft', 0, 1, 10],
|
||||
['bakingTime', 1, 1, 20],
|
||||
['cookiesMax', 1, 1, 10],
|
||||
);
|
||||
}
|
||||
|
||||
# Schedule - called when bot connects to a server, to install any schedulers
|
||||
# use $self->schedule($event, $delay, $times, $data)
|
||||
# where $times is 1 for a single event, -1 for recurring events,
|
||||
# and a +ve number for an event that occurs that many times.
|
||||
sub Schedule {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
$self->schedule($event, \$self->{'bakingTime'}, -1, 'newCookie');
|
||||
$self->SUPER::Schedule($event);
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*(?:please[,.!1?]*\s+)?(?:(?:can|could)\s+i\s+have\s+a\s+|give\s+me\s+a\s+)?(?:fortune\s+cookie|fortune|cookie)(?:[,!1.\s]+now)?(?:[,!1.\s]+please)?\s*[?!1.]*\s*$/osi) {
|
||||
if ($self->{'cookiesLeft'} > 0) {
|
||||
$self->{'cookiesLeft'}--;
|
||||
my $cookie = $self->GetNext('cookies');
|
||||
$cookie =~ s/%from%/$event->{'from'}/gos;
|
||||
$self->say($event, $cookie);
|
||||
} else {
|
||||
$self->say($event, 'I\'m sorry, I\'ve run out of cookies! You\'ll have to wait for me to bake some more.');
|
||||
}
|
||||
} elsif ($message =~ /^\s*(?:new|add)\s+(?:fortune\s+cookie|fortune|cookie)[-!:,;.\s]+(.....+?)\s*$/osi) {
|
||||
push(@{$self->{'cookies'}}, $1);
|
||||
my $count = scalar(@{$self->{'cookies'}});
|
||||
$self->say($event, "$event->{'from'}: Thanks! I have added that fortune cookie to my recipe book. I now have $count fortunes!");
|
||||
$self->saveConfig();
|
||||
} elsif ($message =~ /^\s*cookie\s+(?:report|status|status\s+report)(?:\s+please)?[?!.1]*\s*$/osi) {
|
||||
my $count = scalar(@{$self->{'cookies'}});
|
||||
$self->say($event, "My cookie basket has $self->{'cookiesLeft'} cookies left out of possible $self->{'cookiesMax'}. I have $count fortunes in my recipe book.");
|
||||
} elsif ($message =~ /^\s*fetch\s+cookies\s+from\s+(.+?)\s*$/osi) {
|
||||
$self->getURI($event, $1, 'cookies');
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub GetNext {
|
||||
my $self = shift;
|
||||
my ($list) = @_;
|
||||
$self->{"${list}Index"} = 0 if $self->{"${list}Index"} > $#{$self->{$list}};
|
||||
my $reply = $self->{$list}->[$self->{"${list}Index"}++];
|
||||
# should add some deterministic way of making the output appear more random here XXX
|
||||
$self->saveConfig();
|
||||
return $reply;
|
||||
}
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my ($event, @data) = @_;
|
||||
if ($data[0] eq 'newCookie') {
|
||||
$self->{'cookiesLeft'}++ unless $self->{'cookiesLeft'} >= $self->{'cookiesMax'};
|
||||
} else {
|
||||
$self->SUPER::Scheduled($event, @data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub GotURI {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $output, $type) = @_;
|
||||
if ($type eq 'cookies') {
|
||||
my @output = split(/[\n\r]+/os, $output);
|
||||
if ((@output) and ($output[0] eq 'FORTUNE COOKIE FILE')) {
|
||||
my $count = 0;
|
||||
foreach (@output[1..$#output]) {
|
||||
if (/^[^#].+$/os) {
|
||||
push(@{$self->{'cookies'}}, $_);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
my $total = scalar(@{$self->{'cookies'}});
|
||||
my $s = $count > 1 ? 's' : '';
|
||||
$self->say($event, "$event->{'from'}: Thanks! I have added $count fortune cookie$s to my recipe book. I now have $total fortunes!");
|
||||
$self->saveConfig();
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Sorry, but that's not a fortune cookie file.");
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::GotURI(@_);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# General Module #
|
||||
################################
|
||||
|
||||
package BotModules::General;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
my $VERSION = '2.2';
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable?, value ]
|
||||
['preferredHelpLineLength', 1, 1, 90],
|
||||
);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'The module that provides the bot-wide services.',
|
||||
'help' => 'Gives information about modules and commands. Syntax: help [<topic>]',
|
||||
};
|
||||
}
|
||||
|
||||
# Told - Called for messages prefixed by the bot's nick
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*help(?:\s+($variablepattern))?[ ?!.]*\s*$/osi) {
|
||||
if ($1) {
|
||||
# display help for that command
|
||||
# first, build the help file...
|
||||
my %topicList;
|
||||
foreach my $module (@modules) {
|
||||
my $commands = $module->Help($event);
|
||||
if ($commands->{''}) {
|
||||
$topicList{lc($module->{'_name'})} = [] unless defined($topicList{lc($module->{'_name'})});
|
||||
push(@{$topicList{lc($module->{'_name'})}}, $commands->{''});
|
||||
}
|
||||
foreach (keys %$commands) {
|
||||
$topicList{lc($_)} = [] unless defined($topicList{lc($_)});
|
||||
push(@{$topicList{lc($_)}}, $commands->{lc($_)});
|
||||
}
|
||||
}
|
||||
if (defined($topicList{lc($1)})) {
|
||||
foreach (@{$topicList{lc($1)}}) {
|
||||
$self->say($event, "$1: $_");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "No help for topic '$1'.");
|
||||
}
|
||||
} else {
|
||||
# XXX amusingly, $helpline is defined here, because this
|
||||
# entire module is evaluated in the scope of mozbot.pl.
|
||||
$self->directSay($event, "Help topics for mozbot $VERSION ($helpline):");
|
||||
$self->say($event, "$event->{'from'}: help info /msg'ed") if ($event->{'channel'});
|
||||
local @" = ', '; # to reset font-lock: "
|
||||
my @helplist;
|
||||
foreach my $module (@modules) {
|
||||
my %commands = %{$module->Help($event)};
|
||||
my $moduleHelp = delete($commands{''});
|
||||
my @commands = sort keys %commands;
|
||||
if (@commands) {
|
||||
push(@helplist, "$module->{'_name'}: @commands");
|
||||
} elsif ($moduleHelp) {
|
||||
push(@helplist, "$module->{'_name'}");
|
||||
}
|
||||
}
|
||||
foreach ($self->prettyPrint($self->{'preferredHelpLineLength'}, undef, ' ', '; ', @helplist)) {
|
||||
$self->directSay($event, $_);
|
||||
}
|
||||
$self->directSay($event, 'For help on a particular topic, type \'help <topic>\'. Note that some commands may be disabled in certain channels.');
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it, do nothing else
|
||||
}
|
||||
|
||||
sub CTCPVersion {
|
||||
my $self = shift;
|
||||
my ($event, $who, $what) = @_;
|
||||
local $" = ', ';
|
||||
$self->ctcpReply($event, 'VERSION', "mozbot $VERSION (@modulenames)");
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
################################
|
||||
# God Module #
|
||||
################################
|
||||
|
||||
package BotModules::God;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my $answer = {
|
||||
'' => 'A per-channel auto-opper.',
|
||||
'ops' => 'Lists the autoop list for a channel. Syntax: \'ops in <channel>\'',
|
||||
'opme' => 'Checks the autoop list, and ops the speaker if they are on the autoop list. Must be used in a channel. Syntax: \'op me\' or \'opme\'',
|
||||
'mask' => 'Add or remove a regexp mask from a channel\'s autoop list. Only bot and channel admins can do this. USE OF THIS FEATURE IS HIGHLY DISCOURAGED AS IT IS VERY INSECURE!!! Syntax: \'add mask <user@host> in <channel>\' to add and \'remove mask <user@host> in <channel>\' to remove. The special word \'everywhere\' can be used instead of a channel name to add a mask that works in all channels.',
|
||||
'autoop' => 'Add someone to the autoop list for a channel. Only bot and channel admins can do this. Syntax: \'op <user> in <channel>\'',
|
||||
'deautoop' => 'Remove someone from the autoop list for a channel. Only bot and channel admins can do this. Syntax: \'deautoop <user> in <channel>\'',
|
||||
'enable' => 'Enable a module in a channel. Only bot and channel admins can do this. Syntax: \'enable <module> in <channel>\'',
|
||||
'disable' => 'Disable a module in a channel. Only bot and channel admins can do this. Syntax: \'disable <module> in <channel>\'',
|
||||
};
|
||||
if ($self->isAdmin($event)) {
|
||||
$answer->{'opme'} .= '. As an administrator, you can also say \'op me in <channel>\' or \'op me everywhere\' which will do the obvious things.';
|
||||
$answer->{'promote'} = 'Add someone to the channel admin list for a channel. Only bot admins can do this. Syntax: \'promote <user> in <channel>\'',
|
||||
$answer->{'demote'} = 'Remove someone from the channel admin list for a channel. Only bot admins can do this. Syntax: \'demote <user> in <channel>\'',
|
||||
}
|
||||
return $answer;
|
||||
}
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['channelAdmins', 1, 1, {}],
|
||||
['channelOps', 1, 1, {}],
|
||||
['channelOpMasks', 1, 1, {}],
|
||||
['kickLog', 1, 1, []],
|
||||
['allowPrivateOpRequests', 1, 1, 1],
|
||||
['maxInChannel', 1, 1, 4],
|
||||
);
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($event->{'level'} == 1) {
|
||||
if ($message =~ /^\s*(?:list\s+)?ops\s+(?:in\s+|for\s+)?(\S+)\s*\??$/osi) {
|
||||
my $channel = lc($1);
|
||||
$self->listOps($event, $channel);
|
||||
} elsif ($message =~ /^\s*autoop\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
||||
if (($self->isChannelAdmin($event, $2)) or ($self->isAdmin($event))) {
|
||||
my $channel = $2 eq 'everywhere' ? '' : lc($2);
|
||||
$self->{'channelOps'}->{$channel} .= " $1";
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: User '$1' added to the autoop list of channel '$2'.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Only channel administrators may add people to a channel's autoop list.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*deautoop\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
||||
if (($self->isChannelAdmin($event, $2)) or ($self->isAdmin($event))) {
|
||||
my $channel = $2 eq 'everywhere' ? '' : lc($2);
|
||||
my %people = map { $_ => 1 } split(/ +/os, $self->{'channelOps'}->{$channel});
|
||||
delete($people{$1}); # get rid of any mentions of that person
|
||||
$self->{'channelOps'}->{$channel} = join(' ', keys(%people));
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: User '$1' removed from the autoop list of channel '$2'.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Only channel administrators may remove people from a channel's autoop list.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*add\s+mask\s+(\S+)\s+(?:in|to|for|from)\s+(\S+)\s*$/osi) {
|
||||
if (($self->isChannelAdmin($event, $2)) or ($self->isAdmin($event))) {
|
||||
my $channel = $2 eq 'everywhere' ? '' : lc($2);
|
||||
$self->{'channelOpMasks'}->{$channel} .= " $1";
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Mask '$1' added to the autoop list of channel '$2'.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Only channel administrators may add masks to a channel's autoop list.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*remove\s+mask\s+(\S+)\s+(?:in|from|for|to)\s+(\S+)\s*$/osi) {
|
||||
if (($self->isChannelAdmin($event, $2)) or ($self->isAdmin($event))) {
|
||||
my $channel = $2 eq 'everywhere' ? '' : lc($2);
|
||||
my %people = map { $_ => 1 } split(/ +/os, $self->{'channelOpMasks'}->{$channel});
|
||||
delete($people{$1}); # get rid of any mentions of that person
|
||||
$self->{'channelOpMasks'}->{$channel} = join(' ', keys(%people));
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Mask '$1' removed from the autoop list of channel '$2'.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Only channel administrators may remove masks from a channel's autoop list.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*promote\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
||||
if ($self->isAdmin($event)) {
|
||||
$self->{'channelAdmins'}->{lc($2)} .= " $1";
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: User '$1' promoted to channel administrator status in channel '$2'.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Only administrators may promote people to channel admin status.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*demote\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
||||
if ($self->isAdmin($event)) {
|
||||
my %people = map { $_ => 1 } split(/ +/os, $self->{'channelAdmins'}->{lc($2)});
|
||||
delete($people{$1}); # get rid of any mentions of that person
|
||||
$self->{'channelAdmins'}->{lc($2)} = join(' ', keys(%people));
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: User '$1' removed from the channel administrator list of channel '$2'.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Only administrators may remove people's channel admin status.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*enable\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
||||
if (($self->isAdmin($event)) or ($self->isChannelAdmin($event, $2))) {
|
||||
my $module = $self->getModule($1);
|
||||
if ($1) {
|
||||
push(@{$module->{'channels'}}, lc($2));
|
||||
$module->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Module '$1' enabled in channel '$2'.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: There is no module called '$1', sorry.");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Only channel administrators may change a module's status.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*disable\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
||||
if (($self->isAdmin($event)) or ($self->isChannelAdmin($event, $2))) {
|
||||
my $module = $self->getModule($1);
|
||||
if ($1) {
|
||||
my %channels = map { $_ => 1 } @{$module->{'channels'}};
|
||||
delete($channels{lc($2)}); # get rid of any mentions of that channel
|
||||
@{$module->{'channels'}} = keys %channels;
|
||||
$module->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Module '$1' disabled in channel '$2'.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: There is no module called '$1', sorry.");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Only channel administrators may change a module's status.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*(?:(?:(?:de)?autoop|promote|demote|enable|disable|add\s+mask|remove\s+mask)\s+(\S+)|(?:list\s+)?ops)\s*$/osi) {
|
||||
$self->say($event, "$event->{'from'}: You have to give a channel, as in \'<command> <who> in <channel>\'.");
|
||||
|
||||
# XXX next two could be merged, maybe.
|
||||
} elsif ($message =~ /^\s*op\s*meh?[!1.,\s]*(?:now\s+)?(?:please|(b+[iea]+t+c+h+))?\s*[.!1]*\s*$/osi) {
|
||||
if ($event->{'userName'}) {
|
||||
if ($event->{'channel'}) {
|
||||
unless ($self->checkOpping($event, $event->{'channel'}, $event->{'from'}, $self->isAdmin($event))) {
|
||||
if ($1) {
|
||||
$self->say($event, "$event->{'from'}: No way, beetch!");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Sorry, you are not on my auto-op list.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: You have to use this command in public.");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: You haven't authenticated yet. See 'help auth' for details.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*(?:please\s+)?op\s*me(?:\s+in\s+(\S+)|\s+everywhere)?[\s!1.]*\s*$/osi) {
|
||||
if (($self->{'allowPrivateOpRequests'}) or ($self->isAdmin($event))) {
|
||||
if ($1) {
|
||||
$self->checkOpping($event, lc($1), $event->{'from'}, $self->isAdmin($event));
|
||||
} else {
|
||||
foreach (@{$self->{'channels'}}) {
|
||||
$self->checkOpping($event, $_, $event->{'from'}, $self->isAdmin($event));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Sorry, but no. Try \'help opme\' for details on commansyntax.");
|
||||
}
|
||||
} else {
|
||||
my $parentResult = $self->SUPER::Told(@_);
|
||||
return $parentResult < 2 ? 2 : $parentResult;
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything ese.
|
||||
} elsif ($event->{'level'} == 2) {
|
||||
if (defined($event->{'God_channel'})) {
|
||||
$event->{'God_channel_rights'} = $self->isChannelAdmin($event, $event->{'God_channel'});
|
||||
}
|
||||
}
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
|
||||
# SpottedJoin - Called when someone joins a channel
|
||||
sub SpottedJoin {
|
||||
my $self = shift;
|
||||
my ($event, $channel, $who) = @_;
|
||||
$self->checkOpping(@_, 0);
|
||||
return $self->SUPER::SpottedJoin(@_); # this should not stop anything else happening
|
||||
}
|
||||
|
||||
# do all channels when someone authenticates
|
||||
sub Authed {
|
||||
my $self = shift;
|
||||
my ($event, $who) = @_;
|
||||
foreach (@{$self->{'channels'}}) {
|
||||
$self->checkOpping($event, $_, $who, 0);
|
||||
}
|
||||
return $self->SUPER::Authed(@_); # this should not stop anything else happening
|
||||
}
|
||||
|
||||
# check is someone is in the opping.
|
||||
sub checkOpping {
|
||||
my $self = shift;
|
||||
my ($event, $channel, $who, $override) = @_;
|
||||
if (($self->isAutoopped($event, $channel)) or ($self->isChannelAdmin($event, $channel)) or ($override)) {
|
||||
$self->mode($event, $channel, '+o', $who);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub isChannelAdmin {
|
||||
my $self = shift;
|
||||
my ($event, $channel) = @_;
|
||||
return (($event->{'userName'}) and
|
||||
(defined($self->{'channelAdmins'}->{$channel})) and
|
||||
($self->{'channelAdmins'}->{$channel} =~ /^(|.*\s+)$event->{'userName'}(\s+.*|)$/s));
|
||||
}
|
||||
|
||||
sub isAutoopped {
|
||||
my $self = shift;
|
||||
my ($event, $channel) = @_;
|
||||
return ((($event->{'userName'}) and
|
||||
(defined($self->{'channelOps'}->{$channel})) and
|
||||
(($self->{'channelOps'}->{$channel} =~ /^(|.*\s+)$event->{'userName'}(\s+.*|)$/s) or
|
||||
($self->{'channelOps'}->{''} =~ /^(|.*\s+)$event->{'userName'}(\s+.*|)$/s))) or
|
||||
($self->isMatchedByMask($event, $channel)));
|
||||
}
|
||||
|
||||
# grrrr -- this insecure feature is here by popular demand
|
||||
sub isMatchedByMask {
|
||||
my $self = shift;
|
||||
my ($event, $channel) = @_;
|
||||
my $masks;
|
||||
$masks .= $self->{'channelOpMasks'}->{$channel} if defined($self->{'channelOpMasks'}->{$channel});
|
||||
$masks .= ' '.$self->{'channelOpMasks'}->{''} if defined($self->{'channelOpMasks'}->{''});
|
||||
if (defined($masks)) {
|
||||
my @masks = split(/ +/os, $masks);
|
||||
my $user = $event->{'user'};
|
||||
foreach my $regexp (@masks) {
|
||||
my $pattern;
|
||||
if ($regexp =~ m/ ^ # start at the start
|
||||
([^!@]+) # nick part
|
||||
\! # nick-username delimiter
|
||||
([^!@]+) # username part
|
||||
\@ # username-host delimiter
|
||||
([^!@]+) # host part
|
||||
$ # end at the end
|
||||
/osx) {
|
||||
|
||||
my $nick = $1;
|
||||
my $user = $2;
|
||||
my $host = $3;
|
||||
|
||||
# This was entered as an IRC hostmask so we need to
|
||||
# translate it into a regular expression.
|
||||
foreach ($nick, $user, $host) {
|
||||
quotemeta($_); # escape regular expression magic
|
||||
s/\\\*/.*/gos; # translate "*" into regexp equivalent
|
||||
}
|
||||
|
||||
# If we don't match the first part of the host-mask
|
||||
# (the user's nick) then we should not op them; we
|
||||
# should just skip to the next mask.
|
||||
next unless $event->{'from'} =~ m/^$nick$/i;
|
||||
|
||||
# ok, create hostmask regexp
|
||||
$pattern = "^$user\@$host\$";
|
||||
} else {
|
||||
# this was entered as a regexp, check it is valid.
|
||||
$pattern = $self->sanitizeRegexp($regexp);
|
||||
}
|
||||
if (($pattern =~ /[^\s.*+]/os) # pattern is non-trivial
|
||||
and ($user =~ /$pattern/si)) { # pattern matches user
|
||||
return 1; # op user (so insecure, sigh)
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub Kicked {
|
||||
my $self = shift;
|
||||
my ($event, $channel) = @_;
|
||||
push(@{$self->{'kickLog'}}, "$event->{'from'} kicked us from $channel"); # XXX karma or something... ;-)
|
||||
return $self->SUPER::Kicked(@_);
|
||||
}
|
||||
|
||||
sub getList {
|
||||
my $self = shift;
|
||||
my ($channel, $list) = @_;
|
||||
my $data;
|
||||
my @list;
|
||||
$data = defined($self->{$list}->{$channel}) ? $self->{$list}->{$channel} : '';
|
||||
$data .= defined($self->{$list}->{''}) ? ' '.$self->{$list}->{''} : '';
|
||||
if ($data =~ /^\s*$/os) {
|
||||
@list = ('(none)');
|
||||
} else {
|
||||
@list = sort(split(/\s+/os, $data));
|
||||
while ((@list) and ($list[0] =~ /^\s*$/)) { shift @list; }
|
||||
}
|
||||
return @list;
|
||||
}
|
||||
|
||||
sub listOps {
|
||||
my $self = shift;
|
||||
my ($event, $channel) = @_;
|
||||
my @admins = $self->getList($channel, 'channelAdmins');
|
||||
my @ops = $self->getList($channel, 'channelOps');
|
||||
my @masks = $self->getList($channel, 'channelOpMasks');
|
||||
|
||||
local $" = ' ';
|
||||
my @output = ();
|
||||
push(@output, "$channel admins: @admins");
|
||||
push(@output, "$channel ops: @ops");
|
||||
if (@masks > 2) {
|
||||
push(@output, "$channel autoop masks:");
|
||||
foreach (@masks) {
|
||||
push(@output, " $_");
|
||||
}
|
||||
} else {
|
||||
push(@output, "$channel autoop masks: @masks");
|
||||
}
|
||||
if (scalar(@output) > $self->{'maxInChannel'}) {
|
||||
foreach (@output) {
|
||||
$self->directSay($event, $_);
|
||||
}
|
||||
$self->channelSay($event, "$event->{'from'}: long list /msg'ed");
|
||||
} else {
|
||||
foreach (@output) {
|
||||
$self->say($event, "$event->{'from'}: $_");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,413 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# Greeting Module #
|
||||
################################
|
||||
|
||||
package BotModules::Greeting;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# SpottedNickChange would be a nice one to do if you
|
||||
# can solve the problem of working out which channel
|
||||
# to say stuff in...
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'A polite module for saying hello and goodbye and so on.',
|
||||
'hi' => 'To greet the bot.',
|
||||
'bye' => 'To say goodbye to the bot.',
|
||||
'ping' => 'To check the bot is alive.',
|
||||
'uptime' => 'Gives the amount of time that the bot has been active.',
|
||||
'seen' => 'Says how long it\'s been since the last time someone was seen. Syntax: seen victim',
|
||||
};
|
||||
}
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['greetings', 1, 1, ['hi %', 'yo %', 'salut %', '%! dude!', '%: hello', '%', 'bonjour %']],
|
||||
['greetingsIndex', 1, 1, 0],
|
||||
['byes', 1, 1, ['seeya %', 'bye %', 'night %', '/me waves goodbye to %']],
|
||||
['byesIndex', 1, 1, 0],
|
||||
['ow', 1, 1, ['%!! stop it!!', '%? You want something?', 'I\'m working! Leave me alone!', 'ow!', 'Leave me out of it!', '%: mean!']],
|
||||
['owIndex', 1, 1, 0],
|
||||
['veryow', 1, 1, ['OOOOWWWW!!!', 'GETOFF!!!', '/me fights back', 'Yikes! I\'m being attacked!!', '/me hits % over the head with a 2-by-4']],
|
||||
['veryowIndex', 1, 1, 0],
|
||||
['yousuck', 1, 1, ['%: no, *you* suck!', '/me pouts', '/me cries', '/me . o O ( now what have i done... )']],
|
||||
['yousuckIndex', 1, 1, 0],
|
||||
['thanks', 1, 1, ['sure thing %', 'np', '%: np', '%: just doing my job!']],
|
||||
['thanksIndex', 1, 1, 0],
|
||||
['listen', 1, 1, ['(*', '%: I\'m listening.', '%?']],
|
||||
['listenIndex', 1, 1, 0],
|
||||
['unhappy', 1, 1, [':)', '/me cries', 'but... but...', '/me is all sad', ':(']],
|
||||
['unhappyIndex', 1, 1, 0],
|
||||
['happy', 1, 1, [':)', '/me smiles']],
|
||||
['happyIndex', 1, 1, 0],
|
||||
['vhappy', 1, 1, ['OOoh! %!', 'I love you too, %.']],
|
||||
['vhappyIndex', 1, 1, 0],
|
||||
['whoami', 1, 1, 'I am a bot. /msg me the word \'help\' for a list of commands.'],
|
||||
['lastrheet', 0, 0, 0], # time of last rheet
|
||||
['rheetbuffer', 1, 1, 10], # max of 1 rheet per this many seconds
|
||||
['rheetMaxEs', 1, 1, 100], # number of es at which to stop responding.
|
||||
['autoGreetMute', 1, 1, []], # channels to mute in
|
||||
['autoGreetings', 1, 1, {}], # people to greet and their greeting
|
||||
['autoGreeted', 0, 0, {}], # people to NOT greet, and the last time
|
||||
['autoGreetedBackoffTime', 1, 1, 20], # how long to not greet people (seconds)
|
||||
['evil', 1, 1, ['c++ is evil', '/me mumbles something about c++ being evil', 'c++ is e-- ah, nevermind.', 'c++ sucks', '/me frowns at %']],
|
||||
['evilIndex', 1, 1, 0],
|
||||
['evilBackoffTime', 1, 1, 36000], # how long to not insult c++ (10 hours by default)
|
||||
['lastEvil', 1, 0, 0], # when the last c++ insult took place
|
||||
['assumeThanksTime', 1, 1, 10], # how long to assume that thanks are directed to us after hearing from them (seconds)
|
||||
['_lastSpoken', 0, 0, {}], # who has spoken to us
|
||||
['seenTimes', 1, 1, {}], # the times that the relevant nicks were last seen active
|
||||
['seenStates', 1, 1, {}], # what the relevant nicks were last seen doing
|
||||
['seenOverrides', 1, 1, {'therapist' => 'Look, dude, I\'m feeling fine, mm\'k?'}], # canned responses
|
||||
['source', 1, 1, 'http://lxr.mozilla.org/mozilla/source/webtools/mozbot/'], # reply to give for CTCP SOURCE
|
||||
);
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
my $now = time();
|
||||
$self->{'_lastSpoken'}->{$event->{'user'}} = $now;
|
||||
if ($event->{'channel'} ne '') {
|
||||
my $channel = $event->{'channel'};
|
||||
$self->{'seenTimes'}->{lc $event->{'from'}} = $now;
|
||||
$self->{'seenStates'}->{lc $event->{'from'}} = "saying '$message' to me in $channel.";
|
||||
}
|
||||
my $me = quotemeta($event->{'bot'}->nick);
|
||||
my $expandedme = join('+', split(//gos, $me)).'+';
|
||||
if ($message =~ /^\s*(?:mornin[g']?|hi|heya?|w+a+[sz]+u+p+|hello|wb|welcome\s+back|greetings|yo(?:\s+dude)?|m+[ay]+(?:\s+m+a+i+n+)?\s+m+a+n+|d+u+d+e+)[?!1.\s]*$/osi) {
|
||||
if ($self->canGreet($event)) {
|
||||
$self->Perform($event, 'greetings');
|
||||
}
|
||||
} elsif ($message =~ /^\s*(?:bye|(?:g?'?|good\s+)night|seeya|ciao)[?!1.\s]*$/osi) {
|
||||
$self->Perform($event, 'byes');
|
||||
} elsif ($message =~ /^\s*say[\s:,"']+(hi|hello|good\s*bye|seeya)(?:\s+to\s+(\S+))(?:[,\s]*please)?[?!1.\s]*$/osi) {
|
||||
if ($2) {
|
||||
$self->say($event, "$2: $1");
|
||||
} else {
|
||||
$self->say($event, "$1");
|
||||
}
|
||||
} elsif ($message =~ /^\s*(?:you\s+(?:really\s+)?suck(?:\s+hard|(?:\s+big)?\s+rocks)?|you(?:\s+a|')re\s+an\s+idiot|i\s+hate\s+you)[?!1.\s]*$/osi) {
|
||||
$self->Perform($event, 'yousuck');
|
||||
} elsif ($message =~ /^\s*(?:oh[!1?.,\s]*)?(?:thanks|thank\s+you|cheers)[\s!1.]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/osi) {
|
||||
$self->Perform($event, 'thanks');
|
||||
} elsif ($message =~ /^\s*(?:good\s+bot[.!1\s]*|you\s+rock|:-?\)|(?:have\s+a\s+)?bot\s*snack[.!1\s]*)\s*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/osi) {
|
||||
$self->Perform($event, 'happy');
|
||||
} elsif ($message =~ /^\s*(?:i|we)\s+love\s+you[.!1\s]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/osi) {
|
||||
$self->Perform($event, 'happy');
|
||||
} elsif ($message =~ /^\s*die[!1.\s]*$/osi) {
|
||||
$self->Perform($event, 'unhappy');
|
||||
} elsif ($message =~ /^\s*(?:how\s+are\s+you|how\s+do\s+you\s+do|how'?s\s+things|are\s+you\s+ok)(?:[?!1.,\s]+$expandedme)?\s*[?!1.\s]*$/osi) {
|
||||
$uptime = $self->days($^T);
|
||||
$self->say($event, "$event->{'from'}: fine thanks! I've been up $uptime so far!");
|
||||
} elsif ($message =~ /^\s*(?:who\s+are\s+you)\s*[?!1.\s]*$/osi) {
|
||||
$self->say($event, "$event->{'from'}: $self->{'whoami'}");
|
||||
} elsif ($message =~ /^\s*(?:up\s*(?:time)|status)?[?!1.\s]*$/osi) {
|
||||
$uptime = $self->days($^T);
|
||||
$self->say($event, "$event->{'from'}: I've been up $uptime.");
|
||||
} elsif ($message =~ /^\s*r+h+e(e+)t+[!1.\s]*$/osi) {
|
||||
if (length($1) < $self->{'rheetMaxEs'}) {
|
||||
$self->say($event, "$event->{'from'}: rhe$1$1t!");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: uh, whatever.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*ping\s*$/osi) {
|
||||
$self->say($event, "$event->{'from'}: pong");
|
||||
} elsif ($message =~ /^\s*!?seen\s+(\S+)[\s?]*$/osi) {
|
||||
$self->DoSeen($event, $1);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Heard {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($event->{'channel'} ne '') {
|
||||
my $channel = $event->{'channel'};
|
||||
$self->{'seenTimes'}->{lc $event->{'from'}} = time();
|
||||
$self->{'seenStates'}->{lc $event->{'from'}} = "saying '$message' in $channel.";
|
||||
}
|
||||
my $me = quotemeta($event->{'bot'}->nick);
|
||||
my $expandedme = join('+', split(//gos, $me)).'+';
|
||||
if ($message =~ /^\s*(?:(?:hi|heya?|w+a+s+u+p+|hello|mornin[g']?|greetings|yo(?:\s+yo)*|bonjour|hoi)\s+$me|$expandedme\s*)[!1\s]*$/si) {
|
||||
if ($self->canGreet($event)) {
|
||||
$self->Perform($event, 'greetings');
|
||||
}
|
||||
} elsif ($message =~ /^\s*(?:bye|(?:g?'?|good\s+)night|seeya|ciao)\s+$me[!1.\s]*$/si) {
|
||||
$self->Perform($event, 'byes');
|
||||
} elsif ($message =~ /^\s*(?:oh[!1?,.\s]*)?(?:thanks|thank\s*you|cheers)\s+$me[\s!1.]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/si) {
|
||||
$self->Perform($event, 'thanks');
|
||||
} elsif (($message =~ /^\s*(?:oh[!1?,.\s]*)?(?:thanks|thank\s*you|cheers)[\s!1.]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/si) and ($self->canAssumeThanks($event))) {
|
||||
$self->Perform($event, 'thanks');
|
||||
} elsif (($message =~ /^\s*(?:good\s+bot)[!1.\s]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/si) and ($self->canAssumeThanks($event))) {
|
||||
$self->Perform($event, 'happy');
|
||||
} elsif (($message =~ /^\s*(?:you\s+(?:really\s+)?suck(?:\s+hard|(?:\s+big)?\s+rocks)?|you(?:\s+a|')re\s+an\s+idiot|i\s+hate\s+you)[?!1.\s]*$/osi) and
|
||||
($self->canAssumeThanks($event))) {
|
||||
$self->Perform($event, 'yousuck');
|
||||
} elsif ($message =~ /^\s*(?:good(?:\s$me)?|yay[\s!1.]*|i\s+love\s+you)\s+$me[\s!1.]*(?:[;:8][-o]?[]()\|O0<>[]\s*)?$/si) {
|
||||
$self->Perform($event, 'happy');
|
||||
} elsif ($message =~ /^\s*(?:$me\s*[.?\/]+)\s*$/si) {
|
||||
$self->Perform($event, 'listen');
|
||||
} elsif ($message =~ /^\s*r+h(e+)t+[!1.\s]*$/osi) {
|
||||
if ((time()-$self->{'lastrheet'}) > $self->{'rheetbuffer'}) {
|
||||
if (length($1) < $self->{'rheetMaxEs'}) {
|
||||
$self->say($event, "rhe$1$1t!");
|
||||
}
|
||||
$self->{'lastrheet'} = time();
|
||||
}
|
||||
} elsif ($message =~ /^.+\s+c\+\+\s+.+$/osi) {
|
||||
if ((time() - $self->{'lastEvil'}) > $self->{'evilBackoffTime'}) {
|
||||
$self->{'lastEvil'} = time();
|
||||
$self->Perform($event, 'evil'); # calls GetNext which calls saveConfig
|
||||
}
|
||||
} elsif ($message =~ /^\s*!seen\s+(\S+)\s*$/osi) {
|
||||
$self->DoSeen($event, $1);
|
||||
} else {
|
||||
return $self->SUPER::Heard(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Felt {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($event->{'channel'} ne '') {
|
||||
my $nick = $event->{'from'};
|
||||
my $channel = $event->{'channel'};
|
||||
$self->{'seenTimes'}->{lc $event->{'from'}} = time();
|
||||
$self->{'seenStates'}->{lc $event->{'from'}} = "saying '* $nick $message' in $channel.";
|
||||
}
|
||||
my $me = quotemeta($event->{'bot'}->nick);
|
||||
if ($message =~ /^\s*(?:pokes|prods)\s+$me[\s!1.]*$/si) {
|
||||
$self->Perform($event, 'ow');
|
||||
} elsif ($message =~ /^\s*(?:stabs|slaps|kicks|kills|hits|punches)\s+$me[\s!1.]*$/si) {
|
||||
$self->Perform($event, 'veryow');
|
||||
} elsif ($message =~ /^\s*lights\s+$me\s+on\s+fire[!1.\s]*$/si) {
|
||||
$self->Perform($event, 'veryow');
|
||||
} elsif ($message =~ /^\s*(?:pats|strokes|pets)\s+$me[!1.\s]*$/si) {
|
||||
$self->Perform($event, 'happy');
|
||||
} elsif ($message =~ /^\s*slaps\s+$me\s+(?:around\s+)?(?:a\s+(?:bit|lot|little|while)\s+)?with\s+a\s+(?:(?:big|fat|large|wet|and)[\s,]+)*trout[\s!1.]*$/si) {
|
||||
$self->Perform($event, 'ow');
|
||||
} elsif ($message =~ /^\s*(?:slaps|kicks|smacks)\s+$me[\s!1.]*$/si) {
|
||||
$self->Perform($event, 'yousuck');
|
||||
} elsif ($message =~ /^\s*(?:glares|stares)\s+at\s+$me[\s!1.]*$/si) {
|
||||
$self->Perform($event, 'yousuck');
|
||||
} elsif ($message =~ /^\s*(?:hugs|kisses)\s+$me[\s!1.]*$/si) {
|
||||
$self->Perform($event, 'vhappy');
|
||||
} else {
|
||||
return $self->SUPER::Felt(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Saw {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($event->{'channel'} ne '') {
|
||||
my $nick = $event->{'from'};
|
||||
my $channel = $event->{'channel'};
|
||||
$self->{'seenTimes'}->{lc $event->{'from'}} = time();
|
||||
$self->{'seenStates'}->{lc $event->{'from'}} = "saying '* $nick $message' in $channel.";
|
||||
}
|
||||
if ($message =~ /^\s*r+h+e(e+)t+s?[!1.\s]*$/osi) {
|
||||
if ((time()-$self->{'lastrheet'}) > $self->{'rheetbuffer'}) {
|
||||
$self->say($event, "rhe$1$1t!");
|
||||
$self->{'lastrheet'} = time();
|
||||
}
|
||||
} elsif (($message =~ /^\s*(?:smiles)\s*[!1.\s]*$/si) and ($self->canAssumeThanks($event))) {
|
||||
$self->Perform($event, 'happy');
|
||||
} else {
|
||||
return $self->SUPER::Felt(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
# SpottedJoin - Called when someone joins a channel
|
||||
sub SpottedJoin {
|
||||
my $self = shift;
|
||||
my ($event, $channel, $who) = @_;
|
||||
return if grep lc($_) eq $channel, @{$self->{'autoGreetMute'}};
|
||||
my $user = $event->{'user'};
|
||||
if ($self->canGreet($event) and $self->{'autoGreetings'}->{$who}) {
|
||||
$self->sayOrEmote($event, $self->Expand($event, $self->{'autoGreetings'}->{$who}));
|
||||
$self->{'autoGreeted'}->{$user} = time();
|
||||
}
|
||||
return 1; # don't block other modules...
|
||||
}
|
||||
|
||||
# SpottedNickChange - Called when someone changes nick
|
||||
sub SpottedNickChange {
|
||||
my $self = shift;
|
||||
my ($event, $from, $to) = @_;
|
||||
$self->{'seenTimes'}->{lc $event->{'from'}} = time();
|
||||
$self->{'seenStates'}->{lc $event->{'from'}} = "changing nick to $to.";
|
||||
return $self->SUPER::SpottedNickChange(@_);
|
||||
}
|
||||
|
||||
sub CTCPPing {
|
||||
my $self = shift;
|
||||
my ($event, $who, $what) = @_;
|
||||
$self->ctcpReply($event, 'PING', $what);
|
||||
}
|
||||
|
||||
sub CTCPSource {
|
||||
my $self = shift;
|
||||
my ($event, $who, $what) = @_;
|
||||
$self->ctcpReply($event, 'SOURCE', $self->{'source'});
|
||||
}
|
||||
|
||||
sub GetNext {
|
||||
my $self = shift;
|
||||
my ($list) = @_;
|
||||
$self->{"${list}Index"} = 0 if $self->{"${list}Index"} > $#{$self->{$list}};
|
||||
my $reply = $self->{$list}->[$self->{"${list}Index"}++];
|
||||
$self->saveConfig();
|
||||
return $reply;
|
||||
}
|
||||
|
||||
sub canGreet {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my $user = $event->{'user'};
|
||||
my $reply = 1;
|
||||
if (defined($self->{'autoGreeted'}->{$user})) {
|
||||
$reply = ((time() - $self->{'autoGreeted'}->{$user}) > $self->{'autoGreetedBackoffTime'});
|
||||
delete($self->{'autoGreeted'}->{$user});
|
||||
}
|
||||
return $reply;
|
||||
}
|
||||
|
||||
sub canAssumeThanks {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my $who = $event->{'user'};
|
||||
return ((defined($self->{'_lastSpoken'}->{$who})) and ((time() - $self->{'_lastSpoken'}->{$who}) <= $self->{'assumeThanksTime'}));
|
||||
}
|
||||
|
||||
sub Perform {
|
||||
my $self = shift;
|
||||
my ($event, $list) = @_;
|
||||
$self->sayOrEmote($event, $self->Expand($event, $self->GetNext($list)));
|
||||
}
|
||||
|
||||
# replaces '%' with the target nick (XXX cannot escape a "%"!!!)
|
||||
sub Expand {
|
||||
my $self = shift;
|
||||
my ($event, $data) = @_;
|
||||
$data =~ s/%/$event->{'from'}/gos;
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub DoSeen {
|
||||
my $self = shift;
|
||||
my ($event, $who) = @_;
|
||||
if (lc $who eq lc $event->{'from'}) {
|
||||
$self->say($event, 'You\'re right here, duh!');
|
||||
} elsif (lc $who eq lc $event->{'nick'}) {
|
||||
$self->say($event, 'I\'m right here, duh!');
|
||||
} elsif (defined($self->{'seenOverrides'}->{$who})) {
|
||||
$self->say($event, $self->{'seenOverrides'}->{$who});
|
||||
} else {
|
||||
my $seconds = $self->{'seenTimes'}->{lc $who};
|
||||
if (defined($seconds)) {
|
||||
my $seconds = time() - $seconds;
|
||||
my $time = '';
|
||||
if ($seconds > 90) {
|
||||
my $minutes = int $seconds / 60;
|
||||
$seconds %= 60;
|
||||
if ($minutes > 90) {
|
||||
my $hours = int $minutes / 60;
|
||||
$minutes %= 60;
|
||||
if ($hours > 36) {
|
||||
my $days = int $hours / 24;
|
||||
$hours %= 24;
|
||||
if ($days > 10) {
|
||||
my $weeks = int $days / 7;
|
||||
$days %= 7;
|
||||
if ($weeks > 10) {
|
||||
# good god, nice connection
|
||||
}
|
||||
if ($weeks != 0) {
|
||||
if ($time ne '') {
|
||||
$time .= ', ';
|
||||
}
|
||||
if ($weeks == 1) {
|
||||
$time .= "$weeks week";
|
||||
} else {
|
||||
$time .= "$weeks weeks";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($days != 0) {
|
||||
if ($time ne '') {
|
||||
$time .= ', ';
|
||||
}
|
||||
if ($days == 1) {
|
||||
$time .= "$days day";
|
||||
} else {
|
||||
$time .= "$days days";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($hours != 0) {
|
||||
if ($time ne '') {
|
||||
$time .= ', ';
|
||||
}
|
||||
if ($hours == 1) {
|
||||
$time .= "$hours hour";
|
||||
} else {
|
||||
$time .= "$hours hours";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($minutes != 0) {
|
||||
if ($time ne '') {
|
||||
$time .= ', ';
|
||||
}
|
||||
if ($minutes == 1) {
|
||||
$time .= "$minutes minute";
|
||||
} else {
|
||||
$time .= "$minutes minutes";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($seconds == 0) {
|
||||
if ($time eq '') {
|
||||
$time .= 'right about now';
|
||||
} else {
|
||||
$time .= ' ago';
|
||||
}
|
||||
} else {
|
||||
if ($time ne '') {
|
||||
$time .= ' and ';
|
||||
}
|
||||
if ($seconds == 1) {
|
||||
$time .= 'a second ago';
|
||||
} elsif ($seconds == 2) {
|
||||
$time .= 'a couple of seconds ago';
|
||||
} else {
|
||||
$time .= "$seconds seconds ago";
|
||||
}
|
||||
}
|
||||
my $what = $self->{'seenStates'}->{lc $who};
|
||||
$self->say($event, "$who was last seen $time, $what");
|
||||
} else {
|
||||
my $n = '';
|
||||
if ($who =~ /^[aeiou]/o) {
|
||||
$n = 'n';
|
||||
}
|
||||
$self->say($event, "I've never seen a$n '$who', sorry.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
################################
|
||||
# Hello World Module #
|
||||
################################
|
||||
|
||||
package BotModules::HelloWorld;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'This is the demo module that says Hello World.',
|
||||
'hi' => 'Requests that the bot emit a hello world string.',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*hi\s*$/osi) {
|
||||
$self->say($event, 'Hello World!');
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
@@ -1,762 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# Infobot Module #
|
||||
################################
|
||||
# some of these ideas are stolen from infobot, of course.
|
||||
# see www.infobot.org
|
||||
|
||||
package BotModules::Infobot;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
use AnyDBM_File;
|
||||
use Fcntl;
|
||||
1;
|
||||
|
||||
# XXX "mozbot is a bot" fails (gets handled as a Tell of "is a bot" :-/)
|
||||
# XXX "who is foo" responds "I don't know what is foo" (should respond "I don't know _who_ is foo")
|
||||
|
||||
# it seems tie() works on scope and not on reference counting, so as
|
||||
# soon as the thing it is tying goes out of scope (even if the variable
|
||||
# in question still has active references) it loses its magic.
|
||||
our $factoids = {'is' => {}, 'are' => {}};
|
||||
tie(%{$factoids->{'is'}}, 'AnyDBM_File', 'factoids-is', O_RDWR|O_CREAT, 0666);
|
||||
tie(%{$factoids->{'are'}}, 'AnyDBM_File', 'factoids-are', O_RDWR|O_CREAT, 0666);
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'Keeps track of factoids and returns them on request. '.
|
||||
'To set factoids, just tell me something in the form \'apple is a company\' or \'apples are fruit\'. '.
|
||||
'To find out about something, say \'apple?\' or \'what are apples\'. '.
|
||||
'To correct me, you can use any of: \'no, apple is a fruit\', \'apple =~ s/company/fruit/\', or \'apple is also a fruit\'. '.
|
||||
'To make me forget a factoid, \'forget apple\'. '.
|
||||
'You can use \'|\' to separate several alternative answers.',
|
||||
'who' => 'If a definition contains $who, then it will be replaced by the name of the person who asked the question.',
|
||||
'reply' => 'If a definition starts with <reply> then when responding the initial prefix will be skipped. '.
|
||||
'e.g., \'apples are <reply>mm, apples\' will mean that \'what are apples\' will get the response \'mm, apples\'.',
|
||||
'action' => 'If a definition starts with <action> then when responding the definition will be used as an action. '.
|
||||
'e.g., \'apples are <action>eats one\' will mean that \'what are apples\' will get the response \'* bot eats one\'.',
|
||||
'alias' => 'If a definition starts with <alias> then it will be treated as a symlink to whatever follows. '.
|
||||
'e.g., \'crab apples are <alias>apples\' and \'apples are fruit\' will mean that \'what are crab apples\' will get the response \'apples are fruit\'.',
|
||||
'status' => 'Reports on how many factoids are in the database.',
|
||||
'tell' => 'Make me tell someone something. e.g., \'tell pikachu what apples are\' or \'tell fred about me\'.',
|
||||
'literal' => 'To find out exactly what is stored for an entry apples, you would say to me: literal apples',
|
||||
'remember' => 'If you are having trouble making me remember something (for example \'well, foo is bar\' '.
|
||||
'getting treated as \'foo\' is \'bar\'), then you can prefix your statement with \'remember:\' '.
|
||||
'(following the \'no,\' if you are changing an entry). For example, \'remember: well, foo is bar\'. '.
|
||||
'Note that \'well, foo?\' is treated as \'what is foo\' not is \'what is well, foo\', so this is not always useful.',
|
||||
'no' => 'To correct an entry, prefix your statement with \'no,\'. '.
|
||||
'For example, \'no, I am good\' to correct your entry from \'is bad\' to \'is good\'. :-)',
|
||||
};
|
||||
}
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['autoLearn', 1, 1, ['*']], # in the auto* variables, '*' means 'all channels'
|
||||
['autoHelp', 1, 1, ['*']],
|
||||
['autoEdit', 1, 1, []],
|
||||
['neverLearn', 1, 1, []], # the never* variables override the auto* variables
|
||||
['neverHelp', 1, 1, []],
|
||||
['neverEdit', 1, 1, []],
|
||||
['autoIgnore', 1, 1, []], # list of nicks for which to always turn off auto*
|
||||
['teachers', 1, 1, []], # list of users who may teach, leave blank to allow anyone to teach
|
||||
['factoidPositions', 0, 0, {'is' => {}, 'are' => {}}],
|
||||
['friendBots', 1, 1, []],
|
||||
['researchNotes', 0, 0, {}],
|
||||
['pruneDelay', 1, 1, 120], # how frequently to look through the research notes and remove expired items
|
||||
['queryTimeToLive', 1, 1, 600], # queries can be remembered up to ten minutes by default
|
||||
['dunnoTimeToLive', 1, 1, 604800], # DUNNO queries can be remembered up to a week by default
|
||||
['noIdeaDelay', 1, 1, 2], # how long to wait before admitting lack of knowledge
|
||||
['questions', 0, 0, 0], # how many questions there have been since the last load
|
||||
['edits', 0, 0, 0], # how many edits (learning, editing, forgetting) there have been since the last load
|
||||
['interbots', 0, 0, 0], # how many times we have spoken with other bots
|
||||
['maxInChannel', 1, 1, 200], # beyond this answers are /msged
|
||||
);
|
||||
}
|
||||
|
||||
# Schedule - called when bot connects to a server, to install any schedulers
|
||||
# use $self->schedule($event, $delay, $times, $data)
|
||||
# where $times is 1 for a single event, -1 for recurring events,
|
||||
# and a positive number for an event that occurs that many times.
|
||||
sub Schedule {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
$self->schedule($event, \$self->{'pruneDelay'}, -1, 'pruneInfobot');
|
||||
$self->SUPER::Schedule($event);
|
||||
}
|
||||
|
||||
sub unload {
|
||||
my $self = shift;
|
||||
$self->SUPER::unload(@_);
|
||||
# just to make sure...
|
||||
untie(%{$factoids->{'is'}});
|
||||
untie(%{$factoids->{'are'}});
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*status[?\s]*$/osi) {
|
||||
my $sum = $self->countFactoids();
|
||||
my $questions = $self->{'questions'} == 1 ? "$self->{'questions'} question" : "$self->{'questions'} questions";
|
||||
my $edits = $self->{'edits'} == 1 ? "$self->{'edits'} edit" : "$self->{'edits'} edits";
|
||||
my $interbots = $self->{'interbots'} == 1 ? "$self->{'interbots'} time" : "$self->{'interbots'} times";
|
||||
my $friends = @{$self->{'friendBots'}} == 1 ? (scalar(@{$self->{'friendBots'}}).' bot friend') : (scalar(@{$self->{'friendBots'}}).' bot friends');
|
||||
$self->targettedSay($event, "I have $sum factoids in my database and $friends to help me answer questions. ".
|
||||
"Since the last reload, I've been asked $questions, performed $edits, and spoken with other bots $interbots.", 1);
|
||||
} elsif ($event->{'channel'} eq '' and $message =~ /^:INFOBOT:DUNNO <(\S+)> (.*)$/) {
|
||||
$self->ReceivedDunno($event, $1, $2);
|
||||
} elsif ($event->{'channel'} eq '' and $message =~ /^:INFOBOT:QUERY <(\S+)> (.*)$/) {
|
||||
$self->ReceivedQuery($event, $2, $1);
|
||||
} elsif ($event->{'channel'} eq '' and $message =~ /^:INFOBOT:REPLY <(\S+)> (.+?) =(is|are)?=> (.*)$/) {
|
||||
$self->ReceivedReply($event, $3, $2, $1, $4);
|
||||
} elsif ($message =~ /^\s*literal\s+(.+?)\s*$/) {
|
||||
$self->Literal($event, $1);
|
||||
} elsif (not $self->DoFactoidCheck($event, $message, 1)) {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Baffled {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if (not $self->DoFactoidCheck($event, $message, 2)) {
|
||||
return $self->SUPER::Heard(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Heard {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if (not $self->DoFactoidCheck($event, $message, 0)) {
|
||||
return $self->SUPER::Heard(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub DoFactoidCheck {
|
||||
my $self = shift;
|
||||
my ($event, $message, $direct) = @_;
|
||||
# $direct is one of: 0 = heard, 1 = told, 2 = baffled
|
||||
|
||||
my $shortMessage;
|
||||
if ($message =~ /^\s* (?:(?:well|and|or|yes|[uh]+m*|o+[oh]*[k]+(?:a+y+)?|still|well|so|[ah]+|[oh]+)[:,.!?\s]+|)*
|
||||
(?:(?:geez?|boy|du+des?|golly|gosh|wow|whee|wo+ho+)?[:,.!\s]+|)*
|
||||
(?:(?:heya?|hello|hi)(?:\s+there)?(?:\s+peoples?|\s+kids?|\s+folks?)?[:,!.?\s]+)*
|
||||
(?:(?:geez?|boy|du+des?|golly|gosh|wow|whee|wo+ho+)?[:,.!\s]+|)*
|
||||
(?:(?:(?:stupid\s+)?q(?:uestion)?|basically)[:,.!\s]+)*
|
||||
(?:(?:does\s+)?(?:any|ne)\s*(?:1|one|body)\s+know[,\s]+|)?
|
||||
(.*)
|
||||
\s*$/osix) {
|
||||
$shortMessage = $1;
|
||||
}
|
||||
|
||||
if ($message =~ /^\s*tell\s+(\S+)\s+about\s+me(?:[,\s]+please)?[\s!?.]*$/osi) {
|
||||
$self->GiveFactoid($event,
|
||||
undef, # database
|
||||
$event->{'from'}, # what
|
||||
$direct,
|
||||
$1); # who
|
||||
} elsif ($message =~ /^\s*tell\s+(\S+)\s+about\s+(.+?)(?:[,\s]+please)?[\s!?.]*$/osi) {
|
||||
$self->GiveFactoid($event,
|
||||
undef, # database
|
||||
$2, # what
|
||||
$direct,
|
||||
$1); # who
|
||||
} elsif ($message =~ /^\s*tell\s+(\S+)\s+(?:what|who|where)\s+(?:am\s+I|I\s+am)(?:[,\s]+please)?[\s!?.]*$/osi) {
|
||||
$self->GiveFactoid($event,
|
||||
'is', # database
|
||||
$event->{'from'}, # what
|
||||
$direct,
|
||||
$1); # who
|
||||
} elsif ($message =~ /^\s*tell\s+(\S+)\s+(?:what|who|where)\s+(is|are)\s+(.+?)(?:[,\s]+please)?[\s!?.]*$/osi) {
|
||||
$self->GiveFactoid($event,
|
||||
lc($2), # database
|
||||
$3, # what
|
||||
$direct,
|
||||
$1); # who
|
||||
} elsif ($message =~ /^\s*tell\s+(\S+)\s+(?:what|who|where)\s+(.+?)\s+(is|are)(?:[,\s]+please)?[\s!?.]*$/osi) {
|
||||
$self->GiveFactoid($event,
|
||||
lc($3), # database
|
||||
$2, # what
|
||||
$direct,
|
||||
$1); # who
|
||||
} elsif ($message =~ /^\s*(.+?)\s*=~\s*s?\/(.+?)\/(.*?)\/(i)?(g)?(i)?\s*$/osi) {
|
||||
$self->EditFactoid($event,
|
||||
$1, # subject
|
||||
$2, # first part to remove
|
||||
$3, # second part to remove
|
||||
defined($5), # global?
|
||||
defined($4) || defined($6), # case insensitive?
|
||||
$direct);
|
||||
} elsif ($message =~ /^\s*forget\s+(?:about\s+)?me\s*$/osi) {
|
||||
$self->ForgetFactoid($event, $event->{'from'}, $direct);
|
||||
} elsif ($message =~ /^\s*forget\s+(?:about\s+)?(.+?)\s*$/osi) {
|
||||
$self->ForgetFactoid($event, $1, $direct);
|
||||
} elsif ($shortMessage =~ /^(?:what|where|who) \s+ (is|are) \s+ (.+?) [?\s]* $/osix) {
|
||||
$self->GiveFactoid($event,
|
||||
lc($1), # is/are (optional)
|
||||
$2, # subject
|
||||
$direct);
|
||||
} elsif ($shortMessage =~ /^(?:(?:where|how)\s+can\s+(?:i|one|s?he|we)\s+(?:find|learn|read)(?:\s+about)? | how\s+about | what\'?s) \s+ (.+?) [?\s]* $/osix) {
|
||||
$self->GiveFactoid($event,
|
||||
undef, # is/are (optional)
|
||||
$1, # subject
|
||||
$direct);
|
||||
} elsif ($shortMessage =~ /^(?:what|where|who)\s+ (.+?) (is|are) [?\s]* $/osix) {
|
||||
$self->GiveFactoid($event,
|
||||
lc($2), # is/are (optional)
|
||||
$1, # subject
|
||||
$direct);
|
||||
} elsif ($shortMessage =~ /^(?:what|where|who) \s+ (?:am\s+I|I\s+am) [?\s]* $/osix) {
|
||||
$self->GiveFactoid($event,
|
||||
'is', # am => is
|
||||
$event->{'from'}, # subject
|
||||
$direct);
|
||||
} elsif ($shortMessage =~ /^(no\s*, (\s*$event->{'nick'}\s*,)? \s+)? (?:remember\s*:\s+)? (.+?) \s+ (is|are) \s+ (also\s+)? (.+?) $/six) {
|
||||
# the "remember:" prefix can be used to delimit the start of the actual content, if necessary.
|
||||
$self->SetFactoid($event,
|
||||
defined($1), # replace existing answer?
|
||||
$3, # subject
|
||||
lc($4), # is/are
|
||||
defined($5), # add to existing answer?
|
||||
$6, # object
|
||||
($direct or defined($2)));
|
||||
} elsif ($shortMessage =~ /^(no\s*, (\s*$event->{'nick'}\s*,)? \s+)? (?:remember\s*:\s+)? I \s+ am \s+ (also\s+)? (.+?) $/osix) {
|
||||
# the "remember:" prefix can be used to delimit the start of the actual content, if necessary.
|
||||
$self->SetFactoid($event,
|
||||
defined($1), # replace existing answer?
|
||||
$event->{'from'}, # subject
|
||||
'is', # I am = Foo is
|
||||
defined($2), # add to existing answer?
|
||||
$3, # object
|
||||
$direct);
|
||||
} elsif ((not $direct or $direct == 2) and $shortMessage =~ /^(.+?)\s+(is|are)[?\s]*\?[?\s]*$/osi) {
|
||||
$self->GiveFactoid($event,
|
||||
lc($2), # is/are (optional)
|
||||
$1, # subject
|
||||
$direct);
|
||||
} elsif ((not $direct or $direct == 2) and $shortMessage =~ /^(.+?)[?\s]*\?[?\s]*$/osi) {
|
||||
$self->GiveFactoid($event,
|
||||
undef, # is/are (optional)
|
||||
$1, # subject
|
||||
$direct);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub SetFactoid {
|
||||
my $self = shift;
|
||||
my($event, $replace, $subject, $database, $add, $object, $direct, $fromBot) = @_;
|
||||
if ($direct or $self->allowed($event, 'Learn')) {
|
||||
|
||||
teacher: if (@{$self->{'teachers'}}) {
|
||||
foreach my $user (@{$self->{'teachers'}}) {
|
||||
if ($user eq $event->{'userName'}) {
|
||||
last teacher;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# update the database
|
||||
if (not $replace) {
|
||||
$subject = $self->CanonicalizeFactoid($database, $subject);
|
||||
} else {
|
||||
my $oldSubject = $self->CanonicalizeFactoid($database, $subject);
|
||||
if (defined($factoids->{$database}->{$oldSubject})) {
|
||||
delete($factoids->{$database}->{$oldSubject});
|
||||
}
|
||||
}
|
||||
if ($replace or not defined($factoids->{$database}->{$subject})) {
|
||||
$self->debug("Learning that $subject $database '$object'.");
|
||||
$factoids->{$database}->{$subject} = $object;
|
||||
} elsif (not $add) {
|
||||
my @what = split(/\|/o, $factoids->{$database}->{$subject});
|
||||
local $" = '\' or \'';
|
||||
if (not defined($fromBot)) {
|
||||
if (@what == 1 and $what[0] eq $object) {
|
||||
$self->targettedSay($event, 'Yep, that\'s what I thought. Thanks for confirming it.', $direct);
|
||||
} else {
|
||||
# XXX "that's one of the alternatives, sure..."
|
||||
$self->targettedSay($event, "But $subject $database '@what'...", $direct);
|
||||
}
|
||||
}
|
||||
return 0; # failed to update database
|
||||
} else {
|
||||
$self->debug("Learning that $subject $database also '$object'.");
|
||||
$factoids->{$database}->{$subject} .= "|$object";
|
||||
}
|
||||
if (not defined($fromBot)) {
|
||||
$self->targettedSay($event, 'ok', $direct);
|
||||
}
|
||||
if (defined($self->{'researchNotes'}->{lc($subject)})) {
|
||||
my @queue = @{$self->{'researchNotes'}->{lc($subject)}};
|
||||
foreach my $entry (@queue) {
|
||||
my($eventE, $typeE, $databaseE, $subjectE, $targetE, $directE, $visitedAliasesE, $timeE) = @$entry;
|
||||
if ($typeE eq 'QUERY') {
|
||||
if ((defined($targetE) and $event->{'from'} ne $targetE) or
|
||||
($event->{'from'} ne $eventE->{'from'} and
|
||||
($event->{'channel'} eq '' or $event->{'channel'} ne $eventE->{'channel'}))) {
|
||||
my($how, $what, $propagated) = $self->GetFactoid($eventE, $databaseE, $subjectE,
|
||||
$targetE, $directE, $visitedAliasesE, $event->{'from'});
|
||||
if (defined($how)) {
|
||||
if (defined($targetE)) {
|
||||
$self->debug("I now know what '$subject' $database, so telling $targetE, since $eventE->{'from'} told me to.");
|
||||
} else {
|
||||
$self->debug("I now know what '$subject' $database, so telling $eventE->{'from'} who wanted to know.");
|
||||
}
|
||||
$self->factoidSay($eventE, $how, $what, $directE, $targetE);
|
||||
$entry->[1] = 'OLD';
|
||||
} else {
|
||||
# either $propagated, or database doesn't match requested database, or internal error
|
||||
$self->debug("I now know what '$subject' $database, but for some reason that ".
|
||||
"didn't help me help $eventE->{'from'} who needed to know what '$subjectE' $databaseE.");
|
||||
}
|
||||
}
|
||||
} elsif ($typeE eq 'DUNNO') {
|
||||
my $who = defined($targetE) ? $targetE : $eventE->{'from'};
|
||||
$self->directSay($eventE, ":INFOBOT:REPLY <$who> $subject =$database=> $factoids->{$database}->{$subject}");
|
||||
$entry->[1] = 'OLD';
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->{'edits'}++;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub GiveFactoid {
|
||||
my $self = shift;
|
||||
my($event, $database, $subject, $direct, $target) = @_;
|
||||
if ($direct or $self->allowed($event, 'Help')) {
|
||||
if ($target eq $event->{'nick'}) {
|
||||
$self->targettedSay($event, 'Oh, yeah, great idea, get me to talk to myself.', $direct);
|
||||
} else {
|
||||
if (lc($subject) eq 'you') {
|
||||
# first, skip some words that are handled by other commonly-used modules
|
||||
# in particular, 'who are you' is handled by Greeting.bm
|
||||
return;
|
||||
}
|
||||
$self->{'questions'}++;
|
||||
my($how, $what, $propagated) = $self->GetFactoid($event, $database, $subject, $target, $direct);
|
||||
if (not defined($how)) {
|
||||
$self->scheduleNoIdea($event, $database, $subject, $direct, $propagated);
|
||||
} else {
|
||||
$self->debug("Telling $event->{'from'} about $subject.");
|
||||
$self->factoidSay($event, $how, $what, $direct, $target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub Literal {
|
||||
my $self = shift;
|
||||
my($event, $subject) = @_;
|
||||
my $is = $self->CanonicalizeFactoid('is', $subject);
|
||||
my $are = $self->CanonicalizeFactoid('are', $subject);
|
||||
if (defined($is) or defined($are)) {
|
||||
local $" = '\' or \'';
|
||||
if (defined($factoids->{'is'}->{$is})) {
|
||||
my @what = split(/\|/o, $factoids->{'is'}->{$is});
|
||||
$self->targettedSay($event, "$is is '@what'.", 1);
|
||||
}
|
||||
if (defined($factoids->{'are'}->{$are})) {
|
||||
my @what = split(/\|/o, $factoids->{'are'}->{$is});
|
||||
$self->targettedSay($event, "$are are '@what'.", 1);
|
||||
}
|
||||
} else {
|
||||
$self->targettedSay($event, "I have no record of anything called '$subject'.", 1);
|
||||
}
|
||||
}
|
||||
|
||||
sub scheduleNoIdea {
|
||||
my $self = shift;
|
||||
my($event, $database, $subject, $direct, $propagated) = @_;
|
||||
if (ref($propagated)) {
|
||||
$self->schedule($event, \$self->{'noIdeaDelay'}, 1, 'noIdea', $database, $subject, $direct, $propagated);
|
||||
} else {
|
||||
$self->noIdea($event, $database, $subject, $direct);
|
||||
}
|
||||
}
|
||||
|
||||
sub GetFactoid {
|
||||
my $self = shift;
|
||||
my($event, $originalDatabase, $subject, $target, $direct, $visitedAliases, $friend) = @_;
|
||||
if (not defined($visitedAliases)) {
|
||||
$visitedAliases = {};
|
||||
}
|
||||
my $database;
|
||||
($database, $subject) = $self->FindFactoid($originalDatabase, $subject);
|
||||
if (defined($factoids->{$database}->{$subject})) {
|
||||
my @alternatives = split(/\|/o, $factoids->{$database}->{$subject});
|
||||
my $answer;
|
||||
if (@alternatives) {
|
||||
if (not defined($self->{'factoidPositions'}->{$database}->{$subject})
|
||||
or $self->{'factoidPositions'}->{$database}->{$subject} >= scalar(@alternatives)) {
|
||||
$self->{'factoidPositions'}->{$database}->{$subject} = 0;
|
||||
}
|
||||
$answer = @alternatives[$self->{'factoidPositions'}->{$database}->{$subject}];
|
||||
$self->{'factoidPositions'}->{$database}->{$subject}++;
|
||||
} else {
|
||||
$answer = @alternatives[0];
|
||||
}
|
||||
my $who = defined($target) ? $target : $event->{'from'};
|
||||
$answer =~ s/\$who/$who/go;
|
||||
if ($answer =~ /^<alias>(.*)$/o) {
|
||||
if ($visitedAliases->{$1}) {
|
||||
return ('msg', "see $subject", 0);
|
||||
} else {
|
||||
$visitedAliases->{$subject}++;
|
||||
my($how, $what, $propagated) = $self->GetFactoid($event, undef, $1, $target, $direct, $visitedAliases);
|
||||
if (not defined($how)) {
|
||||
return ('msg', "see $1", $propagated);
|
||||
} else {
|
||||
return ($how, $what, $propagated);
|
||||
}
|
||||
}
|
||||
} elsif ($answer =~ /^<action>/o) {
|
||||
$answer =~ s/^<action>\s*//o;
|
||||
return ('me', $answer, 0);
|
||||
} else {
|
||||
if ($answer =~ /^<reply>/o) {
|
||||
$answer =~ s/^<reply>\s*//o;
|
||||
} else {
|
||||
if (lc($who) eq lc($subject)) {
|
||||
$answer = "you are $answer";
|
||||
} else {
|
||||
$answer = "$subject $database $answer";
|
||||
}
|
||||
if (defined($friend)) {
|
||||
$answer = "$friend knew: $answer";
|
||||
}
|
||||
}
|
||||
return ('msg', $answer, 0);
|
||||
}
|
||||
} else {
|
||||
# we have no idea what this is
|
||||
return (undef, undef, $self->Research($event, $originalDatabase, $subject, $target, $direct, $visitedAliases));
|
||||
}
|
||||
}
|
||||
|
||||
sub CanonicalizeFactoid {
|
||||
my $self = shift;
|
||||
my($database, $subject) = @_;
|
||||
if (not defined($factoids->{$database}->{$subject})) {
|
||||
while (my $key = each %{$factoids->{$database}}) {
|
||||
if (lc($key) eq lc($subject)) {
|
||||
$subject = $key;
|
||||
# can't return or 'each' iterator won't be reset XXX
|
||||
}
|
||||
}
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
|
||||
sub FindFactoid {
|
||||
my $self = shift;
|
||||
my($database, $subject) = @_;
|
||||
if (not defined($database)) {
|
||||
$database = 'is';
|
||||
$subject = $self->CanonicalizeFactoid('is', $subject);
|
||||
if (not defined($factoids->{'is'}->{$subject})) {
|
||||
$subject = $self->CanonicalizeFactoid('are', $subject);
|
||||
if (defined($factoids->{'are'}->{$subject})) {
|
||||
$database = 'are';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$subject = $self->CanonicalizeFactoid($database, $subject);
|
||||
}
|
||||
return ($database, $subject);
|
||||
}
|
||||
|
||||
sub EditFactoid {
|
||||
my $self = shift;
|
||||
my($event, $subject, $search, $replace, $global, $caseInsensitive, $direct) = @_;
|
||||
if ($direct or $self->allowed($event, 'Edit')) {
|
||||
my $database;
|
||||
($database, $subject) = $self->FindFactoid($database, $subject);
|
||||
if (not defined($factoids->{$database}->{$subject})) {
|
||||
$self->targettedSay($event, "Er, I don't know about this $subject thingy...", $direct);
|
||||
return;
|
||||
}
|
||||
$self->debug("Editing the $subject entry.");
|
||||
my @output;
|
||||
foreach my $factoid (split(/\|/o, $factoids->{$database}->{$subject})) {
|
||||
$search = $self->sanitizeRegexp($search);
|
||||
if ($global and $caseInsensitive) {
|
||||
$factoid =~ s/$search/$replace/gi;
|
||||
} elsif ($global) {
|
||||
$factoid =~ s/$search/$replace/g;
|
||||
} elsif ($caseInsensitive) {
|
||||
$factoid =~ s/$search/$replace/i;
|
||||
} else {
|
||||
$factoid =~ s/$search/$replace/;
|
||||
}
|
||||
push(@output, $factoid);
|
||||
}
|
||||
$factoids->{$database}->{$subject} = join('|', @output);
|
||||
$self->targettedSay($event, 'ok', $direct);
|
||||
$self->{'edits'}++;
|
||||
}
|
||||
}
|
||||
|
||||
sub ForgetFactoid {
|
||||
my $self = shift;
|
||||
my($event, $subject, $direct) = @_;
|
||||
if ($direct or $self->allowed($event, 'Edit')) {
|
||||
my $count = 0;
|
||||
my $database;
|
||||
foreach my $db ('is', 'are') {
|
||||
($database, $subject) = $self->FindFactoid($db, $subject);
|
||||
if (defined($factoids->{$database}->{$subject})) {
|
||||
delete($factoids->{$database}->{$subject});
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
if ($count) {
|
||||
$self->targettedSay($event, "I've forgotten what I knew about '$subject'.", $direct);
|
||||
$self->{'edits'}++;
|
||||
} else {
|
||||
$self->targettedSay($event, "I never knew anything about '$subject' in the first place!", $direct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# interbot communications
|
||||
sub Research {
|
||||
my $self = shift;
|
||||
my($event, $database, $subject, $target, $direct, $visitedAliases) = @_;
|
||||
if (not @{$self->{'friendBots'}}) {
|
||||
# no bots to ask, bail out
|
||||
return 0;
|
||||
}
|
||||
# now check that we need to ask the bots about it:
|
||||
my $asked = 0;
|
||||
if (not defined($self->{'researchNotes'}->{$subject})) {
|
||||
$self->{'researchNotes'}->{$subject} = [];
|
||||
} else {
|
||||
entry: foreach my $entry (@{$self->{'researchNotes'}->{lc($subject)}}) {
|
||||
my($eventE, $typeE, $databaseE, $subjectE, $targetE, $directE, $visitedAliasesE, $timeE) = @$entry;
|
||||
if ($typeE eq 'QUERY') {
|
||||
$asked++; # at least one bot was already asked quite recently
|
||||
if ((defined($targetE) and lc($targetE) eq lc($targetE)) or
|
||||
(not defined($targetE) and lc($event->{'from'}) eq lc($eventE->{'from'}))) {
|
||||
# already queued
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# remember to tell these people about $subject if we ever find out about it:
|
||||
my $entry = [$event, 'QUERY', $database, $subject, $target, $direct, $visitedAliases, time()];
|
||||
push(@{$self->{'researchNotes'}->{lc($subject)}}, $entry);
|
||||
my $who = defined($target) ? $target : $event->{'from'};
|
||||
if (not $asked) {
|
||||
# not yet asked, so ask each bot about $subject
|
||||
foreach my $bot (@{$self->{'friendBots'}}) {
|
||||
local $event->{'from'} = $bot;
|
||||
$self->directSay($event, ":INFOBOT:QUERY <$who> $subject");
|
||||
}
|
||||
$self->{'interbots'}++;
|
||||
return $entry; # return reference to entry so that we can check if it has been replied or not
|
||||
} else {
|
||||
return $asked;
|
||||
}
|
||||
}
|
||||
|
||||
sub ReceivedReply {
|
||||
my $self = shift;
|
||||
my($event, $database, $subject, $target, $object) = @_;
|
||||
$self->{'interbots'}++;
|
||||
if (not $self->SetFactoid($event, 0, $subject, $database, 0, $object, 1, 1) and
|
||||
defined($self->{'researchNotes'}->{lc($subject)})) {
|
||||
# we didn't believe $event->{'from'}, but we might as well
|
||||
# tell any users that were wondering.
|
||||
foreach my $entry (@{$self->{'researchNotes'}->{lc($subject)}}) {
|
||||
my($eventE, $typeE, $databaseE, $subjectE, $targetE, $directE, $visitedAliasesE, $timeE) = @$entry;
|
||||
if ($typeE eq 'QUERY') {
|
||||
$self->factoidSay($eventE, 'msg', "According to $event->{'from'}, $subject $database '$object'.", $directE, $targetE);
|
||||
} elsif ($typeE eq 'DUNNO') {
|
||||
my $who = defined($targetE) ? $targetE : $eventE->{'from'};
|
||||
$self->directSay($eventE, ":INFOBOT:REPLY <$who> $subject =$database=> $object");
|
||||
}
|
||||
$entry->[1] = 'OLD';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub ReceivedQuery {
|
||||
my $self = shift;
|
||||
my($event, $subject, $target) = @_;
|
||||
$self->{'interbots'}++;
|
||||
if (not $self->tellBot($event, $subject, $target)) {
|
||||
# in the spirit of embrace-and-extend, we're going to say that
|
||||
# :INFOBOT:DUNNO means "I don't know, but if you ever find
|
||||
# out, please tell me".
|
||||
$self->directSay($event, ":INFOBOT:DUNNO <$event->{'nick'}> $subject");
|
||||
}
|
||||
}
|
||||
|
||||
sub ReceivedDunno {
|
||||
my $self = shift;
|
||||
my($event, $target, $subject) = @_;
|
||||
$self->{'interbots'}++;
|
||||
if (not $self->tellBot($event, $subject, $target)) {
|
||||
# store the request
|
||||
push(@{$self->{'researchNotes'}->{lc($subject)}}, [$event, 'DUNNO', undef, $1, $target, 0, {}, time()]);
|
||||
}
|
||||
}
|
||||
|
||||
sub tellBot {
|
||||
my $self = shift;
|
||||
my($event, $subject, $target) = @_;
|
||||
my $count = 0;
|
||||
my $database;
|
||||
foreach my $db ('is', 'are') {
|
||||
($database, $subject) = $self->FindFactoid($db, $subject);
|
||||
if (defined($factoids->{$database}->{$subject})) {
|
||||
$self->directSay($event, ":INFOBOT:REPLY <$target> $subject =$database=> $factoids->{$database}->{$subject}");
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my ($event, @data) = @_;
|
||||
if ($data[0] eq 'pruneInfobot') {
|
||||
my $now = time();
|
||||
foreach my $key (keys %{$self->{'researchNotes'}}) {
|
||||
my @new;
|
||||
foreach my $entry (@{$self->{'researchNotes'}->{$key}}) {
|
||||
my($eventE, $typeE, $databaseE, $subjectE, $targetE, $directE, $visitedAliasesE, $timeE) = @$entry;
|
||||
if (($typeE eq 'QUERY' and ($now - $timeE) < $self->{'queryTimeToLive'}) or
|
||||
($typeE eq 'DUNNO' and ($now - $timeE) < $self->{'dunnoTimeToLive'})) {
|
||||
push(@new, $entry);
|
||||
}
|
||||
}
|
||||
if (@new) {
|
||||
$self->{'researchNotes'}->{$key} = \@new;
|
||||
} else {
|
||||
delete($self->{'researchNotes'}->{$key});
|
||||
}
|
||||
}
|
||||
} elsif ($data[0] eq 'noIdea') {
|
||||
my(undef, $database, $subject, $direct, $propagated) = @data;
|
||||
my($eventE, $typeE, $databaseE, $subjectE, $targetE, $directE, $visitedAliasesE, $timeE) = @$propagated;
|
||||
# in theory, $eventE = $event, $databaseE = $database,
|
||||
# $subjectE = $subject, $targetE depends on if this was
|
||||
# triggered by a tell, $directE = $direct, $visitedAliasesE is
|
||||
# opaque, and $timeE is opaque.
|
||||
if ($typeE ne 'OLD') {
|
||||
$self->noIdea($event, $database, $subject, $direct);
|
||||
}
|
||||
} else {
|
||||
$self->SUPER::Scheduled($event, @data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# internal helper routines
|
||||
|
||||
sub factoidSay {
|
||||
my $self = shift;
|
||||
my($event, $how, $what, $direct, $target) = @_;
|
||||
if (defined($target)) {
|
||||
$self->targettedSay($event, "told $target", 1);
|
||||
my $helper = $event->{'from'};
|
||||
local $event->{'from'} = $target;
|
||||
if ($how eq 'me') {
|
||||
$self->directEmote($event, $what);
|
||||
} else {
|
||||
if (length($what)) {
|
||||
$self->directSay($event, "$helper wanted you to know: $what");
|
||||
}
|
||||
}
|
||||
} elsif ($how eq 'me') {
|
||||
$self->emote($event, $what);
|
||||
} else {
|
||||
if ($event->{'channel'} eq '' or length($what) < $self->{'maxInChannel'}) {
|
||||
$self->targettedSay($event, $what, 1);
|
||||
} else {
|
||||
if ($direct) {
|
||||
$self->targettedSay($event, substr($what, 0, $self->{'maxInChannel'}) . '... (rest /msged)' , 1);
|
||||
$self->directSay($event, $what);
|
||||
} else {
|
||||
$self->targettedSay($event, substr($what, 0, $self->{'maxInChannel'}) . '... (there is more; ask me in a /msg)' , 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub targettedSay {
|
||||
my $self = shift;
|
||||
my($event, $message, $direct) = @_;
|
||||
if ($direct and length($message)) {
|
||||
if ($event->{'channel'} ne '') {
|
||||
$message = "$event->{'from'}: $message";
|
||||
}
|
||||
$self->say($event, $message);
|
||||
}
|
||||
}
|
||||
|
||||
sub countFactoids {
|
||||
my $self = shift;
|
||||
# don't want to use keys() as that would load the whole database index into memory.
|
||||
my $sum = 0;
|
||||
while (my $factoid = each %{$factoids->{'is'}}) { $sum++; }
|
||||
while (my $factoid = each %{$factoids->{'are'}}) { $sum++; }
|
||||
return $sum;
|
||||
}
|
||||
|
||||
sub allowed {
|
||||
my $self = shift;
|
||||
my($event, $type) = @_;
|
||||
if ($event->{'channel'} ne '') {
|
||||
foreach my $user (@{$self->{'autoIgnore'}}) {
|
||||
if ($user eq $event->{'from'}) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
foreach my $channel (@{$self->{"never$type"}}) {
|
||||
if ($channel eq $event->{'channel'} or
|
||||
$channel eq '*') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
foreach my $channel (@{$self->{"auto$type"}}) {
|
||||
if ($channel eq $event->{'channel'} or
|
||||
$channel eq '*') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub noIdea {
|
||||
my $self = shift;
|
||||
my($event, $database, $subject, $direct) = @_;
|
||||
if (lc($subject) eq lc($event->{'from'})) {
|
||||
$self->targettedSay($event, "Sorry, I've no idea who you are.", $direct);
|
||||
} else {
|
||||
if (not defined($database)) {
|
||||
$database = 'might be';
|
||||
}
|
||||
$self->targettedSay($event, "Sorry, I've no idea what '$subject' $database.", $direct);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
######################################
|
||||
# Infobot Factoid Import/Export Tool #
|
||||
######################################
|
||||
|
||||
use strict;
|
||||
use AnyDBM_File;
|
||||
use Fcntl;
|
||||
|
||||
if (not @ARGV == 2) {
|
||||
&use();
|
||||
} else {
|
||||
my $command = shift @ARGV;
|
||||
my $filename = shift @ARGV;
|
||||
if ($command eq '-d') {
|
||||
&dump($filename);
|
||||
} elsif ($command eq '-i') {
|
||||
&import($filename);
|
||||
} else {
|
||||
&use();
|
||||
}
|
||||
}
|
||||
|
||||
sub use {
|
||||
print "\n";
|
||||
print " usage: $0 -d dbname\n";
|
||||
print " prints out an ascii flat file of the database listed.\n";
|
||||
print " dbname should be the basename of the db, e.g.\n";
|
||||
print " $0 -d ../factoids-is > is.fact\n";
|
||||
print " $0 -d ../factoids-are > are.fact\n";
|
||||
print "\n";
|
||||
print " $0 -i dbname\n";
|
||||
print " prints out an ascii flat file of the database listed.\n";
|
||||
print " dbname should be the basename of the db, e.g.\n";
|
||||
print " $0 -i ../factoids-is < chemicals.fact\n";
|
||||
print " $0 -i ../factoids-is < is.fact\n";
|
||||
print " $0 -i ../factoids-are < are.fact\n";
|
||||
print "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sub dump {
|
||||
my %db;
|
||||
tie(%db, 'AnyDBM_File', shift, O_RDONLY, 0666);
|
||||
while (my ($key, $val) = each %db) {
|
||||
chomp $val;
|
||||
print "$key => $val\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub import {
|
||||
my %db;
|
||||
tie(%db, 'AnyDBM_File', shift, O_WRONLY|O_CREAT, 0666);
|
||||
while (<STDIN>) {
|
||||
chomp;
|
||||
unless (m/\s*(.+?)\s+=(?:is=|are=)?>\s+(.+?)\s*$/o) {
|
||||
m/\s*(.+?)\s+(?:is|are)?\s+(.+?)\s*$/o;
|
||||
}
|
||||
if (length($1) and length($2)) {
|
||||
if (defined($db{$1})) {
|
||||
if (not $db{$1} =~ m/^(|.*\|)\Q$2\E(|.*\|)$/s) {
|
||||
$db{$1} .= "|$2";
|
||||
}
|
||||
} else {
|
||||
$db{$1} = $2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
The Infobot Protocol
|
||||
====================
|
||||
|
||||
Reverse engineered from infobot 0.45.3 by Ian Hickson.
|
||||
|
||||
|
||||
QUERY
|
||||
-----
|
||||
|
||||
If a bot is asked something by a user and does not know the answer, it
|
||||
may send queries to all the bots it knows. Queries must be in private
|
||||
messages and should have the following form:
|
||||
|
||||
:INFOBOT:QUERY <target> subject
|
||||
|
||||
...where "target" is the name of the user who sent the query in the
|
||||
first place, and "subject" is the question that was asked.
|
||||
|
||||
In reality, "target" may be any string of non-whitespace character, so
|
||||
it could be used as an internal ID.
|
||||
|
||||
A bot receiving a QUERY message must not try to contact the user given
|
||||
by "target" (that string should be treated as opaque) and must not
|
||||
make any assumptions about the "subject" string (it could contain
|
||||
*anything*, including high bit characters and the works).
|
||||
|
||||
It is an error for the "subject" string to contain either "=is=>" or
|
||||
"=are=>". Receiving bots may ignore this error, however.
|
||||
|
||||
Bot authors should carefully consider the potential for cascades
|
||||
before writing bots that chain QUERY messages. (As in, send out QUERY
|
||||
messages if they are unable to respond to a QUERY message themselves).
|
||||
In general, this is not a recommended behaviour.
|
||||
|
||||
Bot authors are urged to write protection into their bots to avoid
|
||||
being affected by poorly written bots that cause cascades.
|
||||
|
||||
|
||||
REPLY
|
||||
-----
|
||||
|
||||
Upon receiving a QUERY message, a bot may, if it has information on
|
||||
"subject", opt to send a private message back to the originating bot
|
||||
in the form of a REPLY message. Bots must not send unsolicited REPLY
|
||||
messages. The form of the REPLY message is:
|
||||
|
||||
:INFOBOT:REPLY <target> subject =database=> object
|
||||
|
||||
...where "target" is the string of the same name from the original
|
||||
QUERY message, "subject" is the second string from the original QUERY
|
||||
message, "database" is one of "is" or "are" depending on the whether
|
||||
"subject" is determined to be singular or plural respectively, and
|
||||
"object" is the string that should be assumed to be the answer to
|
||||
"subject". The string may contain special formatting codes, these are
|
||||
described below.
|
||||
|
||||
Upon receiving a REPLY message, bots should first check that they are
|
||||
expecting one. If they are, the user identified by the "target" string
|
||||
should be contacted and given the information represented by the
|
||||
"object" string. (Remember that the "target" string need not actually
|
||||
be the nick of the original user; it could be an internal key that
|
||||
indirectly identifies a user.)
|
||||
|
||||
Bots should carefully check the integrity and authenticity of the
|
||||
"target" string, and must check that "database" is one of "is" or
|
||||
"are". The "subject" string ends at the first occurance of either
|
||||
"=is=>" or "=are=>". It is *not* an error for the "object" string to
|
||||
contain either of those substrings.
|
||||
|
||||
Bots may opt to store the information given by a REPLY request so that
|
||||
future questions may be answered without depending on other bots.
|
||||
|
||||
It is suggested that bots credit which bot actually knew the
|
||||
information when reporting back to the user.
|
||||
|
||||
|
||||
DUNNO
|
||||
-----
|
||||
|
||||
(This is not part of the original infobot protocol. And is, as of
|
||||
2002-02-05, only supported by the mozbot2 Infobot module.)
|
||||
|
||||
Upon receiving a QUERY message, a bot may, if it has no information on
|
||||
the "subject" in question, reply with a DUNNO message. This message
|
||||
has basically the same form as the QUERY message:
|
||||
|
||||
:INFOBOT:DUNNO <target> subject
|
||||
|
||||
The DUNNO message indicates that the bot is not aware of the answer to
|
||||
the question, but would like to be informed of the answer, should the
|
||||
first bot ever find out about it. The "target" string should, as with
|
||||
the QUERY string, be considered opaque.
|
||||
|
||||
Upon receiving a DUNNO message, there are several possible responses.
|
||||
If the bot is aware of the answer to "subject", then it should treat
|
||||
the DUNNO message as if it was a QUERY message (typically resulting in
|
||||
a REPLY message). This can occur if, for example, another bot has sent
|
||||
a REPLY to the original QUERY before this bot has had the chance to
|
||||
send the DUNNO message.
|
||||
|
||||
If the first bot still doesn't know the answer, however, it may store
|
||||
the DUNNO request internally. If, at a future time, the bot is
|
||||
informed (either directly by a user or through a REPLY message) about
|
||||
the answer to "subject", then it may send a REPLY message to the bot
|
||||
that sent the DUNNO request, informing the bot of the value it learnt.
|
||||
|
||||
|
||||
SPECIAL STRINGS
|
||||
---------------
|
||||
|
||||
The "object" string in the REPLY message may contain several special
|
||||
flags.
|
||||
|
||||
$who
|
||||
If the string contains the string "$who" then, when the string is
|
||||
given to the user, it should be replaced by the name of the user.
|
||||
|
||||
|
|
||||
Multiple alternative replies may be encoded in one reply, those
|
||||
should be separated by a vertical bar.
|
||||
|
||||
<reply>
|
||||
If the string is prefixed by "<reply>" then the string should not
|
||||
be prefixed by "subject is" or "subject are" as usual.
|
||||
|
||||
<action>
|
||||
The string should be returned via a CTCP ACTION.
|
||||
|
||||
<alias>
|
||||
The string should be taken as the name of another entry to look up.
|
||||
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
In these examples, A, B and C are bots, and x, y and z are people.
|
||||
|
||||
The first example shows a simple case of one bots asking two other
|
||||
bots for help, one of which gives a reply and the other of which says
|
||||
it has no idea.
|
||||
|
||||
+-------- originator of private message
|
||||
|
|
||||
| +--- target of private message
|
||||
| |
|
||||
V V
|
||||
z -> A: what is foo?
|
||||
A -> z: I have no idea.
|
||||
A -> B: :INFOBOT:QUERY <z> foo
|
||||
A -> C: :INFOBOT:QUERY <z> foo
|
||||
B -> A: :INFOBOT:REPLY <x> foo =is=> bar
|
||||
C -> A: :INFOBOT:DUNNO <C> foo
|
||||
A -> x: B knew: foo is bar
|
||||
A -> C: :INFOBOT:REPLY <C> foo =is=> bar
|
||||
|
||||
Note how the DUNNO in this case comes after the REPLY and thus is
|
||||
immediately answered.
|
||||
|
||||
The next example uses <alias>. One bot knows the answer to the
|
||||
question as an alias to another word, but when the original bot asks
|
||||
about _that_ word, it is the second bot that can help.
|
||||
|
||||
z -> A: what is foo?
|
||||
A -> z: I have no idea.
|
||||
A -> B: :INFOBOT:QUERY <z> foo
|
||||
A -> C: :INFOBOT:QUERY <z> foo
|
||||
B -> A: :INFOBOT:REPLY <x> foo =is=> <alias>bar
|
||||
C -> A: :INFOBOT:DUNNO <C> foo
|
||||
A -> B: :INFOBOT:QUERY <z> bar
|
||||
A -> C: :INFOBOT:QUERY <z> bar
|
||||
A -> C: :INFOBOT:REPLY <C> foo =is=> <alias>bar
|
||||
B -> A: :INFOBOT:DUNNO <B> bar
|
||||
C -> A: :INFOBOT:REPLY <x> bar =is=> baz
|
||||
A -> z: C knew: bar is baz
|
||||
A -> B: :INFOBOT:REPLY <B> bar =is=> baz
|
||||
|
||||
Note how the credit actually goes to the second bot. A better bot
|
||||
might remember all the bots involved and credit all of them. A better
|
||||
bot might also remember what the original question was and reply "foo
|
||||
is baz" instead of "bar is baz".
|
||||
|
||||
Next we have some examples of special codes. If we have:
|
||||
|
||||
foo is bar|<alias>baz|<reply>foo to you too|<action>foos|$who
|
||||
baz is foo
|
||||
|
||||
...then the following are valid responses when asked about foo:
|
||||
|
||||
<A> foo is bar
|
||||
<A> baz is foo
|
||||
<A> foo to you too
|
||||
* A foos
|
||||
<A> foo is z
|
||||
|
||||
-- end --
|
||||
@@ -1,196 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# Karma Module #
|
||||
################################
|
||||
|
||||
package BotModules::Karma;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['scores', 1, 1, {}], # nick => total karma.
|
||||
['privateScores', 1, 1, {}], # nick => nick karma nick karma...
|
||||
['secondsDelayRequired', 1, 1, 20],
|
||||
['_lastspoken', 0, 0, {}], # nick => nick => time
|
||||
);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'A karma tracker. If you have authenticated (using the \'auth\' command) then it will also keep track of your own setting of people\'s karma, as well as the total of everyone\'s settings.',
|
||||
'++' => 'Increase someone\'s karma. Syntax: victim++',
|
||||
'--' => 'Decrease someone\'s karma. Syntax: victim++',
|
||||
'rank' => 'Find someone\'s karma level. Omit the victim\'s name to get a complete listing of everyone\'s karma (long). Syntax: \'rank victim\' or just \'rank\'',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^(\S+)\+\+$/os) {
|
||||
$self->ChangeKarma($event, $1, 1);
|
||||
} elsif ($message =~ /^(\S+)\-\-$/os) {
|
||||
$self->ChangeKarma($event, $1, -1);
|
||||
} elsif ($message =~ /^\s*(?:karma\s+)?ranks?[?\s]*$/os) {
|
||||
$self->ReportKarmaRanks($event, $1);
|
||||
} elsif ($message =~ /^\s*(\S+)\s+(?:karma\s+)?rank[?\s]*$/os or
|
||||
$message =~ /^\s*(?:karma\s+)?rank\s+(\S+)[?\s]*$/os) {
|
||||
$self->ReportKarma($event, $1);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub Heard {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^(\S*[^-+\s])\+\+$/os) {
|
||||
$self->ChangeKarma($event, $1, 1);
|
||||
} elsif ($message =~ /^(\S*[^-+\s])\-\-$/os) {
|
||||
$self->ChangeKarma($event, $1, -1);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub ChangeKarma {
|
||||
my $self = shift;
|
||||
my ($event, $who, $delta) = @_;
|
||||
$self->debug("$who += $delta requested");
|
||||
if ((defined($self->{'_lastSpoken'}->{$event->{'user'}})) and
|
||||
(defined($self->{'_lastSpoken'}->{$event->{'user'}}->{lc $who})) and
|
||||
((time() - $self->{'_lastSpoken'}->{$event->{'user'}}->{lc $who}) <= $self->{'secondsDelayRequired'})) {
|
||||
$self->{'_lastSpoken'}->{$event->{'user'}}->{lc $who} = $self->{'_lastSpoken'}->{$event->{'user'}}->{lc $who}+5;
|
||||
my $delay = $self->{'secondsDelayRequired'} - (time() - $self->{'_lastSpoken'}->{$event->{'user'}}->{lc $who});
|
||||
$self->directSay($event, "You will have to wait another $delay seconds before being able to change ${who}'s karma.");
|
||||
} else {
|
||||
if (not defined($self->{'_lastSpoken'}->{$event->{'user'}})) {
|
||||
$self->{'_lastSpoken'}->{$event->{'user'}} = {};
|
||||
}
|
||||
$self->{'_lastSpoken'}->{$event->{'user'}}->{lc $who} = time();
|
||||
if (lc $event->{'from'} eq lc $who) {
|
||||
if ($delta > 0) {
|
||||
$delta = -$delta;
|
||||
}
|
||||
}
|
||||
if ($event->{'channel'} ne '') {
|
||||
$self->{'scores'}->{lc $who} += $delta;
|
||||
if ($self->{'scores'}->{lc $who} == 0) {
|
||||
delete($self->{'scores'}->{lc $who});
|
||||
}
|
||||
}
|
||||
$nick = lc $event->{'userName'};
|
||||
if ($nick) {
|
||||
if (not defined($self->{"privateScores"}->{$nick})) {
|
||||
$self->{"privateScores"}->{$nick} = (lc($who) . ' ' . $delta);
|
||||
} else {
|
||||
my %privateScores = split(' ', $self->{"privateScores"}->{$nick});
|
||||
$privateScores{lc $who} += $delta;
|
||||
if ($privateScores{lc $who} == 0) {
|
||||
delete($privateScores{lc $who});
|
||||
}
|
||||
my @privateScores = %privateScores;
|
||||
local $" = ' ';
|
||||
$self->{'privateScores'}->{$nick} = "@privateScores";
|
||||
}
|
||||
} elsif ($event->{'channel'} eq '') {
|
||||
$self->say($event, 'For private stats, you need to authenticate. Use the \'newuser\' and \'auth\' commands.');
|
||||
}
|
||||
$self->saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
sub ReportKarma {
|
||||
my $self = shift;
|
||||
my ($event, $who) = @_;
|
||||
if (not defined($self->{'scores'}->{lc $who})) {
|
||||
$self->say($event, "$who has no karma.");
|
||||
} else {
|
||||
my $karma = $self->{'scores'}->{lc $who};
|
||||
my @order = sort { $self->{'scores'}->{$b} <=> $self->{'scores'}->{$a} } keys(%{$self->{'scores'}});
|
||||
my $rank = 0;
|
||||
if (scalar(@order)) {
|
||||
user: foreach my $user (@order) {
|
||||
$rank++;
|
||||
if (lc $user eq lc $who) {
|
||||
last user;
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->say($event, "$who has $karma points of karma (rank $rank).");
|
||||
}
|
||||
if ($event->{'channel'} eq '') {
|
||||
$nick = lc $event->{'userName'};
|
||||
if ($nick) {
|
||||
if (not defined($self->{"privateScores"}->{$nick})) {
|
||||
$self->say($event, "You have not given anyone any karma.");
|
||||
} else {
|
||||
my %privateScores = split(' ', $self->{"privateScores"}->{$nick});
|
||||
my $karma = $privateScores{lc $who};
|
||||
|
||||
if (not defined($karma)) {
|
||||
$self->say($event, "You have not given $who any karma.");
|
||||
} else {
|
||||
$self->say($event, "You have given $who $karma points of karma.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$self->say($event, 'For private stats, you need to authenticate. Use the \'newuser\' and \'auth\' commands.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub ReportKarmaRanks {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my @order = sort { $self->{'scores'}->{$b} <=> $self->{'scores'}->{$a} } keys(%{$self->{'scores'}});
|
||||
if (scalar(@order)) {
|
||||
if ($event->{'channel'} ne '') {
|
||||
my $top = $order[0];
|
||||
my $score = $self->{'scores'}->{$top};
|
||||
$self->say($event, "The person with the most karma is $top with $score points.");
|
||||
}
|
||||
$self->directSay($event, "Global rankings:");
|
||||
$self->ReportKarmaRanksList($event, \@order, $self->{'scores'});
|
||||
}
|
||||
if ($event->{'channel'} eq '') {
|
||||
$nick = lc $event->{'userName'};
|
||||
if ($nick) {
|
||||
if (defined($self->{"privateScores"}->{$nick})) {
|
||||
my %privateScores = split(' ', $self->{"privateScores"}->{$nick});
|
||||
@order = sort { $privateScores{$b} <=> $privateScores{$a} } keys(%privateScores);
|
||||
if (scalar(@order)) {
|
||||
$self->directSay($event, "Personal rankings:");
|
||||
$self->ReportKarmaRanksList($event, \@order, \%privateScores);
|
||||
} else {
|
||||
$self->say($event, "I seem to have lost track of the people to which you gave karma points.");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "You have not given anyone karma.");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, 'For private stats, you need to authenticate. Use the \'newuser\' and \'auth\' commands.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub ReportKarmaRanksList {
|
||||
my $self = shift;
|
||||
my($event, $order, $scores) = @_;
|
||||
my $rank = 1;
|
||||
foreach my $entry (@$order) {
|
||||
my $score = $scores->{$entry};
|
||||
$self->directSay($event, "$rank. $entry ($score)");
|
||||
$rank++;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
################################
|
||||
# KeepAlive Module #
|
||||
################################
|
||||
|
||||
package BotModules::KeepAlive;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['delay', 1, 1, 20],
|
||||
['string', 1, 1, 'ping'],
|
||||
['target', 1, 1, '#spam'],
|
||||
);
|
||||
}
|
||||
|
||||
# Schedule - called when bot connects to a server, to install any schedulers
|
||||
# use $self->schedule($event, $delay, $times, $data)
|
||||
# where $times is 1 for a single event, -1 for recurring events,
|
||||
# and a +ve number for an event that occurs that many times.
|
||||
sub Schedule {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
$self->schedule($event, \$self->{'delay'}, -1, 'keepalive');
|
||||
$self->SUPER::Schedule($event);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'This is a simple keep-alive module, it regularly sends text out. This has been known to help with network lag.',
|
||||
} if $self->isAdmin($event);
|
||||
return {};
|
||||
}
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my ($event, @data) = @_;
|
||||
if ($data[0] eq 'keepalive') {
|
||||
local $event->{'target'} = $self->{'target'};
|
||||
$self->say($event, $self->{'string'});
|
||||
} else {
|
||||
$self->SUPER::Scheduled($event, @data);
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# List Module #
|
||||
################################
|
||||
|
||||
package BotModules::List;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# XXX Wipe entire list command
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['lists', 1, 1, {}], # user => 'list name|item 1|item 2||list name|item1|item 2'
|
||||
['preferredLineLength', 1, 1, 80], # the usual
|
||||
['maxItemsInChannel', 1, 1, 20], # max number of items to print in the channel (above this and direct messages are used)
|
||||
);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'A personal list tracker. Store your lists here. You must be authenticated to use this (see \'newuser\'). Use the \'add\' command to add items to a list.',
|
||||
'add' => 'Add an item to a personal list. List names shouldn\'t contain the word \'to\' otherwise things will be too ambiguous. Syntax: \'add <thing to add> to <list name> list\', e.g. \'add bug 5693 to critical bug list\'.',
|
||||
'remove' => 'Remove an item from a personal list. Syntax: \'remove <thing to add> from <list name> list\', e.g. \'remove bug 5693 from critical bug list\'.',
|
||||
'list' => 'List the items in your list. Syntax: \'list items in <name of list> list\', e.g. \'list items in critical bug list\' or just \'critical bug list\'.',
|
||||
'lists' => 'Tells you what lists you have set up.',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*add\s+(\S(?:.*\S)?)\s+to\s+(?:my\s+)?(\S(?:.*\S)?)\s+list[\s!.]*$/osi and $message !~ /\|/o and $event->{'userName'}) {
|
||||
$self->AddItem($event, $1, $2);
|
||||
} elsif ($message =~ /^\s*remove\s+(\S(?:.*\S)?)\s+from\s+(?:my\s+)?(\S(?:.*\S)?)\s+list[\s!.]*$/osi and $message !~ /\|/o and $event->{'userName'}) {
|
||||
$self->RemoveItem($event, $1, $2);
|
||||
} elsif ($message =~ /^\s* (?:examine \s+ |
|
||||
list \s+ items \s+ in \s+ |
|
||||
what (?:\s+is|'s) \s+ (?:in\s+)? )
|
||||
(?: my \s+ | the \s+ )?
|
||||
( \S (?:.*\S)? )
|
||||
\s+ list [\s!?.]* $/osix
|
||||
and $message !~ /\|/o and $event->{'userName'}) {
|
||||
$self->ListItems($event, $1);
|
||||
} elsif ($message =~ /^\s*lists[?\s.!]*$/osi and $event->{'userName'}) {
|
||||
$self->ListLists($event, $1);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub Baffled {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*(\S(?:.*\S)?)\s+list[\s!?.]*$/osi and $message !~ /\|/o and $event->{'userName'}) {
|
||||
$self->ListItems($event, $1);
|
||||
} else {
|
||||
return $self->SUPER::Baffled(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub Heard {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*add\s+(\S(?:.*\S)?)\s+to\s+(?:my\s+)?(\S(?:.*\S)?)\s+list[\s!.]*$/osi and $message !~ /\|/o and $event->{'userName'}) {
|
||||
$self->AddItem($event, $1, $2);
|
||||
} elsif ($message =~ /^\s*remove\s+(\S(?:.*\S)?)\s+from\s+(?:my\s+)?(\S(?:.*\S)?)\s+list[\s!.]*$/osi and $message !~ /\|/o and $event->{'userName'}) {
|
||||
$self->RemoveItem($event, $1, $2);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub AddItem {
|
||||
my $self = shift;
|
||||
my ($event, $what, $list) = @_;
|
||||
my @lists = split(/\|\|/o, $self->{'lists'}->{$event->{'userName'}});
|
||||
local $" = '\', \'';
|
||||
my %lists;
|
||||
foreach my $sublist (@lists) {
|
||||
my @items = split(/\|/o, $sublist);
|
||||
$lists{shift @items} = \@items;
|
||||
}
|
||||
push(@{$lists{lc $list}}, $what);
|
||||
local $" = '|';
|
||||
my $compoundLists = '';
|
||||
foreach my $list (keys(%lists)) {
|
||||
if ($compoundLists ne '') {
|
||||
$compoundLists .= '||';
|
||||
}
|
||||
$compoundLists .= "$list|@{$lists{$list}}";
|
||||
}
|
||||
$self->{'lists'}->{$event->{'userName'}} = $compoundLists;
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: stored '$what' in '$list' list");
|
||||
}
|
||||
|
||||
sub RemoveItem {
|
||||
my $self = shift;
|
||||
my ($event, $what, $list) = @_;
|
||||
my @lists = split(/\|\|/o, $self->{'lists'}->{$event->{'userName'}});
|
||||
local $" = '\', \'';
|
||||
my %lists;
|
||||
my $removed = 0;
|
||||
foreach my $sublist (@lists) {
|
||||
my @items = split(/\|/o, $sublist);
|
||||
if (lc $list eq $items[0]) {
|
||||
my $listName = shift @items;
|
||||
foreach my $item (@items) {
|
||||
if (lc $what ne lc $item) {
|
||||
push(@{$lists{$listName}}, $item);
|
||||
} else {
|
||||
$removed++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$lists{shift @items} = \@items;
|
||||
}
|
||||
}
|
||||
local $" = '|';
|
||||
my $compoundLists = '';
|
||||
foreach my $list (keys(%lists)) {
|
||||
if ($compoundLists ne '') {
|
||||
$compoundLists .= '||';
|
||||
}
|
||||
$compoundLists .= "$list|@{$lists{$list}}";
|
||||
}
|
||||
$self->{'lists'}->{$event->{'userName'}} = $compoundLists;
|
||||
$self->saveConfig();
|
||||
if ($removed) {
|
||||
$self->say($event, "$event->{'from'}: removed '$what' from '$list' list");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: could not find '$what' in '$list' list");
|
||||
}
|
||||
}
|
||||
|
||||
sub ListItems {
|
||||
my $self = shift;
|
||||
my ($event, $list) = @_;
|
||||
my @lists = split(/\|\|/o, $self->{'lists'}->{$event->{'userName'}});
|
||||
my %lists;
|
||||
foreach my $list (@lists) {
|
||||
my @items = split(/\|/o, $list);
|
||||
$lists{lc shift @items} = \@items;
|
||||
}
|
||||
if (defined(@{$lists{lc $list}})) {
|
||||
my $size = scalar(@{$lists{lc $list}});
|
||||
if ($size > $self->{'maxItemsInChannel'}) {
|
||||
$self->channelSay($event, "$event->{'from'}: Your $list list contains $size items, which I am /msg'ing you.");
|
||||
$self->directSay($event, $self->prettyPrint($self->{'preferredLineLength'}, "Your $list list contains: ", '', ', ', @{$lists{lc $list}}));
|
||||
} else {
|
||||
$self->say($event, $self->prettyPrint($self->{'preferredLineLength'}, "Your $list list contains: ", $event->{'channel'} eq '' ? '' : "$event->{'from'}: ", ', ', @{$lists{lc $list}}));
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "You don't have a $list list, sorry.");
|
||||
}
|
||||
}
|
||||
|
||||
sub ListLists {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my @lists = split(/\|\|/o, $self->{'lists'}->{$event->{'userName'}});
|
||||
my @listNames;
|
||||
foreach my $list (@lists) {
|
||||
my @items = split(/\|/o, $list);
|
||||
push(@listNames, $items[0]);
|
||||
}
|
||||
$self->say($event, $self->prettyPrint($self->{'preferredLineLength'}, "Your lists are: ", $event->{'channel'} eq '' ? '' : "$event->{'from'}: ", ', ', @listNames));
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
################################
|
||||
# MiniLogger Module #
|
||||
################################
|
||||
|
||||
package BotModules::MiniLogger;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my %help = (
|
||||
'' => 'This module keeps a log of the last few comments that match some patterns. For example, it can be used to remember URIs that have recently been mentioned.',
|
||||
);
|
||||
foreach (keys %{$self->{'patterns'}}) {
|
||||
$help{$_} = 'Returns any recent comment that matched the pattern /'.$self->sanitizeRegexp($self->{'patterns'}->{$_})."/. To narrow the search down even more, you can include a search string after the $_, as in '$_ goats'. To restrict the search to a particular channel, append \'in <channel>\' at the end.";
|
||||
}
|
||||
if ($self->isAdmin($event)) {
|
||||
$help{''} .= ' To add a new pattern, use the following syntax: vars MiniLogger patterns \'+|name|pattern\'';
|
||||
$help{'flush'} = 'Deletes any logs for patterns or channels that are no longer relevant, makes sure all the logs are no longer than the \'bufferSize\' length. Syntax: \'flush minilogs\'.';
|
||||
}
|
||||
return \%help;
|
||||
}
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['log', 0, 0, {}], # log -> channel -> patternName -> [<who> text]
|
||||
['bufferSize', 1, 1, 20], # number of comments to remember, per channel/pattern combination
|
||||
['patterns', 1, 1, {'links'=>'<?(:?[Uu][Rr][LlIi]:)?\s*(?:https?|ftp)://[^\s>"]+>?'}], # list of patternNames and patterns (regexp)
|
||||
['blockedPatterns', 1, 1, []], # list of patterns (regexp) to ignore
|
||||
);
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if (($message =~ /^\s*([a-zA-Z0-9]+)(?:\s+(.+?))?(?:\s+in\s+(.+?))?\s*$/osi) and ($self->{'patterns'}->{$1})) {
|
||||
$self->Report($event, $3, $1, $2); # event, channel, log, pattern
|
||||
} elsif ($self->isAdmin($event)) {
|
||||
if ($message =~ /^\s*flush\s+minilogs\s*$/osi) {
|
||||
$self->FlushMinilogs($event);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Log {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
if (($event->{'firsttype'} eq 'Told') or ($event->{'firsttype'} eq 'Heard')) {
|
||||
$self->DoLog($event, "<$event->{'from'}> $event->{'data'}");
|
||||
} elsif (($event->{'firsttype'} eq 'Felt') or ($event->{'firsttype'} eq 'Saw')) {
|
||||
$self->DoLog($event, "* $event->{'from'} $event->{'data'}");
|
||||
}
|
||||
}
|
||||
|
||||
sub DoLog {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($event->{'channel'} ne '') {
|
||||
# don't log private messages
|
||||
foreach my $pattern (keys %{$self->{'patterns'}}) {
|
||||
my $regexp = $self->sanitizeRegexp($self->{'patterns'}->{$pattern});
|
||||
if ($message =~ /$regexp/s) {
|
||||
# wohay, we have a candidate!
|
||||
# now check for possible blockers...
|
||||
unless ($self->isBlocked($message)) {
|
||||
$self->debug("LOGGING: $message");
|
||||
push(@{$self->{'log'}->{$event->{'channel'}}->{$pattern}}, $message);
|
||||
if (@{$self->{'log'}->{$event->{'channel'}}->{$pattern}} > $self->{'bufferSize'}) {
|
||||
shift(@{$self->{'log'}->{$event->{'channel'}}->{$pattern}});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub isBlocked {
|
||||
my $self = shift;
|
||||
my ($message) = @_;
|
||||
foreach my $blockedPattern (@{$self->{'blockedPatterns'}}) {
|
||||
my $regexp = $self->sanitizeRegexp($blockedPattern);
|
||||
if ($message =~ /$regexp/s) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub Report {
|
||||
my $self = shift;
|
||||
my ($event, $channel, $log, $pattern) = @_;
|
||||
my @channels = $channel ? lc($channel) : @{$self->{'channels'}};
|
||||
my $count;
|
||||
$pattern = $self->sanitizeRegexp($pattern);
|
||||
foreach $channel (@channels) {
|
||||
foreach my $match (@{$self->{'log'}->{$channel}->{$log}}) {
|
||||
if ((!$pattern) or ($match =~ /$pattern/s)) {
|
||||
$self->directSay($event, $match);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
unless ($count) {
|
||||
$self->directSay($event, 'No matches, sorry.');
|
||||
}
|
||||
$self->channelSay($event, "$event->{'from'}: minilog matches /msg'ed");
|
||||
}
|
||||
|
||||
sub FlushMinilogs {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
# remove dead channels
|
||||
my %channels = map { lc($_) => 1 } @{$self->{'channels'}};
|
||||
foreach my $channel (keys %{$self->{'log'}}) {
|
||||
if ($channels{$channel}) {
|
||||
# remove dead logs
|
||||
foreach my $pattern (keys %{$self->{'log'}->{$channel}}) {
|
||||
if ($self->{'patterns'}) {
|
||||
# remove any newly blocked patterns
|
||||
my @newpatterns;
|
||||
foreach my $match (@{$self->{'log'}->{$channel}->{$pattern}}) {
|
||||
unless ($self->isBlocked($match)) {
|
||||
push (@newpatterns, $match);
|
||||
}
|
||||
}
|
||||
# remove excess logs
|
||||
if (@newpatterns) {
|
||||
@{$self->{'log'}->{$channel}->{$pattern}} = (@newpatterns[
|
||||
@newpatterns - $self->{'bufferSize'} < 0 ? 0 : @newpatterns - $self->{'bufferSize'},
|
||||
$#newpatterns]
|
||||
);
|
||||
} else {
|
||||
@{$self->{'log'}->{$channel}->{$pattern}} = ();
|
||||
}
|
||||
} else {
|
||||
delete($self->{'log'}->{$channel}->{$pattern});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
delete($self->{'log'}->{$channel});
|
||||
}
|
||||
}
|
||||
$self->say($event, 'Minilogs flushed.');
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
################################
|
||||
# Parrot Module #
|
||||
################################
|
||||
|
||||
package BotModules::Parrot;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
if ($self->isAdmin($event)) {
|
||||
return {
|
||||
'' => 'This module allows you to make the bot do stuff.',
|
||||
'say' => 'Makes the bot say something. The <target> can be a person or channel. Syntax: say <target> <text>',
|
||||
'do' => 'Makes the bot do (/me) something. The <target> can be a person or channel. Syntax: do <target> <text>',
|
||||
'invite' => 'Makes the bot invite (/invite) somebody to a channel. Syntax: invite <who> <channel>',
|
||||
'announce' => 'Makes the bot announce something to every channel in which this module is enabled. Syntax: announce <text>',
|
||||
};
|
||||
} else {
|
||||
return $self->SUPER::Help($event);
|
||||
}
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ((($event->{'level'} == 1) and ($self->isAdmin($event))) or
|
||||
(($event->{'level'} == 3) and ($event->{'God_channel_rights'}) and ($event->{'Parrot_channel'} eq $event->{'God_channel'}))) {
|
||||
if ($message =~ /^\s*say\s+(\S+)\s+(.*)$/osi) {
|
||||
local $event->{'target'} = $1;
|
||||
$self->say($event, $2);
|
||||
} elsif ($message =~ /^\s*do\s+(\S+)\s+(.*)$/osi) {
|
||||
local $event->{'target'} = $1;
|
||||
$self->emote($event, $2);
|
||||
} elsif ($message =~ /^\s*announce\s+(.*)$/osi) {
|
||||
$self->announce($event, $1);
|
||||
} elsif ($message =~ /^\s* invite \s+
|
||||
(\S+) \s+
|
||||
(?: (?:in|to|into) \s+
|
||||
(?:channel \s+)? )?
|
||||
(\S+) \s*$/osix) {
|
||||
$self->invite($event, $1, $2);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
} else {
|
||||
if (($event->{'level'} == 1) and (($message =~ /^\s*say\s+(\S+)\s+(.*)$/osi) or ($message =~ /^\s*do\s+(\S+)\s+(.*)$/osi))) {
|
||||
$event->{'God_channel'} = lc($1);
|
||||
$event->{'Parrot_channel'} = lc($1);
|
||||
}
|
||||
my $result = $self->SUPER::Told(@_);
|
||||
return $result < (3 * defined($event->{'Parrot_channel'})) ? 3 : $result;
|
||||
|
||||
# Note: We go through some contortions here because if the parent
|
||||
# returns 3 or more, some other module sets God_channel, and
|
||||
# the command is either not 'say' or 'do' (or the God_channel happens
|
||||
# to be different to the channel we are looking at) then it is theoretically
|
||||
# possible that God_channel_rights could be set, but not for the channel
|
||||
# we care about. Or something..... ;-)
|
||||
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
@@ -1,572 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# Quiz Module #
|
||||
################################
|
||||
# some of these ideas are stolen from moxquizz (an eggdrop module)
|
||||
# see http://www.meta-x.de/moxquizz/
|
||||
|
||||
package BotModules::Quiz;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# XXX high score table
|
||||
# XXX do something with level
|
||||
# XXX make bot able to self-abort if no-one is taking part
|
||||
# XXX implement feature so that users that can be quiz admins in certain channels
|
||||
# XXX accept user submission
|
||||
# XXX README for database format (for now see http://www.meta-x.de/moxquizz/README.database)
|
||||
# XXX pause doesn't stop count of how long answer takes to answer
|
||||
# XXX different quiz formats, e.g. university challenge, weakest link (maybe implement by inheritance?)
|
||||
# XXX stats, e.g. number of questions skipped
|
||||
# XXX category filtering
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
my $help = {
|
||||
'' => "Runs quizzes. Start a quiz with the $self->{'prefix'}ask command.",
|
||||
$self->{'prefix'}.'ask' => 'Starts a quiz.',
|
||||
$self->{'prefix'}.'pause' => "Pauses the current quiz. Resume with $self->{'prefix'}resume.",
|
||||
$self->{'prefix'}.'resume' => 'Resumes the current quiz.',
|
||||
$self->{'prefix'}.'repeat' => 'Repeats the current question.',
|
||||
$self->{'prefix'}.'endquiz' => 'Ends the current quiz.',
|
||||
$self->{'prefix'}.'next' => 'Jump to the next question (at least half of the active participants have to say this for the question to be skipped).',
|
||||
$self->{'prefix'}.'score' => 'Show the current scores for the round.',
|
||||
};
|
||||
if ($self->isAdmin($event)) {
|
||||
$help->{'reload'} = 'To just reload the quiz data files instead of the whole module, use: reload Quiz Data';
|
||||
}
|
||||
return $help;
|
||||
}
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['questionSets', 1, 1, ['trivia.en']], # the list of files to read (from the Quiz/ directory)
|
||||
['questions', 0, 0, []], # the list of questions (hashes)
|
||||
['categories', 0, 0, {}], # hash of arrays whose values are indexes into questions
|
||||
['questionsPerRound', 1, 1, -1], # how many questions per round (-1 = infinite)
|
||||
['currentQuestion', 1, 0, {}], # the active question (per-channel hash)
|
||||
['questionIndex', 1, 0, 0], # where to start when picking the next question
|
||||
['skipMargin', 1, 1, 10], # maximum number of questions to skip at a time
|
||||
['remainingQuestions', 1, 0, {}], # how many more questions this round (per-channel hash)
|
||||
['questionsTime', 1, 0, {}], # when the question was asked
|
||||
['quizTime', 1, 0, {}], # when the quiz was started
|
||||
['paused', 1, 0, {}], # if the game is paused
|
||||
['totalScores', 1, 1, {}], # user => score
|
||||
['quizScores', 1, 0, {}], # channel => "user score"
|
||||
['skip', 1, 0, {}], # channel => "user 1"
|
||||
['players', 1, 0, {}], # channel => "user last time"
|
||||
['tip', 1, 0, {}], # which tip should next be given on this channel
|
||||
['tipDelay', 1, 1, 10], # seconds to wait before giving a tip
|
||||
['timeout', 1, 1, 120], # seconds to wait before giving up
|
||||
['skipFractionRequired', 1, 1, 0.5], # fraction of players that must say !skip to skip
|
||||
['askDelay', 1, 1, 2], # how long to wait between answer and question
|
||||
['prefix', 1, 1, '!'], # the prefix to have at the start of commands
|
||||
);
|
||||
}
|
||||
|
||||
sub Schedule {
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
$self->reloadData($event);
|
||||
my $fakeEvent = {%$event};
|
||||
foreach my $channel (keys %{$self->{'currentQuestion'}}) {
|
||||
$fakeEvent->{'channel'} = $channel;
|
||||
$fakeEvent->{'target'} = $channel;
|
||||
$self->debug("Restarting quiz in $channel... (qid $self->{'questionsTime'}->{$channel})");
|
||||
$self->schedule($fakeEvent, \$self->{'tipDelay'}, 1, 'tip', $self->{'questionsTime'}->{$channel});
|
||||
$self->schedule($fakeEvent, \$self->{'timeout'}, 1, 'timeout', $self->{'questionsTime'}->{$channel});
|
||||
if ($self->{'questionsTime'}->{$event->{'channel'}} == 0) {
|
||||
$self->schedule($event, \$self->{'askDelay'}, 1, 'ask');
|
||||
}
|
||||
}
|
||||
$self->SUPER::Schedule($event);
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my($event, $message) = @_;
|
||||
if ($message =~ /^\s*reload\s+quiz\s+data\s*$/osi and $self->isAdmin($event)) {
|
||||
my $count = $self->reloadData($event);
|
||||
$self->say($event, "$count questions loaded");
|
||||
} elsif ($message =~ /^\s*status[?\s]*$/osi) {
|
||||
my $questions = @{$self->{'questions'}};
|
||||
my $quizzes = keys %{$self->{'currentQuestion'}};
|
||||
my $prefix = '';
|
||||
if ($event->{'channel'} ne '') {
|
||||
$prefix = "$event->{'from'}: ";
|
||||
}
|
||||
$self->say($event, $prefix."I have $questions questions and am running $quizzes quizzes.", 1); # XXX 1 quizzes
|
||||
} elsif (not $self->DoQuizCheck($event, $message, 1)) {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Baffled {
|
||||
my $self = shift;
|
||||
my($event, $message) = @_;
|
||||
if (not $self->quizAnswer($event, $message)) {
|
||||
return $self->SUPER::Baffled(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub Heard {
|
||||
my $self = shift;
|
||||
my($event, $message) = @_;
|
||||
if (not $self->DoQuizCheck($event, $message, 0) and
|
||||
not $self->quizAnswer($event, $message)) {
|
||||
return $self->SUPER::Heard(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub DoQuizCheck {
|
||||
my $self = shift;
|
||||
my($event, $message, $direct) = @_;
|
||||
if ($message =~ /^\s*\Q$self->{'prefix'}\Eask\s*$/si) {
|
||||
$self->quizStart($event);
|
||||
} elsif ($message =~ /^\s*\Q$self->{'prefix'}\Epause\s*$/si) {
|
||||
$self->quizPause($event);
|
||||
} elsif ($message =~ /^\s*\Q$self->{'prefix'}\E(?:resume|unpause)\s*$/si) {
|
||||
$self->quizResume($event);
|
||||
} elsif ($message =~ /^\s*\Q$self->{'prefix'}\Erepeat\s*$/si) {
|
||||
$self->quizRepeat($event);
|
||||
} elsif ($message =~ /^\s*\Q$self->{'prefix'}\E(?:end|stop|strivia|exit)(?:quiz)?\s*$/si) {
|
||||
$self->quizEnd($event);
|
||||
} elsif ($message =~ /^\s*\Q$self->{'prefix'}\E(?:dunno|skip|next)\s*$/si) {
|
||||
$self->quizSkip($event);
|
||||
} elsif ($message =~ /^\s*\Q$self->{'prefix'}\E(?:scores)\s*$/si) {
|
||||
$self->quizScores($event);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub reloadData {
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
$self->{'questions'} = [];
|
||||
$self->{'categories'} = {};
|
||||
$self->debug('Loading quiz data...');
|
||||
foreach my $set (@{$self->{'questionSets'}}) {
|
||||
if ($set =~ m/^[a-zA-Z0-9-][a-zA-Z0-9.-]*$/os) {
|
||||
local *FILE;
|
||||
if (not open(FILE, "<BotModules/Quiz/$set")) { # XXX what if the directory has changed?
|
||||
$self->debug(" * $set (Not loaded; $!)");
|
||||
next;
|
||||
}
|
||||
$self->debug(" * $set");
|
||||
my $category;
|
||||
my $question = {'tip' => []};
|
||||
while (defined($_ = <FILE>)) {
|
||||
chomp;
|
||||
next if m/^\#/os; # skip comment lines
|
||||
next if m/^\s*$/os; # skip blank lines
|
||||
if (m/^Category:\s*(.*?)\s*$/os) {
|
||||
# Category? (should always be on top!)
|
||||
$category = $1;
|
||||
if (not defined($self->{'categories'}->{$category})) {
|
||||
$self->{'categories'}->{$category} = [];
|
||||
}
|
||||
} elsif (m/^Question:\s*(.*?)\s*$/os) {
|
||||
# Question (should always stand after Category)
|
||||
$question = {'question' => $1, 'tip' => []};
|
||||
if (defined($category)) {
|
||||
$question->{'category'} = $category;
|
||||
undef($category);
|
||||
}
|
||||
push(@{$self->{'questions'}}, $question);
|
||||
push(@{$self->{'categories'}->{$category}}, $#{$self->{'questions'}});
|
||||
} elsif (m/^Answer:\s*(?:(.*?)\#(.*?)\#(.*?)|(.*?))\s*$/os) {
|
||||
# Answer (will be matched if no regexp is provided)
|
||||
if (defined($1)) {
|
||||
$question->{'answer-long'} = "$1$2$3";
|
||||
$question->{'answer-short'} = $2;
|
||||
} else {
|
||||
$question->{'answer-long'} = $4;
|
||||
$question->{'answer-short'} = $4;
|
||||
}
|
||||
} elsif (m/^Regexp:\s*(.*?)\s*$/os) {
|
||||
# Regexp? (use UNIX-style expressions)
|
||||
$question->{'answer-regexp'} = $1;
|
||||
} elsif (m/^Author:\s*(.*?)\s*$/os) {
|
||||
# Author? (the brain behind this question)
|
||||
$question->{'author'} = $1;
|
||||
} elsif (m/^Level:\s*(.*?)\s*$/os) {
|
||||
# Level? [baby|easy|normal|hard|extreme] (difficulty)
|
||||
$question->{'level'} = $1;
|
||||
} elsif (m/^Comment:\s*(.*?)\s*$/os) {
|
||||
# Comment? (comment line)
|
||||
$question->{'comment'} = $1;
|
||||
} elsif (m/^Score:\s*(.*?)\s*$/os) {
|
||||
# Score? [#] (credits for answering this question)
|
||||
$question->{'score'} = $1;
|
||||
} elsif (m/^Tip:\s*(.*?)\s*$/os) {
|
||||
# Tip* (provide one or more hints)
|
||||
push(@{$question->{'tip'}}, $1);
|
||||
} elsif (m/^TipCycle:\s*(.*?)\s*$/os) {
|
||||
# TipCycle? [#] (Specify number of generated tips)
|
||||
$question->{'tip-cycle'} = $1;
|
||||
} else {
|
||||
# XXX error handling
|
||||
}
|
||||
}
|
||||
close(FILE);
|
||||
} # else XXX invalid filename, ignore it
|
||||
}
|
||||
# if no more questions, abort running quizes.
|
||||
if (not @{$self->{'questions'}}) {
|
||||
foreach my $channel (keys %{$self->{'currentQuestion'}}) {
|
||||
local $event->{'channel'} = $channel;
|
||||
$self->say($event, 'There are no more questions.');
|
||||
$self->quizEnd($event);
|
||||
}
|
||||
}
|
||||
return scalar(@{$self->{'questions'}});
|
||||
}
|
||||
|
||||
|
||||
# game implementation
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my($event, @data) = @_;
|
||||
if ($data[0] eq 'tip') {
|
||||
if ($self->{'questionsTime'}->{$event->{'channel'}} == $data[1] and
|
||||
defined($self->{'currentQuestion'}->{$event->{'channel'}})) {
|
||||
# $self->debug('time for a tip');
|
||||
if ($self->{'paused'}->{$event->{'channel'}} or
|
||||
$self->quizTip($event)) {
|
||||
$self->schedule($event, \$self->{'tipDelay'}, 1, @data);
|
||||
}
|
||||
}
|
||||
} elsif ($data[0] eq 'timeout') {
|
||||
if ($self->{'questionsTime'}->{$event->{'channel'}} == $data[1] and
|
||||
defined($self->{'currentQuestion'}->{$event->{'channel'}})) {
|
||||
if ($self->{'paused'}->{$event->{'channel'}}) {
|
||||
$self->schedule($event, \$self->{'timeout'}, 1, @data);
|
||||
} else {
|
||||
my $answer = $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-long'};
|
||||
$self->say($event, "Too late! The answer was: $answer");
|
||||
$self->quizQuestion($event);
|
||||
}
|
||||
}
|
||||
} elsif ($data[0] eq 'ask') {
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) {
|
||||
$self->quizQuestion($event);
|
||||
}
|
||||
} else {
|
||||
$self->SUPER::Scheduled($event, @data);
|
||||
}
|
||||
}
|
||||
|
||||
sub quizStart { # called by user
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
if ($event->{'channel'} ne '' and
|
||||
not defined($self->{'currentQuestion'}->{$event->{'channel'}})) {
|
||||
if (@{$self->{'questions'}} == 0) {
|
||||
# if no questions, complain.
|
||||
$self->say($event, 'I cannot run a quiz with no questions!');
|
||||
} else {
|
||||
# no game in progress, start one
|
||||
$self->{'remainingQuestions'}->{$event->{'channel'}} = $self->{'questionsPerRound'};
|
||||
$self->{'paused'}->{$event->{'channel'}} = 0;
|
||||
$self->{'quizTime'}->{$event->{'channel'}} = time();
|
||||
$self->{'quizScores'}->{$event->{'channel'}} = '';
|
||||
$self->{'players'}->{$event->{'channel'}} = '';
|
||||
$self->quizQuestion($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub quizQuestion { # called from quizStart or delayed from quizAnswer
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
if ($event->{'channel'} ne '' and # in channel
|
||||
not $self->{'paused'}->{$event->{'channel'}}) { # quiz not paused
|
||||
if ($self->{'remainingQuestions'}->{$event->{'channel'}} != 0) {
|
||||
$self->{'remainingQuestions'}->{$event->{'channel'}}--;
|
||||
my $category = $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'category'};
|
||||
while ($category eq $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'category'}
|
||||
and my $try++ < keys %{$self->{'questions'}}) {
|
||||
$self->{'currentQuestion'}->{$event->{'channel'}} = $self->pickQuestion();
|
||||
}
|
||||
$self->{'questionsTime'}->{$event->{'channel'}} = time();
|
||||
$self->{'tip'}->{$event->{'channel'}} = 0;
|
||||
$self->{'skip'}->{$event->{'channel'}} = '';
|
||||
$self->schedule($event, \$self->{'tipDelay'}, 1, 'tip', $self->{'questionsTime'}->{$event->{'channel'}});
|
||||
$self->schedule($event, \$self->{'timeout'}, 1, 'timeout', $self->{'questionsTime'}->{$event->{'channel'}});
|
||||
$self->say($event, "Question: $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'question'}");
|
||||
$self->debug("Question: $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'question'}");
|
||||
$self->debug("Answer: $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-long'}");
|
||||
$self->saveConfig();
|
||||
} else {
|
||||
$self->quizEnd($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub quizAnswer { # called by user
|
||||
my $self = shift;
|
||||
my($event, $message) = @_;
|
||||
if ($event->{'channel'} ne '' and # in channel
|
||||
defined($self->{'currentQuestion'}->{$event->{'channel'}}) and # in quiz
|
||||
$self->{'questionsTime'}->{$event->{'channel'}} and # not answered
|
||||
not $self->{'paused'}->{$event->{'channel'}}) { # quiz not paused
|
||||
$self->stringHash(\$self->{'players'}->{$event->{'channel'}}, $event->{'from'}, time());
|
||||
if (lc($message) eq lc($self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-long'}) or
|
||||
(defined($self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-short'}) and
|
||||
lc($message) eq lc($self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-short'})) or
|
||||
(defined($self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-regexp'}) and
|
||||
$message =~ /$self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-regexp'}/si)) {
|
||||
# they got it right
|
||||
my $who = $event->{'from'};
|
||||
my $answer = $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-long'};
|
||||
my $score = $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'score'};
|
||||
if (not defined($score)) {
|
||||
$score = 1; # use difficulty XXX
|
||||
}
|
||||
my $time = time() - $self->{'questionsTime'}->{$event->{'channel'}};
|
||||
my $total = $self->score($event, $who, $score);
|
||||
$self->debug("Answered by: $who");
|
||||
$self->say($event, "$who got the right answer in $time seconds (+$score points giving $total). The answer was: $answer");
|
||||
$self->saveConfig();
|
||||
$self->{'questionsTime'}->{$event->{'channel'}} = 0;
|
||||
$self->schedule($event, \$self->{'askDelay'}, 1, 'ask');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub quizTip { # called by timer, only during game
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
my $tip;
|
||||
if (defined($self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'tips'}) and
|
||||
$self->{'tip'}->{$event->{'channel'}} < @{$self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'tips'}}) {
|
||||
$tip = $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'tips'}->[$self->{'tip'}->{$event->{'channel'}}];
|
||||
} else {
|
||||
if (not defined($self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'tips'}) and
|
||||
(not defined($self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'tipCycle'}) or
|
||||
$self->{'tip'}->{$event->{'channel'}} < $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'tipCycle'})) {
|
||||
$tip = $self->generateTip($self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-long'},
|
||||
$self->{'tip'}->{$event->{'channel'}},
|
||||
$self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'tipCycle'});
|
||||
}
|
||||
}
|
||||
if (defined($tip)) {
|
||||
$self->{'tip'}->{$event->{'channel'}} += 1;
|
||||
$self->say($event, "Hint: $tip...");
|
||||
$self->saveConfig();
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub quizPause { # called by user
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) { # game in progress
|
||||
if (not $self->{'paused'}->{$event->{'channel'}}) { # not paused
|
||||
# pause game
|
||||
$self->{'paused'}->{$event->{'channel'}} = 1;
|
||||
$self->saveConfig();
|
||||
$self->say($event, "Quiz paused. Use $self->{'prefix'}resume to continue.");
|
||||
} else {
|
||||
$self->say($event, "Quiz already paused. Use $self->{'prefix'}resume to continue.");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "No quiz in progress, use $self->{'prefix'}ask to start one.");
|
||||
}
|
||||
}
|
||||
|
||||
sub quizResume { # called by user
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) { # game in progress
|
||||
if ($self->{'paused'}->{$event->{'channel'}}) { # paused
|
||||
# unpause game
|
||||
$self->{'paused'}->{$event->{'channel'}} = 0;
|
||||
$self->saveConfig();
|
||||
$self->say($event, "Quiz resumed. Question: $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'question'}");
|
||||
} else {
|
||||
$self->say($event, "Quiz already in progress. Use $self->{'prefix'}repeat to be told the question again, and $self->{'prefix'}pause to pause the quiz.");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "No quiz in progress, use $self->{'prefix'}ask to start one.");
|
||||
}
|
||||
}
|
||||
|
||||
sub quizRepeat { # called by user
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) { # game in progress
|
||||
$self->say($event, "Question: $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'question'}");
|
||||
} else {
|
||||
$self->say($event, "No quiz in progress, use $self->{'prefix'}ask to start one.");
|
||||
}
|
||||
}
|
||||
|
||||
sub quizEnd { # called by question and user
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) {
|
||||
# get the scores for each player that player in the game
|
||||
my @scores = $self->getScores($event, sub {
|
||||
my($event, $score) = @_;
|
||||
# XXX this means that a user has to be there till the end
|
||||
# of the game to get points added to his high score table.
|
||||
# XXX it also means a user can get better simply by
|
||||
# playing more games.
|
||||
$self->{'totalScores'}->{$score->[1]} += $score->[2];
|
||||
});
|
||||
# print them
|
||||
if (@scores) {
|
||||
local $" = ', ';
|
||||
$self->say($event, "Quiz Ended. Scores: @scores");
|
||||
} else {
|
||||
$self->say($event, 'Quiz Ended. No questions were answered.');
|
||||
}
|
||||
delete($self->{'currentQuestion'}->{$event->{'channel'}});
|
||||
$self->saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
sub quizScores { # called by user
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) {
|
||||
# get the scores for each player that player in the game
|
||||
my @scores = $self->getScores($event, sub {});
|
||||
# get other stats
|
||||
my $remaining = '';
|
||||
if ($self->{'remainingQuestions'}->{$event->{'channel'}} > 0) {
|
||||
$remaining = " There are $self->{'remainingQuestions'}->{$event->{'channel'}} more questions to go.";
|
||||
}
|
||||
# print them
|
||||
if (@scores) {
|
||||
local $" = ', ';
|
||||
$self->say($event, "Current Scores: @scores$remaining");
|
||||
} else {
|
||||
$self->say($event, "No questions have been answered yet.$remaining");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "No quiz in progress, use $self->{'prefix'}ask to start one.");
|
||||
}
|
||||
}
|
||||
|
||||
sub quizSkip { # called by user
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) { # game in progress
|
||||
if (not $self->{'paused'}->{$event->{'channel'}}) { # not paused
|
||||
if ($self->{'questionsTime'}->{$event->{'channel'}}) { # question asked and not answered
|
||||
# XXX should only let players skip (at the moment even someone who has not tried to answer any question can skip)
|
||||
# Get number of users who have said !skip (and set current user)
|
||||
my(undef, $skipCount) = $self->stringHash(\$self->{'skip'}->{$event->{'channel'}}, $event->{'from'}, 1);
|
||||
# Get number of users who are playing
|
||||
my $playerCount = $self->getActivePlayers($event);
|
||||
if ($skipCount >= $playerCount * $self->{'skipFractionRequired'}) {
|
||||
my $answer = $self->{'questions'}->[$self->{'currentQuestion'}->{$event->{'channel'}}]->{'answer-long'};
|
||||
$self->say($event, "$skipCount players wanted to skip. Moving to next question. The answer was: $answer");
|
||||
$self->quizQuestion($event);
|
||||
}
|
||||
} # else drop it
|
||||
} else {
|
||||
$self->say($event, "Quiz paused. Use $self->{'prefix'}resume to continue the quiz.");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "No quiz in progress, use $self->{'prefix'}ask to start one.");
|
||||
}
|
||||
}
|
||||
|
||||
sub pickQuestion {
|
||||
my $self = shift;
|
||||
$self->{'questionIndex'} += 1 + time() % $self->{'skipMargin'};
|
||||
$self->{'questionIndex'} %= @{$self->{'questions'}};
|
||||
return $self->{'questionIndex'};
|
||||
}
|
||||
|
||||
sub score {
|
||||
my $self = shift;
|
||||
my($event, $who, $score) = @_;
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) {
|
||||
my($score, undef) = $self->stringHash(\$self->{'quizScores'}->{$event->{'channel'}}, $who, $score, 1);
|
||||
$self->saveConfig();
|
||||
return $score;
|
||||
}
|
||||
}
|
||||
|
||||
sub getScores {
|
||||
my $self = shift;
|
||||
my($event, $perUser) = @_;
|
||||
my @scores;
|
||||
foreach my $player ($self->getActivePlayers($event)) {
|
||||
my($score, undef) = $self->stringHash(\$self->{'quizScores'}->{$event->{'channel'}}, $player);
|
||||
if (defined($score)) {
|
||||
push(@scores, ["$player: $score", $player, $score]);
|
||||
}
|
||||
}
|
||||
# sort the scores by number
|
||||
@scores = sort {$a->[2] <=> $b->[2]} @scores;
|
||||
foreach my $score (@scores) {
|
||||
&$perUser($event, $score);
|
||||
$score = $score->[0];
|
||||
}
|
||||
return @scores;
|
||||
}
|
||||
|
||||
sub generateTip {
|
||||
my $self = shift;
|
||||
my($answer, $tipID, $maxTips) = @_;
|
||||
if (length($answer) > $tipID+1) {
|
||||
return substr($answer, 0, $tipID+1);
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub getActivePlayers {
|
||||
my $self = shift;
|
||||
my($event) = @_;
|
||||
my @players;
|
||||
if (defined($self->{'currentQuestion'}->{$event->{'channel'}})) { # game in progress
|
||||
my $start = $self->{'quizTime'}->{$event->{'channel'}};
|
||||
my %players = split(' ', $self->{'players'}->{$event->{'channel'}});
|
||||
foreach my $player (keys %players) {
|
||||
if ($players{$player} > $start) {
|
||||
push(@players, $player);
|
||||
}
|
||||
}
|
||||
}
|
||||
return @players;
|
||||
}
|
||||
|
||||
sub stringHash {
|
||||
my $self = shift;
|
||||
my($string, $key, $value, $multiple) = @_;
|
||||
my %hash = split(' ', $$string);
|
||||
my @hash;
|
||||
if (defined($value)) {
|
||||
if (defined($multiple)) {
|
||||
$hash{$key} = $hash{$key} * $multiple + $value;
|
||||
} else {
|
||||
$hash{$key} = $value;
|
||||
}
|
||||
local $" = ' ';
|
||||
@hash = %hash;
|
||||
$$string = "@hash";
|
||||
} else {
|
||||
@hash = %hash;
|
||||
}
|
||||
return ($hash{$key}, scalar(@hash) / 2);
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
################################
|
||||
# RDF Module #
|
||||
################################
|
||||
|
||||
package BotModules::RDF;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['sites', 1, 1, {}],
|
||||
['updateDelay', 1, 1, 600],
|
||||
['preferredLineLength', 1, 1, 80],
|
||||
['maxInChannel', 1, 1, 5],
|
||||
['trimTitles', 1, 1, '0'],
|
||||
['data', 0, 0, {}], # data -> uri -> (title, link, last, items -> uri)
|
||||
['mutes', 1, 1, {}], # uri -> "channel channel channel"
|
||||
);
|
||||
}
|
||||
|
||||
# Schedule - called when bot connects to a server, to install any schedulers
|
||||
# use $self->schedule($event, $delay, $times, $data)
|
||||
# where $times is 1 for a single event, -1 for recurring events,
|
||||
# and a +ve number for an event that occurs that many times.
|
||||
sub Schedule {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
$self->schedule($event, \$self->{'updateDelay'}, -1, 'rdf');
|
||||
$self->SUPER::Schedule($event);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my %commands;
|
||||
if ($self->isAdmin($event)) {
|
||||
$commands{''} = "The RDF module monitors various websites. Add new RDF channels to the 'sites' hash. Duplicates with different nicknames are fine. For example, \"vars $self->{'_name'} sites '+|slashdot|http://...'\" and \"vars $self->{'_name'} sites '+|/.|http://...'\" is fine.";
|
||||
$commands{'mute'} = 'Disable reporting of a site in a channel. (Only does something if the given site exists.) Syntax: mute <site> in <channel>';
|
||||
$commands{'unmute'} = 'Enable reporting of a site in a channel. By default, sites are reported in all channels that the module is active in. Syntax: unmute <site> in <channel>';
|
||||
} else {
|
||||
$commands{''} = 'The RDF module monitors various websites.';
|
||||
}
|
||||
foreach my $site (keys(%{$self->{'sites'}})) {
|
||||
if ($self->{'data'}->{$self->{'sites'}->{$site}}) {
|
||||
$commands{$site} = "Reports the headlines listed in $self->{'data'}->{$self->{'sites'}->{$site}}->{'title'}";
|
||||
|
||||
# -- #mozilla was here --
|
||||
# <Hixie> anyway, $self->{'data'}->{$self->{'sites'}->{$site}}->{'title'} is
|
||||
# another nice piece of perl (embedded in a quoted string in this case)
|
||||
# <moogle> yeah, that's a bit more familiar
|
||||
# <jag> Oooh, nice one
|
||||
# <jag> Reminds me of Java, a bit :-)
|
||||
# <jag> Without all the casting about from Object to Hashtable
|
||||
# <Hixie> all this, BTW, is from the RDF module (the one that mozbot uses to
|
||||
# report changes in mozillazine and so on)
|
||||
# <moogle> I still tend to comment these things a bit just for maintainability
|
||||
# by others who might not wish to do mental gymnastics :)
|
||||
# <Hixie> :-)
|
||||
|
||||
} else {
|
||||
$commands{$site} = "Reports the headlines listed in $self->{'sites'}->{$site}";
|
||||
}
|
||||
|
||||
}
|
||||
return \%commands;
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
foreach my $site (keys(%{$self->{'sites'}})) {
|
||||
if ($message =~ /^\s*(\Q$site\E)\s*$/si) {
|
||||
$self->GetSite($event, $1, 'request');
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
}
|
||||
if ($self->isAdmin($event)) {
|
||||
if ($message =~ /^\s*mute\s+(\S+?)\s+in\s+(\S+?)\s*$/osi) {
|
||||
my $site = $1 eq 'RDF' ? '' : $self->{'sites'}->{$1};
|
||||
my $siteName = $site eq '' ? 'all sites' : $site;
|
||||
if (defined($site)) {
|
||||
$self->{'mutes'}->{$site} .= " $2";
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: RDF notifications for $siteName muted in channel $2.");
|
||||
} else {
|
||||
# can't say this, other modules might recognise it: $self->say($event, "$event->{'from'}: I don't know about any '$1' site...");
|
||||
}
|
||||
} elsif ($message =~ /^\s*unmute\s+(\S+?)\s+in\s+(\S+?)\s*$/osi) {
|
||||
my $site = $1 eq 'RDF' ? '' : $self->{'sites'}->{$1};
|
||||
my $siteName = $site eq '' ? 'all sites' : $site;
|
||||
if (defined($site)) {
|
||||
my %mutedChannels = map { lc($_) => 1 } split(/ /o, $self->{'mutes'}->{$site});
|
||||
delete($mutedChannels{lc($2)}); # get rid of any mentions of that channel
|
||||
$self->{'mutes'}->{$site} = join(' ', keys(%mutedChannels));
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: RDF notifications for $siteName resumed in channel $2.");
|
||||
} else {
|
||||
# can't say this, other modules might recognise it: $self->say($event, "$event->{'from'}: I don't know about any '$1' site...");
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub GetSite {
|
||||
my $self = shift;
|
||||
my ($event, $site, $intent) = @_;
|
||||
if (defined($self->{'sites'}->{$site})) {
|
||||
my $uri = $self->{'sites'}->{$site};
|
||||
$self->getURI($event, $uri, $intent);
|
||||
} else {
|
||||
# XXX
|
||||
}
|
||||
}
|
||||
|
||||
sub GotURI {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $output, $intent) = @_;
|
||||
|
||||
$self->{'data'}->{$uri}->{'ready'} = defined($self->{'data'}->{$uri});
|
||||
|
||||
if ($output) {
|
||||
|
||||
# last update stamp
|
||||
$self->{'data'}->{$uri}->{'last'} = time();
|
||||
|
||||
# this, of course, is a disaster waiting to happen.
|
||||
# for example, we don't cope with comments.
|
||||
# someone write a real XML version of this pleeeeease... XXX
|
||||
|
||||
# get the juicy stuff out
|
||||
my $channelpart = "";
|
||||
if ($output =~ /<channel>(.*)<\/channel>/osi) {
|
||||
$channelpart = $1;
|
||||
}
|
||||
|
||||
# remove any image related stuff
|
||||
$output =~ s/<image>.*<\/image>//gosi;
|
||||
|
||||
# get the channel title
|
||||
$self->{'data'}->{$uri}->{'title'} = $uri;
|
||||
if ($channelpart =~ /<title>\s*(.+?)\s*<\/title>/osi) {
|
||||
$self->{'data'}->{$uri}->{'title'} = $self->unescapeXML($1);
|
||||
$self->{'data'}->{$uri}->{'title'} =~ s/: News for nerds, stuff that matters//gosi if $self->{'trimTitles'};
|
||||
}
|
||||
|
||||
# get the channel website
|
||||
$self->{'data'}->{$uri}->{'link'} = $uri;
|
||||
if ($channelpart =~ /<link>\s*(.+?)\s*<\/link>/osi) {
|
||||
$self->{'data'}->{$uri}->{'link'} = $self->unescapeXML($1);
|
||||
}
|
||||
|
||||
# get all the items
|
||||
while ($output =~ /<item>.*?<title>\s*(.+?)\s*<\/title>.*?<\/item>/osig) {
|
||||
unless (($1 =~ /^last update/osi) or (defined($self->{'data'}->{$uri}->{'items'}->{$self->unescapeXML($1)}))) {
|
||||
$self->{'data'}->{$uri}->{'items'}->{$self->unescapeXML($1)} = $self->{'data'}->{$uri}->{'last'};
|
||||
}
|
||||
}
|
||||
|
||||
$self->ReportDiffs($event, $uri, $intent);
|
||||
if ($intent eq 'request') {
|
||||
$self->ReportAll($event, $uri);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ($intent eq 'request') {
|
||||
$self->say($event, "$event->{'from'}: Dude, the file was empty! ($uri)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my ($event, @data) = @_;
|
||||
if ($data[0] eq 'rdf') {
|
||||
my %sites = map { $_ => 1 } values(%{$self->{'sites'}});
|
||||
foreach (keys(%sites)) {
|
||||
$self->getURI($event, $_, 'update');
|
||||
}
|
||||
} else {
|
||||
$self->SUPER::Scheduled($event, @data);
|
||||
}
|
||||
}
|
||||
|
||||
sub ReportDiffs {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $request) = @_;
|
||||
return unless $self->{'data'}->{$uri}->{'ready'};
|
||||
my $last = $self->{'data'}->{$uri}->{'last'};
|
||||
my @output;
|
||||
foreach (keys(%{$self->{'data'}->{$uri}->{'items'}})) {
|
||||
push(@output, $_) if ($self->{'data'}->{$uri}->{'items'}->{$_} == $last);
|
||||
}
|
||||
if (@output) {
|
||||
|
||||
@output = $self->prettyPrint($self->{'preferredLineLength'},
|
||||
"Just appeared in $self->{'data'}->{$uri}->{'title'} - $self->{'data'}->{$uri}->{'link'} : ",
|
||||
'', ' -- ', @output);
|
||||
|
||||
my %mutedChannels = ();
|
||||
if (defined($self->{'mutes'}->{$uri})) {
|
||||
%mutedChannels = map { lc($_) => 1 } split(/\s+/os, $self->{'mutes'}->{$uri});
|
||||
}
|
||||
if (defined($self->{'mutes'}->{''})) {
|
||||
%mutedChannels = (%mutedChannels, map { lc($_) => 1 } split(/\s+/os, $self->{'mutes'}->{''}));
|
||||
}
|
||||
if ($request eq 'request') {
|
||||
$mutedChannels{$event->{'channel'}} = 1;
|
||||
}
|
||||
foreach (@{$self->{'channels'}}) {
|
||||
unless ($mutedChannels{$_}) {
|
||||
local $event->{'target'} = $_;
|
||||
foreach (@output) {
|
||||
$self->say($event, $_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub ReportAll {
|
||||
my $self = shift;
|
||||
my ($event, $uri) = @_;
|
||||
my @output;
|
||||
foreach (keys(%{$self->{'data'}->{$uri}->{'items'}})) {
|
||||
push(@output, $_);
|
||||
}
|
||||
|
||||
@output = $self->prettyPrint($self->{'preferredLineLength'},
|
||||
"Items in $self->{'data'}->{$uri}->{'title'} - $self->{'data'}->{$uri}->{'link'}: ",
|
||||
"$event->{'from'}: ", ' -- ', @output);
|
||||
|
||||
if (@output > $self->{'maxInChannel'}) {
|
||||
foreach (@output) {
|
||||
$self->directSay($event, $_);
|
||||
}
|
||||
$self->channelSay($event, "$event->{'from'}: /msg'ed");
|
||||
} else {
|
||||
foreach (@output) {
|
||||
$self->say($event, $_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
################################
|
||||
# Rude Module #
|
||||
################################
|
||||
|
||||
package BotModules::Rude;
|
||||
use vars qw(@ISA);
|
||||
use Net::Telnet;
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'The Rude Module is... rude. Very rude! So rude!!!',
|
||||
'insult' => 'Insults someone. Syntax: \'insult <who>\'',
|
||||
'excuse' => 'Gives you an excuse for the system being down. Syntax: \'excuse\'',
|
||||
};
|
||||
}
|
||||
|
||||
# -- timeless was here --
|
||||
# <timeless> Rude module is missing a jar jar quote ~how wude~
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['insultHost', 1, 1, 'insulthost.colorado.edu'],
|
||||
['insultPort', 1, 1, '1695'],
|
||||
['excuseHost', 1, 1, 'bofh.engr.wisc.edu'], # or bofh.jive.org
|
||||
['excusePort', 1, 1, '666'],
|
||||
['insultOverrides', 1, 1, { # overrides for the insults (keys must be lowercase)
|
||||
'mozilla' => 'You are nothing but the best browser on the planet.',
|
||||
'mozilla.org' => 'You are nothing but the best caretaker Mozilla ever had.',
|
||||
'c++' => 'you are evil',
|
||||
}],
|
||||
);
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*insult\s+(\S+?)\s*$/osi) {
|
||||
my $line;
|
||||
if (defined($self->{'insultOverrides'}->{lc $1})) {
|
||||
$line = "$1: ".$self->{'insultOverrides'}->{lc $1};
|
||||
} else {
|
||||
my $t = new Net::Telnet (Timeout => 3);
|
||||
$t->Net::Telnet::open(Host => $self->{'insultHost'}, Port => $self->{'insultPort'});
|
||||
$line = "$1: ".$t->Net::Telnet::getline(Timeout => 4);
|
||||
}
|
||||
if ($line) {
|
||||
$self->say($event, $line);
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: What have they ever done to you! Leave 'em alone!");
|
||||
$self->debug("yikes, $self->{'insultHost'}:$self->{'insultPort'} is down!");
|
||||
}
|
||||
} elsif ($message =~ /^\s*(?:please\s+)?(?:can\s+i\s+have\s+an\s+|(?:(?:can|could)\s+you\s+)?give\s+me\s+an\s+)?excuse(?:[?,.!1\s]+please)?\s*[!?,.1]*\s*$/osi) {
|
||||
my $t = new Net::Telnet (Timeout => 3);
|
||||
$t->Net::Telnet::open(Host => $self->{'excuseHost'}, Port => $self->{'excusePort'});
|
||||
# print "=== The BOFH-style Excuse Server --- Feel The Power!\n";
|
||||
$t->Net::Telnet::getline(Timeout => 4);
|
||||
# print "=== By Jeff Ballard <ballard\@cs.wisc.edu>\n";
|
||||
$t->Net::Telnet::getline(Timeout => 4);
|
||||
# print "=== See http://www.cs.wisc.edu/~ballard/bofh/ for more info.\n";
|
||||
$t->Net::Telnet::getline(Timeout => 4);
|
||||
# print "Your excuse is: $excuses[$j]";
|
||||
my $line = $t->Net::Telnet::getline(Timeout => 4);
|
||||
if ($line) {
|
||||
# $line =~ s/^.*?Your excuse is: //gosi;
|
||||
# $self->say($event, "$event->{'from'}: '$line'");
|
||||
$self->say($event, "$line");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Don't ask *me* for an excuse! Sheesh!");
|
||||
$self->debug("yikes, $self->{'insultHost'}:$self->{'insultPort'} is down!");
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
################################
|
||||
# Sheriff Module #
|
||||
################################
|
||||
|
||||
package BotModules::Sheriff;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['tree', 1, 1, 'SeaMonkey'],
|
||||
['baseURI', 1, 1, 'http://tinderbox.mozilla.org/'],
|
||||
['_sheriff', 1, 0, undef], # the undef actually means "don't touch", of course
|
||||
['updateDelay', 1, 1, 360],
|
||||
# XXX implement per-channel muting of the update notification
|
||||
);
|
||||
}
|
||||
|
||||
# Schedule - called when bot connects to a server, to install any schedulers
|
||||
# use $self->schedule($event, $delay, $times, $data)
|
||||
# where $times is 1 for a single event, -1 for recurring events,
|
||||
# and a +ve number for an event that occurs that many times.
|
||||
sub Schedule {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
$self->schedule($event, \$self->{'updateDelay'}, -1, 'sheriff');
|
||||
$self->SUPER::Schedule($event);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'The Sheriff module keeps track of the current sheriff.',
|
||||
'sheriff' => 'Display the current sheriff. Syntax: sheriff [tree]',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*(?:who's\s+|whose\s+|whos\s+|who\s+is\s+the\s+|who\s+is\s+|who\s+)?sheriff(?:\s+(?:of\s+)?(.*?))?(?:[\s,]+today)?[.?!1]*\s*$/osi) {
|
||||
$self->GetSheriff($event, $1 || $self->{'tree'}, 'requested');
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub GetSheriff {
|
||||
my $self = shift;
|
||||
my ($event, $tree, $requested) = @_;
|
||||
my $url = "$self->{'baseURI'}$tree/sheriff.pl";
|
||||
$self->getURI($event, $url, $tree, $requested);
|
||||
}
|
||||
|
||||
sub GotURI {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $output, $tree, $requested) = @_;
|
||||
# someone please pretty up the logic here... XXX
|
||||
if ($output) {
|
||||
# magicness
|
||||
{ no warnings; # this can go _very_ wrong easily
|
||||
# sheriff.pl is created using the following lines:
|
||||
# $m =~ s/\'/\\\'/g;
|
||||
# print SHERIFF "\$current_sheriff = '$m';\n1;";
|
||||
$output =~ s/^\$current_sheriff = '//gosi; # strip front
|
||||
$output =~ s/';\n1;$//gosi; # strip back
|
||||
$output =~ s/\\\'/\'/gosi; # dequote quotes
|
||||
# heuristics
|
||||
$output =~ s/\n|\r|<a\s+href="|<\/a>//gosi;
|
||||
$output =~ s/">/, /gosi;
|
||||
$output =~ s/<br>/ /gosi;
|
||||
$output =~ s/<\/?(?:b|strong)>/*/gosi;
|
||||
$output =~ s/<\/?(?:u|em)>/_/gosi;
|
||||
$output =~ s/<\/?(?:q)>/"/gosi;
|
||||
$output =~ s/<\/?(?:i|dfn|cite)>/\//gosi;
|
||||
}
|
||||
if (defined($output)) {
|
||||
if ($tree eq $self->{'tree'}) {
|
||||
if ((defined($self->{'_sheriff'})) and ($self->{'_sheriff'} ne '')) { # not first time
|
||||
if ($output ne $self->{'_sheriff'}) { # changed.
|
||||
$self->announce($event, "Sheriff change: $output");
|
||||
if (($requested) and (not ($event->{'channel'}))) {
|
||||
$self->directSay($event, "$output");
|
||||
}
|
||||
} elsif ($requested) {
|
||||
$self->say($event, "$event->{'from'}: $output");
|
||||
}
|
||||
} else { # first time
|
||||
$self->say($event, "$event->{'from'}: $output") if ($requested);
|
||||
}
|
||||
$self->{'_sheriff'} = $output; # update internal cache
|
||||
} else { # not default tree
|
||||
if ($requested) {
|
||||
$self->say($event, "$event->{'from'}: $output");
|
||||
} # else EH!?
|
||||
}
|
||||
} else {
|
||||
# something went very wrong
|
||||
$self->say($event, "$event->{'from'}: I have no idea -- the '$tree' tree probably doesn't have a sheriff.") if ($requested);
|
||||
if ($tree eq $self->{'tree'}) {
|
||||
if (defined($self->{'_sheriff'})) {
|
||||
# only do it once
|
||||
$self->tellAdmin($event, "Oh dear lord what happened to the '$tree' sheriff line on the tinderbox page!!");
|
||||
$self->{'_sheriff'} = undef;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($tree eq $self->{'tree'}) {
|
||||
$self->say($event, "$event->{'from'}: Call an admin, I couldn't find the Sheriff page. Sorry!") if ($requested);
|
||||
if (defined($self->{'_sheriff'})) {
|
||||
# only do it once
|
||||
$self->tellAdmin($event, "Looks like either I am badly configured or tinderbox is down - '$tree' came up blank when I went looking for the Sheriff.");
|
||||
$self->{'_sheriff'} = undef;
|
||||
}
|
||||
} else {
|
||||
if ($requested) {
|
||||
$self->say($event, "$event->{'from'}: Are you sure there is a tree called '$tree'? I couldn't find one...");
|
||||
} # else EH!?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my ($event, @data) = @_;
|
||||
if ($data[0] eq 'sheriff') {
|
||||
$self->GetSheriff($event, $self->{'tree'}, 0);
|
||||
} else {
|
||||
$self->SUPER::Scheduled($event, @data);
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# Spell Checker Module #
|
||||
################################
|
||||
|
||||
package BotModules::Spell;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# XXX Ideally we should move to using www.dict.org
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'This module checks for spelling errors.',
|
||||
'sp' => 'If you aren\'t sure of the spelling of a word, append \'(sp)\' to the word, and it will be checked for you. '.
|
||||
'For example: \'My speling (sp?) is awful!\''
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
$self->checkSpelling($event, $message);
|
||||
return $self->SUPER::Heard(@_);
|
||||
}
|
||||
|
||||
sub Heard {
|
||||
my $self = shift;
|
||||
my ($event, $text) = @_;
|
||||
$self->checkSpelling($event, $text);
|
||||
return $self->SUPER::Heard(@_);
|
||||
}
|
||||
|
||||
sub checkSpelling {
|
||||
my $self = shift;
|
||||
my ($event, $text) = @_;
|
||||
while ($text =~ s/^.*? # take everything up to the first word to check
|
||||
\b # look for a word break
|
||||
(\w+) # take the word to spell
|
||||
\s+ # look for whitespace following it
|
||||
\(sp\??\) # followed by (sp) or (sp?)
|
||||
//isox) { # and remove everything up to here so we can do another check in a minute
|
||||
my $word = $1;
|
||||
# XXX escape $word
|
||||
$self->getURI($event, "http://www.m-w.com/cgi-bin/dictionary?va=$word", 'word', $1); # XXX should be configurable!
|
||||
}
|
||||
}
|
||||
|
||||
sub GotURI {
|
||||
my $self = shift;
|
||||
my ($event, $query, $result, $command, $word) = @_;
|
||||
if ($command ne 'word') {
|
||||
return $self->SUPER::GorURI(@_);
|
||||
} else {
|
||||
my $reply;
|
||||
# Determine if page is error or not
|
||||
if (!length($result)) {
|
||||
$self->debug("Waah, failed utterly to get a response for '$word' from the dictionary server.");
|
||||
$reply = "The dictionary service is not accessible right now, sorry.";
|
||||
} elsif ($result =~ / # Match
|
||||
The\ word\ you've\ entered\ # literal string
|
||||
isn't\ in\ the\ dictionary\. # (not very smart),
|
||||
.*? # anything (non-greedy),
|
||||
<PRE> # PRE tag,
|
||||
(.*?) # our suggestions,
|
||||
<\/PRE> # PRE tag
|
||||
/osx
|
||||
# XXX this is hardcoded to m-w.com!
|
||||
) {
|
||||
# Strip line numbering and anchor tags
|
||||
my $suggestions = $1;
|
||||
$suggestions =~ s/\s+[\d]+\.\s+//go;
|
||||
$suggestions =~ s/<a href.*?>(.*?)<\/a>/$1 /go;
|
||||
|
||||
# get them in list format
|
||||
my @suggestions = split(' ', $suggestions);
|
||||
|
||||
# Comma delimit suggestions
|
||||
local $" = ', ';
|
||||
if (@suggestions > 7) {
|
||||
# lots of suggestions!
|
||||
# 7 is not arbitrary, it's supposed to be the number
|
||||
# of items people can remember at once.
|
||||
@suggestions = @suggestions[0..6];
|
||||
$reply = "Suggestions for '$word': @suggestions[0..6]...";
|
||||
} elsif (@suggestions) {
|
||||
# just a few suggestions
|
||||
$reply = "Suggestions for '$word': @suggestions";
|
||||
} else {
|
||||
# eh? Weird. Some problem on the server probably.
|
||||
$self->debug("Didn't get any suggestions for '$word'!");
|
||||
$reply = "I have no idea what '$word' is supposed to be, sorry.";
|
||||
}
|
||||
} else {
|
||||
# horrah!
|
||||
$reply = "'$word' seems to be the correct spelling.";
|
||||
}
|
||||
$self->say($event, $reply);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# Stocks Module #
|
||||
################################
|
||||
|
||||
package BotModules::Stocks;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# XXX Per-channel configurable notification of stock changes
|
||||
# XXX Currency
|
||||
# XXX Non-US markets
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'This module gets stock quotes. Ask me a ticker symbol, I will retrieve the quote.',
|
||||
'stock' => 'Call this command with a ticker symbol to get the current stock price and change. Syntax: stock FBAR',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*stocks?\s+(.+?)\s*$/osi) {
|
||||
$self->getURI($event, "http://quote.yahoo.com/d/quotes.csv?f=sl1d1t1c1ohgv&e=.csv&s=$1", $1);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
sub GotURI {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $output, $stock) = @_;
|
||||
$self->debug($output);
|
||||
my $message = '';
|
||||
if ($event->{'channel'} ne '') {
|
||||
$message .= "$event->{'from'}: ";
|
||||
}
|
||||
# The data currently listed in this format are: ticker symbol, last price, date, time, change, open price, daily high, daily low, and volume.
|
||||
# -- http://help.yahoo.com/help/us/fin/quote/quote-05.html
|
||||
my @stockValues = split(',', $output);
|
||||
foreach my $part (@stockValues) {
|
||||
$part =~ s/"//gos; # remove all quotes. Bit of a hack, but... XXX
|
||||
}
|
||||
if ($stockValues[4] > 0) {
|
||||
$stockValues[4] = 'up ' . (0+$stockValues[4]);
|
||||
} elsif ($stockValues[4] < 0) {
|
||||
$stockValues[4] = 'down ' . (0-$stockValues[4]);
|
||||
} else {
|
||||
$stockValues[4] = 'no change';
|
||||
}
|
||||
$message .= "Stock quote for $stockValues[0]: $stockValues[1], $stockValues[4] (low: $stockValues[7], high: $stockValues[6])";
|
||||
$self->say($event, $message);
|
||||
}
|
||||
@@ -1,472 +0,0 @@
|
||||
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
################################
|
||||
# Tinderbox Module #
|
||||
################################
|
||||
|
||||
package BotModules::Tinderbox;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['trees', 1, 1, ['SeaMonkey', 'SeaMonkey-Ports', 'MozillaTest', 'Grendel']],
|
||||
['treesAnnounced', 1, 1, ['SeaMonkey', 'SeaMonkey-Ports']],
|
||||
['treesDefault', 1, 1, ['SeaMonkey']],
|
||||
['treeStates', 0, 0, {}], # ->tree->(current, previous, lastupdate)
|
||||
['lasttreesStates', 0, 0, []], # copy of trees in last test
|
||||
['tinderboxStates', 0, 0, {}], # ->tree->build->(current, previous, lastupdate)
|
||||
['updateDelay', 1, 1, 120],
|
||||
['_lastupdate', 0, 0, 0],
|
||||
['preferredLineLength', 1, 1, 100],
|
||||
['mutes', 1, 1, {}], # tree -> "channel channel channel"
|
||||
['states', 1, 1, {'success' => 'Success', 'testfailed' => 'Test Failed', 'busted' => 'Burning', }],
|
||||
['maxInChannel', 1, 1, 5], # maximum number of lines to report in a channel
|
||||
);
|
||||
}
|
||||
|
||||
# Schedule - called when bot connects to a server, to install any schedulers
|
||||
# use $self->schedule($event, $delay, $times, $data)
|
||||
# where $times is 1 for a single event, -1 for recurring events,
|
||||
# and a +ve number for an event that occurs that many times.
|
||||
sub Schedule {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
$self->schedule($event, \$self->{'updateDelay'}, -1, 'tinderbox');
|
||||
$self->SUPER::Schedule($event);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my %commands = (
|
||||
'' => 'The Tinderbox module monitors who the state of the tinderboxen.',
|
||||
'qt' => 'Quick trees, same as \'trees terse\'. You can give it a <tree> argument if you like, for example \'qt seamonkey\'.',
|
||||
'builds' => 'Gives the status of all the builds in all the trees that match a particular pattern. Syntax: \'builds <build>\'. For example: \'builds Mac\'.',
|
||||
'trees' => 'Reports on the current state of the tinderboxen. Syntax: \'trees <options> <tree>\' where <options> is any number of: '.
|
||||
'all (show all trees and all builds), main (show only main trees), burning (show only burning builds), '.
|
||||
'long, medium, short, terse (how much detail to include), and <tree> is the name of the tree to show (or a regexp matching it).',
|
||||
);
|
||||
if ($self->isAdmin($event)) {
|
||||
$commands{'mute'} = 'Disable reporting of a tree in a channel. (Only does something if the given tree exists.) Syntax: mute <tree> in <channel>';
|
||||
$commands{'unmute'} = 'Enable reporting of a tree in a channel. By default, trees are reported in all channels that the module is active in. Syntax: unmute <tree> in <channel>';
|
||||
}
|
||||
return \%commands;
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*trees?(?:\s+(.*?))?\s*(?:[, ]\s*please)?\?*\s*$/osi) {
|
||||
|
||||
# initial setup
|
||||
my $trees = -1; # 0=default; 1=all; 'x'=pattern match
|
||||
my $builds = -1; # 0=all; 1=horked and test failed; 2=horked only
|
||||
my $verbosity = -1; # 1=terse; 2; 3; 4=verbose
|
||||
|
||||
# parse parameters
|
||||
if (defined($1)) {
|
||||
foreach (split(/\s+/, $1)) {
|
||||
if (/^all$/osi) { $trees = '1' if $trees < 0; $builds = 0 if $builds < 0; }
|
||||
elsif (/^main$/osi) { $trees = '0'; }
|
||||
elsif (/^burning$/osi) { $builds = 2; }
|
||||
elsif (/^long$/osi) { $verbosity = 4; }
|
||||
elsif (/^medium$/osi) { $verbosity = 3; }
|
||||
elsif (/^short$/osi) { $verbosity = 2; }
|
||||
elsif (/^terse$/osi) { $verbosity = 1; }
|
||||
else { $trees = $_; }
|
||||
}
|
||||
}
|
||||
|
||||
# defaults
|
||||
$trees = '0' if $trees < 0;
|
||||
$builds = 1 if $builds < 0;
|
||||
$verbosity = 2 if $verbosity < 0;
|
||||
|
||||
# go
|
||||
$self->GetTrees($event, 1, $trees, $builds, $verbosity);
|
||||
|
||||
} elsif ($message =~ /^\s*builds?\s+(.*?)\s*\?*\s*$/osi) {
|
||||
$self->GetTrees($event, 2, $1);
|
||||
} elsif ($message =~ /^\s*qt(?:\s+(.+?))?\s*$/osi) {
|
||||
$self->GetTrees($event, 1, defined($1) ? $1 : 0, 1, 1);
|
||||
} elsif ($self->isAdmin($event)) {
|
||||
if ($message =~ /^\s*mute\s+(\S+?)\s+in\s+(\S+?)\s*$/osi) {
|
||||
my $tree = $1 eq 'Tinderbox' ? '' : $1;
|
||||
my $treeName = $tree eq '' ? 'all trees' : "trees named $tree";
|
||||
if (($tree eq '') or (grep $_ eq $tree, @{$self->{'trees'}})) {
|
||||
$self->{'mutes'}->{$tree} .= " $2";
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Tinderbox notifications for $treeName muted in channel $2.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: There is no tree called $tree is there?.");
|
||||
}
|
||||
} elsif ($message =~ /^\s*unmute\s+(\S+?)\s+in\s+(\S+?)\s*$/osi) {
|
||||
my $tree = $1 eq 'Tinderbox' ? '' : $1;
|
||||
my $treeName = $tree eq '' ? 'all trees' : "trees named $tree";
|
||||
if (($tree eq '') or (grep $_ eq $tree, @{$self->{'trees'}})) {
|
||||
my %mutedChannels = map { lc($_) => 1 } split(/ /o, $self->{'mutes'}->{$1});
|
||||
delete($mutedChannels{lc($2)}); # get rid of any mentions of that channel
|
||||
$self->{'mutes'}->{$1} = join(' ', keys(%mutedChannels));
|
||||
$self->saveConfig();
|
||||
$self->say($event, "$event->{'from'}: Tinderbox notifications for trees named $1 resumed in channel $2.");
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: There is no tree called $tree is there?.");
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub GetTrees {
|
||||
my $self = shift;
|
||||
my ($event, $requested, @mode) = @_;
|
||||
my @trees = @{$self->{'trees'}};
|
||||
local $" = ','; # XXX %-escape this
|
||||
my $uri = "http://tinderbox.mozilla.org/showbuilds.cgi?quickparse=1&tree=@trees";
|
||||
$self->getURI($event, $uri, $requested, @mode);
|
||||
}
|
||||
|
||||
sub GotURI {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $output, $requested, @mode) = @_;
|
||||
if ($output) {
|
||||
my $now = time();
|
||||
$self->{'_lastupdate'} = $now;
|
||||
my @lines = split(/\n/os, $output);
|
||||
|
||||
# NOTE. There is a box in Tinderbox whereby if you pass it an invalid tree name, it
|
||||
# will stop at that tree and not give you any others. It won't give you an error
|
||||
# message, either. So do not give it the wrong trees!!! (XXX should fix this)
|
||||
|
||||
# loop through quickparse output
|
||||
foreach my $line (@lines) {
|
||||
my ($type, $tree, $build, $state) = split(/\|/os, $line);
|
||||
if ($type eq 'State') {
|
||||
$self->{'treeStates'}->{$tree}->{'lastupdate'} = $now;
|
||||
if (defined($self->{'treeStates'}->{$tree}->{'current'})) {
|
||||
$self->{'treeStates'}->{$tree}->{'previous'} = $self->{'treeStates'}->{$tree}->{'current'};
|
||||
}
|
||||
$self->{'treeStates'}->{$tree}->{'current'} = $state;
|
||||
$self->{'states'}->{$state} = $state unless defined($self->{'states'}->{$state});
|
||||
} elsif ($type eq 'Build') {
|
||||
$self->{'tinderboxStates'}->{$tree}->{$build}->{'lastupdate'} = $now;
|
||||
if (defined($self->{'tinderboxStates'}->{$tree}->{$build}->{'current'})) {
|
||||
$self->{'tinderboxStates'}->{$tree}->{$build}->{'previous'} = $self->{'tinderboxStates'}->{$tree}->{$build}->{'current'};
|
||||
}
|
||||
$self->{'tinderboxStates'}->{$tree}->{$build}->{'current'} = $state;
|
||||
$self->{'states'}->{$state} = $state unless defined($self->{'states'}->{$state});
|
||||
} # else unsupported type XXX
|
||||
}
|
||||
$self->CheckForUpdates($event, $requested);
|
||||
if ($requested == 1) {
|
||||
$self->ReportState($event, @mode);
|
||||
} elsif ($requested == 2) {
|
||||
$self->ReportBuild($event, @mode);
|
||||
}
|
||||
# update list of active trees
|
||||
@{$self->{'lasttreesState'}} = @{$self->{'trees'}};
|
||||
} else {
|
||||
if ($requested) {
|
||||
$self->say($event, "$event->{'from'}: I can't access tinderbox right now, sorry.");
|
||||
}
|
||||
$self->debug('failed to get tinderbox data');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub Scheduled {
|
||||
my $self = shift;
|
||||
my ($event, @data) = @_;
|
||||
if ($data[0] eq 'tinderbox') {
|
||||
$self->GetTrees($event, 0);
|
||||
} else {
|
||||
$self->SUPER::Scheduled($event, @data);
|
||||
}
|
||||
}
|
||||
|
||||
sub CheckForUpdates {
|
||||
my $self = shift;
|
||||
my ($event, $avoidTarget) = @_;
|
||||
my $a; # disclaimer: I was asleep when I wrote the next line. I've no longer any idea what it does.
|
||||
my @trees = map { $a = $_; grep { $_ eq $a } @{$self->{'lasttreesState'}}; } @{$self->{'treesAnnounced'}};
|
||||
# After staring at it for a few minutes, I think what it does is get a list of the trees that should
|
||||
# be announced, AND that have already been found to exist. But I'm not 100% sure.
|
||||
foreach my $tree (@trees) {
|
||||
my @newTrees;
|
||||
my @newBuilds;
|
||||
my @lostBuilds;
|
||||
my @lostTrees;
|
||||
my @changes;
|
||||
|
||||
# check trees
|
||||
if (defined($self->{'treeStates'}->{$tree})) {
|
||||
if ($self->{'treeStates'}->{$tree}->{'lastupdate'} == $self->{'_lastupdate'}) {
|
||||
if (defined($self->{'treeStates'}->{$tree}->{'previous'})) {
|
||||
if ($self->{'treeStates'}->{$tree}->{'previous'} ne $self->{'treeStates'}->{$tree}->{'current'}) {
|
||||
push(@changes, "$tree has changed state from $self->{'states'}->{$self->{'treeStates'}->{$tree}->{'previous'}} to $self->{'states'}->{$self->{'treeStates'}->{$tree}->{'current'}}.");
|
||||
}
|
||||
} else {
|
||||
push(@newTrees, "New tree added to tinderbox: $tree (state: $self->{'states'}->{$self->{'treeStates'}->{$tree}->{'current'}}).");
|
||||
}
|
||||
} else {
|
||||
# tree has dissappeared!
|
||||
delete($self->{'treeStates'}->{$tree});
|
||||
push(@lostTrees, "Eek!!! Tree '$tree' has been removed from tinderbox!");
|
||||
}
|
||||
} # else tree doesn't exist
|
||||
|
||||
# check builds
|
||||
if (defined($self->{'tinderboxStates'}->{$tree})) {
|
||||
foreach my $build (keys(%{$self->{'tinderboxStates'}->{$tree}})) {
|
||||
if ($self->{'tinderboxStates'}->{$tree}->{$build}->{'lastupdate'} == $self->{'_lastupdate'}) {
|
||||
if (defined($self->{'tinderboxStates'}->{$tree}->{$build}->{'previous'})) {
|
||||
if ($self->{'tinderboxStates'}->{$tree}->{$build}->{'previous'} ne $self->{'tinderboxStates'}->{$tree}->{$build}->{'current'}) {
|
||||
push(@changes, "$tree: '$build' has changed state from $self->{'states'}->{$self->{'tinderboxStates'}->{$tree}->{$build}->{'previous'}} to $self->{'states'}->{$self->{'tinderboxStates'}->{$tree}->{$build}->{'current'}}.");
|
||||
}
|
||||
} else {
|
||||
push(@newBuilds, "New build added to $tree: $build (status: $self->{'states'}->{$self->{'tinderboxStates'}->{$tree}->{$build}->{'current'}}).");
|
||||
}
|
||||
} else {
|
||||
# build has dissappeared!
|
||||
delete($self->{'tinderboxStates'}->{$tree}->{$build});
|
||||
push(@lostBuilds, "Build '$build' has dropped from the '$tree' tinderbox.");
|
||||
}
|
||||
}
|
||||
} # else tree doesn't exist
|
||||
|
||||
# sort out which channels to talk to
|
||||
my %mutedChannels = ();
|
||||
if (defined($self->{'mutes'}->{$tree})) {
|
||||
%mutedChannels = map { lc($_) => 1 } split(/\s+/os, $self->{'mutes'}->{$tree});
|
||||
}
|
||||
if (defined($self->{'mutes'}->{''})) {
|
||||
%mutedChannels = (%mutedChannels, map { lc($_) => 1 } split(/\s+/os, $self->{'mutes'}->{''}));
|
||||
}
|
||||
if (($avoidTarget) and ($event->{'target'} eq $event->{'channel'})) {
|
||||
$mutedChannels{$event->{'channel'}} = 1;
|
||||
}
|
||||
|
||||
# speak!
|
||||
my @output = (@newTrees, @lostTrees, @newBuilds, @lostBuilds);
|
||||
foreach (@{$self->{'channels'}}) {
|
||||
unless ($mutedChannels{$_}) {
|
||||
local $event->{'target'} = $_;
|
||||
foreach (@changes) {
|
||||
$self->say($event, $_);
|
||||
}
|
||||
if (@output < 3) {
|
||||
foreach (@output) {
|
||||
$self->say($event, $_);
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "Many tree changes just occured. Check tinderbox to see what they were.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub ReportState {
|
||||
my $self = shift;
|
||||
my ($event, $trees, $builds, $verbosity) = @_;
|
||||
|
||||
# $trees: 0=default; 1=all; 'x'=pattern match
|
||||
# $builds: 0=all; 1=horked and test failed; 2=horked only
|
||||
# $verbosity: 1=terse; 2; 3; 4=verbose
|
||||
|
||||
# the complete output
|
||||
my @lines;
|
||||
|
||||
# work out which trees we want
|
||||
my @trees;
|
||||
if ($trees eq '0') {
|
||||
@trees = @{$self->{'treesDefault'}};
|
||||
} elsif ($trees eq '1') {
|
||||
@trees = @{$self->{'trees'}};
|
||||
} else {
|
||||
my $pattern = $self->sanitizeRegexp($trees);
|
||||
foreach my $tree (keys %{$self->{'treeStates'}}) {
|
||||
push(@trees, $tree) if $tree =~ /$pattern/si;
|
||||
}
|
||||
}
|
||||
|
||||
if (@trees) {
|
||||
|
||||
foreach my $tree (@trees) {
|
||||
if ((defined($self->{'treeStates'}->{$tree})) and ($self->{'treeStates'}->{$tree}->{'lastupdate'} == $self->{'_lastupdate'})) {
|
||||
|
||||
# setup
|
||||
my @output;
|
||||
my ($redShort) = ($self->{'states'}->{'bustedShort'} or split(//osi, $self->{'states'}->{'busted'}));
|
||||
my $red = 0;
|
||||
my ($orangeShort) = ($self->{'states'}->{'testfailedShort'} or split(//osi, $self->{'states'}->{'testfailed'}));
|
||||
my $orange = 0;
|
||||
my ($greenShort) = ($self->{'states'}->{'successShort'} or split(//osi, $self->{'states'}->{'success'}));
|
||||
my $green = 0;
|
||||
|
||||
# foreach build
|
||||
if (defined($self->{'tinderboxStates'}->{$tree})) {
|
||||
foreach my $build (keys(%{$self->{'tinderboxStates'}->{$tree}})) {
|
||||
if ($self->{'tinderboxStates'}->{$tree}->{$build}->{'lastupdate'} == $self->{'_lastupdate'}) {
|
||||
|
||||
my $state = $self->{'tinderboxStates'}->{$tree}->{$build}->{'current'};
|
||||
|
||||
# count results
|
||||
if ($state eq 'success') {
|
||||
$green++;
|
||||
} elsif ($state eq 'testfailed') {
|
||||
$orange++;
|
||||
} else {
|
||||
$red++;
|
||||
}
|
||||
|
||||
# make sure we should list this build
|
||||
if ($state eq 'success') {
|
||||
next if $builds >= 1;
|
||||
} elsif ($state eq 'testfailed') {
|
||||
next if $builds >= 2;
|
||||
}
|
||||
|
||||
if ($verbosity == 1) {
|
||||
my($minibuild) = split(/\s/osi, $build);
|
||||
my $ministate = $self->{'states'}->{$state.'Short'};
|
||||
if (not $ministate) {
|
||||
($ministate) = split(//osi, $self->{'states'}->{$state});
|
||||
}
|
||||
push(@output, "$minibuild: $ministate;");
|
||||
} elsif (($verbosity == 2) || ($verbosity == 3)) {
|
||||
my($minibuild) = $verbosity == 2 ? split(/\s/osi, $build) : ($build);
|
||||
my $ministate = $self->{'states'}->{$state.'Medium'};
|
||||
if (not $ministate) {
|
||||
$ministate = $self->{'states'}->{$state};
|
||||
}
|
||||
push(@output, "$minibuild ($ministate),");
|
||||
} else {
|
||||
push(@output, "[$build: $self->{'states'}->{$state}]")
|
||||
}
|
||||
|
||||
} # else build is dead
|
||||
} # (foreach build)
|
||||
} # else tree is dead
|
||||
|
||||
# pretty print it
|
||||
my @newoutput;
|
||||
if ($verbosity == 1) {
|
||||
if (@output == 0) {
|
||||
unless ($red + $green + $orange) {
|
||||
push(@output, "(none)");
|
||||
} elsif ($builds <= 1) {
|
||||
push(@output, "(all green)");
|
||||
} else {
|
||||
push(@output, "(none red)");
|
||||
}
|
||||
}
|
||||
my $ministate = $self->{'states'}->{$self->{'treeStates'}->{$tree}->{'current'}.'Short'};
|
||||
if (not $ministate) {
|
||||
($ministate) = split(//osi, $self->{'states'}->{$self->{'treeStates'}->{$tree}->{'current'}});
|
||||
}
|
||||
@newoutput = $self->wordWrap($self->{'preferredLineLength'},
|
||||
"$tree <$ministate> $redShort:${red} $orangeShort:${orange} $greenShort:${green} ",
|
||||
' ', ' ', @output);
|
||||
$newoutput[0] =~ s/^ //o;
|
||||
$newoutput[$#newoutput] =~ s/;$//o;
|
||||
push(@lines, @newoutput);
|
||||
} elsif (($verbosity == 2) || ($verbosity == 3)) {
|
||||
unless ($red+$orange+$green) {
|
||||
push(@lines, "$tree <$self->{'states'}->{$self->{'treeStates'}->{$tree}->{'current'}}>: no tinderboxen for this tree.");
|
||||
} elsif (($red) or ($orange)) {
|
||||
if (@output == 0) {
|
||||
# can only happen if $red is 0 and $builds is 1.
|
||||
push(@output, "all tinderboxen compile");
|
||||
}
|
||||
my @newoutput = $self->wordWrap($self->{'preferredLineLength'},
|
||||
"$tree <$self->{'states'}->{$self->{'treeStates'}->{$tree}->{'current'}}> $red red, $orange orange, $green green: ",
|
||||
' ', ' ', @output);
|
||||
$newoutput[0] =~ s/^ //o;
|
||||
$newoutput[$#newoutput] =~ s/,$//o;
|
||||
# if (length(@newoutput[$#newoutput]) < $self->{'preferredLineLength'} - 33) {
|
||||
# $newoutput[$#newoutput] .= " Summary: $red red, $orange orange, $green green";
|
||||
# } else {
|
||||
# push(@newoutput, " Summary: $red red, $orange orange, $green green");
|
||||
# }
|
||||
push(@lines, @newoutput);
|
||||
} else {
|
||||
push(@lines, "$tree <$self->{'states'}->{$self->{'treeStates'}->{$tree}->{'current'}}>: all $green tinderboxen green!");
|
||||
}
|
||||
} else {
|
||||
if (@output == 0) {
|
||||
unless ($red + $green + $orange) {
|
||||
push(@output, "no tinderboxen for this tree.");
|
||||
} elsif ($builds <= 1) {
|
||||
push(@output, "all tinderboxen for this tree are green!");
|
||||
} else {
|
||||
push(@output, "all tinderboxen for this tree compile successfully.");
|
||||
}
|
||||
}
|
||||
@newoutput = $self->wordWrap($self->{'preferredLineLength'},
|
||||
"$tree <$self->{'states'}->{$self->{'treeStates'}->{$tree}->{'current'}}> $red red, $orange orange, $green green: ",
|
||||
' ', ' ', @output);
|
||||
$newoutput[0] =~ s/^ //o;
|
||||
push(@lines, @newoutput);
|
||||
}
|
||||
|
||||
} # else tree is dead
|
||||
|
||||
} # (foreach tree)
|
||||
|
||||
} else { # no tree selected
|
||||
@lines = ("No tree matches the pattern '$trees', sorry!");
|
||||
}
|
||||
|
||||
$self->Report($event, 'tree status', @lines);
|
||||
}
|
||||
|
||||
sub ReportBuild {
|
||||
my $self = shift;
|
||||
my ($event, $pattern) = @_;
|
||||
|
||||
# the complete output
|
||||
my @output;
|
||||
|
||||
foreach my $tree (@{$self->{'trees'}}) {
|
||||
if ((defined($self->{'treeStates'}->{$tree})) and
|
||||
($self->{'treeStates'}->{$tree}->{'lastupdate'} == $self->{'_lastupdate'}) and
|
||||
(defined($self->{'tinderboxStates'}->{$tree}))) {
|
||||
foreach my $build (keys(%{$self->{'tinderboxStates'}->{$tree}})) {
|
||||
if (($self->{'tinderboxStates'}->{$tree}->{$build}->{'lastupdate'} == $self->{'_lastupdate'}) and
|
||||
($build =~ /\Q$pattern\E/is)) {
|
||||
push(@output, "[$build: $self->{'states'}->{$self->{'tinderboxStates'}->{$tree}->{$build}->{'current'}}]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@output = ('There are no matching builds.') unless @output;
|
||||
@output = $self->prettyPrint($self->{'preferredLineLength'}, undef, "$event->{'from'}: ", ' ', @output);
|
||||
|
||||
$self->Report($event, 'tree status', @output);
|
||||
}
|
||||
|
||||
sub Report {
|
||||
my $self = shift;
|
||||
my ($event, $what, @output) = @_;
|
||||
if (scalar(@output) > $self->{'maxInChannel'}) {
|
||||
foreach (@output) {
|
||||
$self->directSay($event, $_);
|
||||
}
|
||||
$self->channelSay($event, "$event->{'from'}: $what /msg'ed");
|
||||
} else {
|
||||
foreach (@output) {
|
||||
$self->say($event, $_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
################################
|
||||
# Translate Module #
|
||||
################################
|
||||
|
||||
package BotModules::Translate;
|
||||
use vars qw(@ISA);
|
||||
use WWW::Babelfish;
|
||||
|
||||
# Ah, the previous line looks so innocent. Yet it hides horrible
|
||||
# evil. Yes, this module requires the following:
|
||||
#
|
||||
# WWW::Babelfish
|
||||
# libwww (a bundle)
|
||||
# URI
|
||||
# MIME-Base64
|
||||
# HTML::Parser
|
||||
# HTML-Tagset
|
||||
# libnet (you probably already have this)
|
||||
# Digest::MD5
|
||||
# IO::String
|
||||
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# -- #mozilla was here! --
|
||||
# *** Signoff: techbot_Hixie (~techbot_Hixie@129.59.231.42) has left IRC [Leaving]
|
||||
# <timeless> oops, i killed your techbot
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['languages', 1, 1, {
|
||||
'en' => 'English',
|
||||
'fr' => 'French',
|
||||
'de' => 'German',
|
||||
'it' => 'Italian',
|
||||
'es' => 'Spanish',
|
||||
}], # short code => Babelfish Language Name
|
||||
['defaultLanguage', 1, 1, 'en'],
|
||||
);
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my @languages = keys(%{$self->{'languages'}});
|
||||
local $";
|
||||
$" = '|';
|
||||
return {
|
||||
'' => 'Uses babelfish.altavista.com to translate something.',
|
||||
'translate' => "Syntax: \'translate [from (@languages)] [to (@languages)] sentence\'",
|
||||
'x' => 'Same as translate (which see).',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*(?:translate|x)\s+(.*?)\s*$/osi) {
|
||||
$self->Translate($event, $1);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub translate_do {
|
||||
my $self = shift;
|
||||
my ($event, $lang1, $lang2, $words) = @_;
|
||||
my $translate_babelfish = new WWW::Babelfish();
|
||||
my $result = $translate_babelfish->translate(
|
||||
'source' => $self->{'languages'}->{$lang1},
|
||||
'destination' => $self->{'languages'}->{$lang2},
|
||||
'text' => $words,
|
||||
);
|
||||
if ($result !~ /^ *$/os) {
|
||||
return "$event->{'from'}: $result";
|
||||
} else {
|
||||
my $error = $translate_babelfish->error;
|
||||
if ($error =~ /^ *$/os) {
|
||||
return "$event->{'from'}: I'm afraid I cannot translate that from $self->{'languages'}->{$lang1} to $self->{'languages'}->{$lang2}.";
|
||||
} else {
|
||||
return "$event->{'from'}: $error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ChildCompleted - Called when a child process has quit
|
||||
sub ChildCompleted {
|
||||
my $self = shift;
|
||||
my ($event, $type, $output, @data) = @_;
|
||||
if ($type eq 'babelfish') {
|
||||
$self->say($event, $output);
|
||||
} else {
|
||||
$self->SUPER::ChildCompleted($event, $type, $output, @data);
|
||||
}
|
||||
}
|
||||
|
||||
sub Translate {
|
||||
my $self = shift;
|
||||
my ($event, $rest) = @_;
|
||||
my ($lang1, $lang2, $words) = (
|
||||
$self->{'defaultLanguage'},
|
||||
$self->{'defaultLanguage'},
|
||||
);
|
||||
|
||||
my @languages = keys(%{$self->{'languages'}});
|
||||
local $";
|
||||
$" = '|';
|
||||
|
||||
# check syntax
|
||||
if ($rest =~ /^\s*from\s+(@languages)\s+to\s+(@languages)\s+(.+)$/os) {
|
||||
$lang1 = $1;
|
||||
$lang2 = $2;
|
||||
$words = $3;
|
||||
} elsif ($rest =~ /^\s*to\s+(@languages)\s+from\s+(@languages)\s+(.+)$/os) {
|
||||
$lang2 = $1;
|
||||
$lang1 = $2;
|
||||
$words = $3;
|
||||
} elsif ($rest =~ /^\s*(from|to)\s+(@languages)\s+(.+)$/os) {
|
||||
$lang1 = $2 if $1 eq 'from';
|
||||
$lang2 = $2 if $1 eq 'to';
|
||||
$words = $3;
|
||||
} else {
|
||||
$self->say($event, "$event->{'from'}: Noooo... That\'s not the right syntax at all! Try something like \'translate [from (@languages)] [to (@languages)] sentence\'");
|
||||
return;
|
||||
}
|
||||
|
||||
# translate
|
||||
if ($lang1 eq $lang2) {
|
||||
$self->say($event, "$event->{'from'}: Erm, well, translating from one language to the same language... doesn't change anything!");
|
||||
} else {
|
||||
$self->spawnChild($event, \&translate_do, [$self, $event, $lang1, $lang2, $words], 'babelfish', []);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
################################
|
||||
# UUIDGen Module #
|
||||
################################
|
||||
|
||||
# "uuidgen" should be installed on the path somewhere.
|
||||
# you can get the source of uuidgen from CVS, see:
|
||||
# http://lxr.mozilla.org/mozilla/source/webtools/mozbot/uuidgen/
|
||||
|
||||
package BotModules::UUIDGen;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'This module is an interface to the uuidgen application.',
|
||||
'uuid' => 'Generates a UUID.',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*uuid(?:[\s,!?]+please)?[\s,!?]*\s*$/osi) {
|
||||
$self->spawnChild($event, 'uuidgen', [], 'UUID', []);
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
|
||||
# ChildCompleted - Called when a child process has quit
|
||||
sub ChildCompleted {
|
||||
my $self = shift;
|
||||
my ($event, $type, $output, @data) = @_;
|
||||
if ($type eq 'UUID') {
|
||||
$self->say($event, $output);
|
||||
} else {
|
||||
return $self->SUPER::ChildCompleted(@_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
################################
|
||||
# WWW Module #
|
||||
################################
|
||||
|
||||
package BotModules::WWW;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
# $self->registerVariables(
|
||||
# # [ name, save?, settable? ]
|
||||
# ['x', 1, 1, 0],
|
||||
# );
|
||||
}
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'The WWW module provides a web interface.',
|
||||
'wwwsize' => 'Reports on the size of a webpage. Syntax: \'wwwsize http://...\'',
|
||||
'wwwlint' => 'Reports on whether the webpage contains any obvious (I mean _really_ obvious) no-nos like <layer> or document.all. Syntax: \'wwwlint http://...\'',
|
||||
'wwwdoctype' => 'Reports on the doctype of a webpage. (Warning: Does not check that the doctype is not commented out!) Syntax: \'wwwdoctype http://...\'',
|
||||
'wwwtitle' => 'Tries to heuristically determine a web page\'s title. Syntax: \'wwwtitle http://...\'',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*wwwsize\s+(.+?)\s*$/osi) {
|
||||
$self->Fetch($event, $1, 'size');
|
||||
} elsif ($message =~ /^\s*wwwlint\s+(.+?)\s*$/osi) {
|
||||
$self->Fetch($event, $1, 'lint');
|
||||
} elsif ($message =~ /^\s*wwwdoctype\s+(.+?)\s*$/osi) {
|
||||
$self->Fetch($event, $1, 'doctype');
|
||||
} elsif ($message =~ /^\s*wwwtitle\s+(.+?)\s*$/osi) {
|
||||
$self->Fetch($event, $1, 'title');
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # dealt with it...
|
||||
}
|
||||
|
||||
sub Fetch {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $type) = @_;
|
||||
$self->getURI($event, $uri, $type);
|
||||
}
|
||||
|
||||
sub GotURI {
|
||||
my $self = shift;
|
||||
my ($event, $uri, $output, $type) = @_;
|
||||
my $chars = length($output);
|
||||
if ($type eq 'size') {
|
||||
if ($chars) {
|
||||
$self->say($event, "$uri is $chars bytes long.");
|
||||
} else {
|
||||
$self->say($event, "$uri is either empty, or I could not download it.");
|
||||
}
|
||||
} elsif ($type eq 'lint') {
|
||||
# ignore whether things are commented out or not.
|
||||
unless ($chars) {
|
||||
$self->say($event, "$uri is either empty, or I could not download it.");
|
||||
} else {
|
||||
my @status;
|
||||
if ($output =~ /document\.all/os) {
|
||||
push(@status, 'document.all');
|
||||
}
|
||||
if ($output =~ /document\.layers/os) {
|
||||
push(@status, 'document.layers');
|
||||
}
|
||||
if ($output =~ /<i?layer/osi) {
|
||||
push(@status, 'the <layer> tag');
|
||||
}
|
||||
if (@status) {
|
||||
my $status = shift(@status);
|
||||
if (@status) {
|
||||
while (scalar(@status) > 1) {
|
||||
$status .= ', '.shift(@status);
|
||||
}
|
||||
$status .= ' and '.shift(@status);
|
||||
}
|
||||
$self->say($event, "$uri contains $status.");
|
||||
} else {
|
||||
$self->say($event, "$uri doesn't have any _obvious_ flaws..."); # XXX doesn't work! try php.net
|
||||
}
|
||||
}
|
||||
} elsif ($type eq 'doctype') {
|
||||
# assume doctype is not commented.
|
||||
unless ($chars) {
|
||||
$self->say($event, "$uri is either empty, or I could not download it.");
|
||||
} elsif ($output =~ /(<!DOCTYPE\s[^>]*>)/osi) {
|
||||
my $doctype = $1;
|
||||
$doctype =~ s/[\n\r]+/ /gosi;
|
||||
|
||||
# -- #mozilla was here --
|
||||
# <Hixie> it would break 99% of the web if we didn't do it that way.
|
||||
# <Hixie> including most of my test cases ;-)
|
||||
# <dbaron> test cases don't matter...
|
||||
# <dbaron> you'll fix them if we decide they're wrong
|
||||
# <dbaron> but the web is a problem
|
||||
|
||||
if (length($doctype) > 220) { # arbitrary length greater than two 80 character lines
|
||||
$self->say($event, "$uri has a very long and possibly corrupted doctype (maybe it has an internal subset).");
|
||||
} else {
|
||||
$self->say($event, "$uri has the following doctype: $doctype");
|
||||
}
|
||||
} else {
|
||||
$self->say($event, "$uri has no specified doctype.");
|
||||
}
|
||||
} elsif ($type eq 'title') {
|
||||
# assume doctype is not commented.
|
||||
unless ($chars) {
|
||||
$self->say($event, "$uri is either empty, or I could not download it.");
|
||||
} elsif ($output =~ /<title\s*>(.*?)<\/title\s*>/osi or
|
||||
$output =~ /<h1\s*>(.*?)<\/h1\s*>/osi) {
|
||||
my $title = $1;
|
||||
$title =~ s/\s+/ /gosi;
|
||||
if (length($title) > 100) { # arbitrary length
|
||||
$title = substr($title, 0, 100) . '...';
|
||||
}
|
||||
$self->say($event, "$uri has the following title: $title");
|
||||
} else {
|
||||
$self->say($event, "$uri has no specified title.");
|
||||
}
|
||||
} else {
|
||||
return $self->SUPER::GotURI(@_);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
################################
|
||||
# Wishlist Module #
|
||||
################################
|
||||
|
||||
package BotModules::Wishlist;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
my $reply = {
|
||||
'' => 'A module to store wishlist items, typically used to file bugs on the bot, but really for that you should use Bugzilla -- http://bugzilla.mozilla.org/ -- component MozBot in product WebTools.',
|
||||
'wish' => 'Adds an item to the wishlist. Please use Bugzilla for this purpose though, see http://bugzilla.mozilla.org/ product WebTools, component Mozbot. Syntax: \'wish <text of wish>\'',
|
||||
'wishes' => 'Causes the bot to list all the wishes that have been made. Since this may be long, it may only be done in a /msg. Syntax: \'wishes\'',
|
||||
};
|
||||
$$reply{''} .= ' To remove wishes, use the following command: vars Wishlist wishes \'-<full text of the wish to remove>\'' if $self->isAdmin($event);
|
||||
return $reply;
|
||||
}
|
||||
|
||||
# RegisterConfig - Called when initialised, should call registerVariables
|
||||
sub RegisterConfig {
|
||||
my $self = shift;
|
||||
$self->SUPER::RegisterConfig(@_);
|
||||
$self->registerVariables(
|
||||
# [ name, save?, settable? ]
|
||||
['wishes', 1, 1, []],
|
||||
['reply', 1, 1, 'Noted!'],
|
||||
);
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*(?:i\s+)?wish(?:list)?[-\s:.,;!]+(...+?)\s*$/osi) {
|
||||
push(@{$self->{'wishes'}}, "<$event->{'from'}> $1");
|
||||
$self->say($event, "$event->{'from'}: $self->{'reply'}");
|
||||
$self->saveConfig();
|
||||
} elsif ($message =~ /^\s*wishes[\s?]*$/osi) {
|
||||
if (@{$self->{'wishes'}}) {
|
||||
$self->directSay($event, 'Wishes:');
|
||||
foreach (@{$self->{'wishes'}}) {
|
||||
$self->directSay($event, " $_");
|
||||
}
|
||||
$self->directSay($event, 'End of wishes.');
|
||||
} else {
|
||||
$self->directSay($event, 'No-one has wished for anything!');
|
||||
}
|
||||
$self->channelSay($event, "$event->{'from'}: wishlist /msg'ed");
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
return 0; # we've dealt with it, no need to do anything else.
|
||||
}
|
||||
@@ -1,841 +0,0 @@
|
||||
MODULE API DOCUMENTATION
|
||||
========================
|
||||
|
||||
This file documents the mozbot 2.2 bot module API.
|
||||
Revisions are welcome.
|
||||
|
||||
Sample module
|
||||
-------------
|
||||
|
||||
Here is the HelloWorld module:
|
||||
|
||||
################################
|
||||
# Hello World Module #
|
||||
################################
|
||||
|
||||
package BotModules::HelloWorld;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
1;
|
||||
|
||||
sub Help {
|
||||
my $self = shift;
|
||||
my ($event) = @_;
|
||||
return {
|
||||
'' => 'This is the demo module that says Hello World.',
|
||||
'hi' => 'Requests that the bot emit a hello world string.',
|
||||
};
|
||||
}
|
||||
|
||||
sub Told {
|
||||
my $self = shift;
|
||||
my ($event, $message) = @_;
|
||||
if ($message =~ /^\s*hi\s*$/osi) {
|
||||
$self->say($event, 'Hello World!');
|
||||
} else {
|
||||
return $self->SUPER::Told(@_);
|
||||
}
|
||||
}
|
||||
|
||||
################################
|
||||
|
||||
|
||||
Creating a module
|
||||
-----------------
|
||||
|
||||
Modules are perl objects with names that start with 'BotModules::'
|
||||
and that are stored in files with the '.bm' extension in the
|
||||
'BotModules' directory. The first non-comment line of each module
|
||||
should be the 'package' line, which in the HelloWorld module reads:
|
||||
|
||||
package BotModules::HelloWorld;
|
||||
|
||||
For a module to work correctly, it should inherit from the
|
||||
'BotModules' module (which is implemented internally in the main bot
|
||||
executable). This is done by including the following two lines
|
||||
immediately after the 'package' line:
|
||||
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(BotModules);
|
||||
|
||||
Since modules are dynamically loaded and unloaded, they should avoid
|
||||
using package globals. All variables should be stored in the '$self'
|
||||
blessed hashref. For more details, see the documentation of the
|
||||
'Initialise' function (below). Another result of the dynamic nature
|
||||
of modules is that they should not use BEGIN {} or END {} blocks, nor
|
||||
should they execute any code during their evaluation. Thus,
|
||||
immediately after the @ISA... line, the module should return success.
|
||||
This can be done easily:
|
||||
|
||||
1;
|
||||
|
||||
Following this, you are free to implement all the functions you need
|
||||
for your module. Certain functions have certain calling semantics,
|
||||
these are described below.
|
||||
|
||||
|
||||
Module Functions
|
||||
----------------
|
||||
|
||||
This section contains the names and descriptions of the functions in
|
||||
your module that will be called automatically depending on what is
|
||||
happening on IRC.
|
||||
|
||||
All your functions should start by shifting the $self variable from the
|
||||
argument list:
|
||||
|
||||
my $self = shift;
|
||||
|
||||
After this, it is common to get the other variables too:
|
||||
|
||||
my ($event, @anythingElse) = @_;
|
||||
|
||||
...where the bit in the brackets is given in the brackets of the
|
||||
definitions of the functions as shown below. For example, for
|
||||
JoinedChannel it would be ($event, $channel), so a function to override
|
||||
the default JoinedChannel action would be something like:
|
||||
|
||||
sub JoinedChannel {
|
||||
my $self = shift;
|
||||
my ($event, $channel) = @_;
|
||||
# ...
|
||||
return $self->SUPER::JoinedChannel($event, $channel); # call inherited method
|
||||
}
|
||||
|
||||
Many functions have to return a special value, typically 0 if the event
|
||||
was handled, and 1 if it was not.
|
||||
|
||||
What actually happens is that for every event that occurs, the bot
|
||||
has a list of event handlers it should call. For example, if
|
||||
someone says 'bot: hi' then the bot wants to call the Told()
|
||||
handler and the Baffled() handler. It first calls the Told()
|
||||
handler of every module. It then looks to see if any of the
|
||||
handlers returned 0. If so, it stops. Note, though, that every
|
||||
Told() handler got called! If none of the handlers returned 0,
|
||||
then it looks to see what the highest return value was. If it was
|
||||
greater than 1, then it increments the 'level' field of the $event
|
||||
hash (see below) and calls all the Told() handlers that returned 1
|
||||
or more again. This means that if your module decides whether or
|
||||
not to respond by looking at a random number, it is prone to being
|
||||
confused by another module!
|
||||
|
||||
YOU SHOULD NOT USE RANDOM NUMBERS TO DECIDE WHETHER OR NOT TO
|
||||
RESPOND TO A MESSAGE!
|
||||
|
||||
Once all the relevant Told() handlers have been called again, the
|
||||
bot once again examines all the return results, and stops if any
|
||||
returned 0. If none did and if the current value of the level field
|
||||
is less than the highest number returned from any of the modules,
|
||||
then it repeats the whole process again. Once the level field is
|
||||
equal to the highest number returned, then, if no module has ever
|
||||
returned 0 in that whole loopy time, it moves on to the next
|
||||
handler in the list (in this case Baffled()), and does the
|
||||
_entire_ process again.
|
||||
|
||||
You may be asking yourself "Why oh why!". It is to allow you to
|
||||
implement priority based responses. If your module returns '5' to
|
||||
the Told() function, and only handles the event (i.e., only
|
||||
returns 0) once the level field is 5, then it will only handle the
|
||||
event if no other module has wanted to handle the event in any of
|
||||
the prior levels.
|
||||
|
||||
It also allows inter-module communication, although since that is
|
||||
dodgy, the details are left as an exercise to the reader.
|
||||
|
||||
Important: if you use this, make sure that you only reply to the
|
||||
user once, based on the $event->{'level'} field. e.g., if you
|
||||
replied when level was zero, then don't reply _again_ when it is
|
||||
set to 1. This won't be a problem if your module only returns 1
|
||||
(the default) or 0 (indicating success).
|
||||
|
||||
|
||||
*** Help($event)
|
||||
|
||||
Every module that does anything visible should provide a 'Help'
|
||||
function. This is called by the General module's 'help' command
|
||||
implementation.
|
||||
|
||||
This function should return a hashref, with each key representing a
|
||||
topic (probably a command) and each value the relevant help string.
|
||||
The '' topic is special and should contain the help string for the
|
||||
module itself.
|
||||
|
||||
|
||||
*** Initialise()
|
||||
|
||||
Called when the module is loaded.
|
||||
|
||||
No special return values.
|
||||
|
||||
|
||||
*** Schedule($event)
|
||||
|
||||
Schedule - Called after bot is set up, to set up any scheduled
|
||||
tasks. See 'schedule' in the API documentation below for information
|
||||
on how to do this.
|
||||
|
||||
No special return values. Always call inherited function!
|
||||
|
||||
|
||||
*** JoinedIRC($event)
|
||||
|
||||
Called before joining any channels (but after module is setup). This
|
||||
does not get called for dynamically installed modules.
|
||||
|
||||
No special return values. Always call inherited function!
|
||||
|
||||
|
||||
*** JoinedChannel($event, $channel)
|
||||
|
||||
Called after joining a channel for the first time, for example if
|
||||
the bot has been /invited.
|
||||
|
||||
No special return values. Always call inherited the function, as this
|
||||
is where the autojoin function is implemented.
|
||||
|
||||
|
||||
*** PartedChannel($event, $channel)
|
||||
|
||||
Called after the bot has left a channel, for example if the bot has
|
||||
been /kicked.
|
||||
|
||||
No special return values. Always call inherited the function, as this
|
||||
is where the autopart function is implemented.
|
||||
|
||||
|
||||
*** InChannel($event)
|
||||
|
||||
Called to determine if the module is 'in' the channel or not.
|
||||
Generally you will not need to override this.
|
||||
|
||||
Return 0 if the module is not enabled in the channel in which the
|
||||
event occured, non zero otherwise.
|
||||
|
||||
|
||||
*** IsBanned($event)
|
||||
|
||||
Same as InChannel(), but for determining if the user is banned or
|
||||
not.
|
||||
|
||||
Return 1 if the user that caused the event is banned from this
|
||||
module, non zero otherwise.
|
||||
|
||||
|
||||
*** Log($event)
|
||||
|
||||
Called once for most events, regardless of the result of the
|
||||
other handlers. This is the event to use if you wish to log
|
||||
everything that happens on IRC (duh).
|
||||
|
||||
No return value.
|
||||
|
||||
|
||||
*** Baffled($event, $message)
|
||||
|
||||
Called for messages prefixed by the bot's nick which we don't
|
||||
understand (i.e., that Told couldn't deal with).
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation of Baffled() does).
|
||||
|
||||
|
||||
*** Told($event, $message)
|
||||
|
||||
Called for messages heard that are prefixed by the bot's nick. See
|
||||
also Baffled.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation of Told() does).
|
||||
|
||||
|
||||
*** Heard($event, $message)
|
||||
|
||||
Called for all messages not aimed directly at the bot, or those
|
||||
aimed at the bot but with no content (e.g., "bot!!!").
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation of Heard() does).
|
||||
|
||||
|
||||
*** Felt($event, $message)
|
||||
|
||||
Called for all emotes containing bot's nick.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation of Felt() does).
|
||||
|
||||
|
||||
*** Saw($event, $message)
|
||||
|
||||
Called for all emotes except those directly at the bot.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** Invited($event, $channel)
|
||||
|
||||
Called when bot is invited into another channel.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** Kicked($event, $channel)
|
||||
|
||||
Called when bot is kicked out of a channel.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** ModeChange($event, $what, $change, $who)
|
||||
|
||||
Called when either the channel or a person has a mode flag changed.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** GotOpped($event, $channel, $who)
|
||||
|
||||
Called when the bot is opped. (Not currently implemented.)
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** GotDeopped($event, $channel, $who)
|
||||
|
||||
Called when the bot is deopped. (Not currently implemented.)
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** Authed($event, $who)
|
||||
|
||||
Called when someone authenticates with us. Note that you cannot
|
||||
do any channel-specific operations here since authentication is
|
||||
done directly and without any channels involved. (Of course,
|
||||
you can always do channel-wide stuff based on a channel list...)
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** SpottedNickChange($event, $from, $to)
|
||||
|
||||
Called when someone changes their nick. You cannot use directSay
|
||||
here, since $event has the details of the old nick. And 'say' is
|
||||
useless since the channel is the old userhost string... This may be
|
||||
changed in a future implementation.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** SpottedTopicChange($event, $channel, $newtopic)
|
||||
|
||||
Called when the topic in a channel is changed.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** SpottedJoin($event, $channel, $who)
|
||||
|
||||
Called when someone joins a channel.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** SpottedPart($event, $channel, $who)
|
||||
|
||||
Called when someone leaves a channel.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** SpottedKick($event, $channel, $who)
|
||||
|
||||
Called when someone leaves a channel, um, forcibly.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** SpottedQuit($event, $who, $why)
|
||||
|
||||
Called when someone leaves a server. You can't use say or directSay
|
||||
as no channel involved and the user has quit, anyway (obviously).
|
||||
This may change in future implementations (don't ask me how, please...).
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** SpottedOpping($event, $channel, $who)
|
||||
|
||||
Called when someone is opped. (Not currently implemented.)
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** SpottedDeopping($event, $channel, $who)
|
||||
|
||||
Called when someone is... deopped, maybe? (Not currently implemented.)
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** CTCPPing($event, $who, $what)
|
||||
|
||||
Called when the bot receives a CTCP ping.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** CTCPVerson($event, $who, $what)
|
||||
|
||||
Called when the bot receives a CTCP verson.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** CTCPSource($event, $who, $what)
|
||||
|
||||
Called when the bot receives a CTCP source.
|
||||
|
||||
Return 1 if you can't do anything (this is all the default
|
||||
implementation does).
|
||||
|
||||
|
||||
*** Scheduled($event, @data)
|
||||
|
||||
Called when a scheduled timer triggers. (See 'schedule' in the next
|
||||
section to see how to schedule stuff.) By default, if the first
|
||||
element of the @data array is a coderef, then the coderef is called
|
||||
with ($event,@data) as the arguments. Otherwise, 'debug' is called
|
||||
(see below).
|
||||
|
||||
No special return values. Always call inherited function if you
|
||||
cannot handle the scheduled event yourself.
|
||||
|
||||
|
||||
*** GotURI($event, $uri, $contents, @data)
|
||||
|
||||
Called when a requested URI has been downloaded. $contents contains
|
||||
the actual contents of the file. See getURI().
|
||||
|
||||
No special return values.
|
||||
|
||||
|
||||
*** ChildCompleted($event, $type, $output, @data)
|
||||
|
||||
Called when a spawned child has completed. $output contains
|
||||
the output of the process. $type contains the child type as
|
||||
given to the spawnChild() API function (which see).
|
||||
|
||||
No special return values. Always call the inherited function if
|
||||
you cannot handle the given '$type'!
|
||||
|
||||
|
||||
*** RegisterConfig()
|
||||
|
||||
Called when initialised, should call registerVariables(), which see
|
||||
below.
|
||||
|
||||
No special return values. Always call inherited function!
|
||||
|
||||
|
||||
*** Set($event, $variable, $value)
|
||||
|
||||
Called to set a variable to a particular value.
|
||||
|
||||
Should return one of the following:
|
||||
-1 - silent success (caller should not report back to user)
|
||||
0 - success
|
||||
1 - can't set variable because it is of type ref($module->{$variable})
|
||||
2 - variable not found or not writable (if $module->{$variable})
|
||||
3 - variable is list and wrong format was used
|
||||
4 - variable is hash and wrong format was used
|
||||
9 - unknown error
|
||||
|
||||
Note that error codes 1-4 are probably too specific to the default
|
||||
'Set' function to be of any use. Reporting your own error messages
|
||||
is fine.
|
||||
|
||||
Always call inherited function if you cannot set the variable yourself!
|
||||
|
||||
|
||||
*** Get($event, $variable)
|
||||
|
||||
Called to get a particular variable.
|
||||
|
||||
Should return the value of the variable. Default returns the value
|
||||
of $self->{$variable}.
|
||||
|
||||
Always call inherited function if you cannot get the variable yourself!
|
||||
|
||||
|
||||
The $event variable is a hash with the following keys:
|
||||
|
||||
'bot' => the IRC bot object - DO NOT USE THIS!!! [1]
|
||||
'channel' => the channel the event occured in, or '' if n/a [2]
|
||||
'from' => the nick of the person who created the event, if any
|
||||
'target' => the target of the 'say' function (channel || from)
|
||||
'user' => the userhost of the event
|
||||
'data' => the main data of the event
|
||||
'to' => the target of the event
|
||||
'subtype' => the IRC module's idea of what the event was [1]
|
||||
'maintype' => the name of the first handler called (eg. 'Told')
|
||||
'level' => the number of times the handler has been called in a row
|
||||
'userName' => the name of the user as they authenticated
|
||||
'userFlags' => used internally for the implementation of isAdmin(). [1]
|
||||
'nick' => the nick of the bot
|
||||
|
||||
It is passed to most functions, as the first parameter. Modify at your
|
||||
own risk! ;-) If you do write to this hash at all, ensure that you make
|
||||
a 'local' copy first. See the 'Parrot' module for an example of safely
|
||||
modifying the $event hash. Note that some of these fields may be
|
||||
inaccurate at times, due to peculiarities of the IRC protocol.
|
||||
|
||||
[1]: These fields are dependent on the underlying implementation, so
|
||||
if you use them then your modules will not be compatible with any other
|
||||
implementations that use the same API. The 'bot' field in particular is
|
||||
a blessed reference to a Net::IRC::Connection object in this
|
||||
implementation, and is passed around so that the API functions know
|
||||
what to operate on. However, in a POE implementation it could be
|
||||
something totally different, maybe even undef. There are some other
|
||||
fields in the $event hash that start with an underscore (in particular
|
||||
there is '_event'). Do not even _think_ about using those. Using them
|
||||
is akin to hard-coding the ionode of the 'ls' program into your source
|
||||
so that you can read directories by branching to a disk address.
|
||||
|
||||
[2]: The 'channel' field is ALWAYS lowercase. You should always lowercase
|
||||
any channel names you get from users before using them in comparisons or
|
||||
hash lookups.
|
||||
|
||||
|
||||
Module API
|
||||
----------
|
||||
|
||||
This section contains the names and descriptions of the functions
|
||||
that your module can call. While you can override these, it is not
|
||||
recommended.
|
||||
|
||||
*** debug(@messages)
|
||||
|
||||
Outputs each item in @messages to the console (or the log file if
|
||||
the bot has lost its controlling tty).
|
||||
|
||||
Example:
|
||||
$self->debug('about to fetch listing from FTP...');
|
||||
|
||||
|
||||
*** saveConfig()
|
||||
|
||||
Saves the state of the module's registered variables to the
|
||||
configuration file. This should be called when the variables have
|
||||
changed.
|
||||
|
||||
Example:
|
||||
$self->saveConfig(); # save our state!
|
||||
|
||||
|
||||
*** registerVariables( [ $name, $persistent, $settable, $value ] )
|
||||
|
||||
Registers variables (duh). It actually takes a list of arrayrefs.
|
||||
The first item in each arrayref is the name to use (the name of the
|
||||
variable in the blessed hashref that is the module's object, i.e.
|
||||
$self). The second controls if the variable is saved when
|
||||
saveConfig() is called. If it is set to 1 then the variable is
|
||||
saved, if 0 then it is not, and if undef then the current setting is
|
||||
not changed. Similarly, the third item controls whether or not the
|
||||
variable can be set using the 'vars' command (in the Admin
|
||||
module). 1 = yes, 0 = no, undef = leave unchanged. The fourth value,
|
||||
if defined, is used to set the variable. See the Initialise
|
||||
function's entry for more details.
|
||||
|
||||
Example:
|
||||
$self->registerVariables(
|
||||
[ 'ftpDelay', 1, 1, 60 ],
|
||||
[ 'ftpSite', 1, 1, 'ftp.mozilla.org' ],
|
||||
);
|
||||
|
||||
|
||||
*** schedule($event, $time, $times, @data)
|
||||
|
||||
Schedules one or more events. $event is the usual event hash. $time
|
||||
is the number of seconds to wait. It can be a scalarref to a
|
||||
variable that contains this number, too, in which case it is
|
||||
dereferenced. This comes in useful for making the frequency of
|
||||
repeating events customisable. $times is the number of times to
|
||||
perform the event, which can also be -1 meaning 'forever'. @data
|
||||
(the remainder of the parameters) will be passed, untouched, to the
|
||||
event handler, Scheduled. See the previous section.
|
||||
|
||||
Example:
|
||||
$self->schedule($event, \$self->{'ftpDelay'}, -1, 'ftp', \$ftpsite);
|
||||
|
||||
|
||||
*** getURI($event, $uri, @data)
|
||||
|
||||
Gets a URI in the background then calls GotURI (which see, above).
|
||||
|
||||
Example:
|
||||
$self->getURI($event, $ftpsite, 'ftp');
|
||||
|
||||
|
||||
*** spawnChild($event, $command, $arguments, $type, $data)
|
||||
|
||||
Spawns a child in the background then calls ChildCompleted (which see,
|
||||
above). $arguments and $data are array refs! $command is either a
|
||||
command name (e.g., 'wget', 'ls') or a CODEREF. If it is a CODEREF,
|
||||
then you will be wanting to make sure that the first argument is
|
||||
the object reference, unless we are talking inlined code or something...
|
||||
|
||||
Example:
|
||||
$self->spawnChild($event, '/usr/games/fortune', ['-s', '-o'],
|
||||
'fortune', [@data]);
|
||||
|
||||
*** getModule($name)
|
||||
|
||||
Returns a reference to the module with the given name. In general you
|
||||
should not need to use this, but if you write a management module, for
|
||||
instance, then this could be useful. See God.bm for an example of this.
|
||||
|
||||
IT IS VITAL THAT YOU DO NOT KEEP THE REFERENCE
|
||||
THAT THIS FUNCTION RETURNS!!!
|
||||
|
||||
If you did so, the module would not get garbage collected if it ever
|
||||
got unloaded or some such.
|
||||
|
||||
Example:
|
||||
my $module = $self->getModule('Admin');
|
||||
push(@{$module->{'files'}}, 'BotModules/SupportFile.pm');
|
||||
|
||||
|
||||
*** unescapeXML($xml)
|
||||
|
||||
Performs the following conversions on the argument and returns the result:
|
||||
' => '
|
||||
" => "
|
||||
< => <
|
||||
> => >
|
||||
& => &
|
||||
|
||||
Example:
|
||||
my $text = $self->unescapeXML($output);
|
||||
|
||||
|
||||
*** tellAdmin($event, $data);
|
||||
|
||||
Tries to tell an administrator $data. As currently implemented, only
|
||||
one administrator will get the message, and there is no guarentee
|
||||
that they will read it or even that the admin in question is
|
||||
actually on IRC at the time.
|
||||
|
||||
Example:
|
||||
$self->tellAdmin($event, 'Someone just tried to crack me...');
|
||||
|
||||
|
||||
*** say($event, $data)
|
||||
|
||||
Says $data in whatever channel the event was spotted in (this can be
|
||||
/msg if that is how the event occured).
|
||||
|
||||
Example:
|
||||
$self->say($event, 'Yo, dude.');
|
||||
|
||||
|
||||
*** announce($event, $data)
|
||||
|
||||
Says $data in all the channels the module is in.
|
||||
|
||||
Example:
|
||||
$self->announce($event, 'Bugzilla is back up.');
|
||||
|
||||
|
||||
*** directSay($event, $data)
|
||||
|
||||
Sends a message directly to the cause of the last event (i.e., like
|
||||
/msg). It is recommended to use 'say' normally, so that users have a
|
||||
choice of whether or not to get the answer in the channel (they
|
||||
would say their command there) or not (they would /msg their
|
||||
command).
|
||||
|
||||
Example:
|
||||
$self->directSay($event, 'Actually, that\'s not right.');
|
||||
|
||||
|
||||
*** directSay($event, $data)
|
||||
|
||||
Sends a message to the channel in which the message was given.
|
||||
If the original command was sent in a /msg, then this will result
|
||||
in precisely nothing. Useful in conjunction with directSay() to
|
||||
make it clear that a reply was sent privately.
|
||||
|
||||
Example:
|
||||
$self->directSay($event, $veryLongReply);
|
||||
$self->channelSay($event, "$event->{'from'}: data /msg'ed");
|
||||
|
||||
|
||||
*** emote($event, $what)
|
||||
*** directEmote($event, $what)
|
||||
|
||||
Same as say() and directSay(), but do the equivalent of /me instead.
|
||||
|
||||
Examples:
|
||||
$self->emote($event, "slaps $event->{'from'} with a big smelly trout.");
|
||||
$self->directEmote($event, "waves.");
|
||||
|
||||
|
||||
*** sayOrEmote($event, $what)
|
||||
*** directSayOrEmote($event, $what)
|
||||
|
||||
Call say (directSay) or emote (directEmote) based on the contents of $what.
|
||||
If $what starts with '/me' then the relevant emote variation is called,
|
||||
otherwise the say variations are used. The leading '/me' is trimmed before
|
||||
being passed on.
|
||||
|
||||
Examples:
|
||||
$self->sayOrEmote($event, $greeting);
|
||||
$self->directSayOrEmote($event, $privateMessage);
|
||||
|
||||
|
||||
*** ctcpSend($event, $type, $data)
|
||||
|
||||
Same as say() but for sending CTCP messages.
|
||||
|
||||
Examples:
|
||||
$self->ctcpSend($event, 'PING', time());
|
||||
|
||||
|
||||
*** ctcpReply($event, $type, $data)
|
||||
|
||||
Same as ctcpSend() but for sending CTCP replies.
|
||||
|
||||
Examples:
|
||||
$self->ctcpReply($event, 'VERSION', "Version $major.$minor");
|
||||
|
||||
|
||||
*** isAdmin($event)
|
||||
|
||||
Returns true if the cause of the event was an authenticated administrator.
|
||||
|
||||
Example:
|
||||
if ($self->isAdmin($event)) { ... }
|
||||
|
||||
|
||||
*** setAway($event, $message)
|
||||
|
||||
Set the bot's 'away' flag. A blank message will mark the bot as
|
||||
back. Note: If you need this you are doing something wrong!!!
|
||||
Remember that you should not be doing any lengthy processes since if
|
||||
you are away for any length of time, the bot will be disconnected!
|
||||
|
||||
Also note that in 2.0 this is not throttled, so DO NOT call this
|
||||
repeatedly, or put yourself in any position where you allow IRC
|
||||
users to cause your module to call this. Otherwise, you open
|
||||
yourself to denial of service attacks.
|
||||
|
||||
Finally, note that calling 'do', 'emote', 'say', and all the
|
||||
related functions will also reset the 'away' flag.
|
||||
|
||||
Example:
|
||||
$self->setAway($event, 'brb...');
|
||||
|
||||
|
||||
*** setNick($event, $nick)
|
||||
|
||||
Set the bot's nick. This handles all the changing of the internal
|
||||
state variables and saving the configuration and everything.
|
||||
It will also add the nick to the list of nicks to try when
|
||||
the bot finds its nick is already in use.
|
||||
|
||||
Note that in 2.0 this is not throttled, so DO NOT call this
|
||||
repeatedly, or put yourself in any position where you allow IRC
|
||||
users to cause your module to call this. Otherwise, you open
|
||||
yourself to denial of service attacks.
|
||||
|
||||
Example:
|
||||
$self->setNick($event, 'zippy');
|
||||
|
||||
|
||||
*** mode($event, $channel, $mode, $argument)
|
||||
|
||||
Changes a mode of channel $channel.
|
||||
|
||||
Example:
|
||||
$self->mode($event, $event->{'channel'}, '+o', 'Hixie');
|
||||
|
||||
|
||||
*** invite($event, $who, $channel)
|
||||
|
||||
Invite $who to channel $channel. This can be used for intrabot
|
||||
control, or to get people into a +i channel, for instance.
|
||||
|
||||
Example:
|
||||
$self->invite($event, 'Hixie', '#privateChannel');
|
||||
|
||||
|
||||
*** prettyPrint($preferredLineLength, $prefix, $indent, $divider, @input)
|
||||
|
||||
Takes @input, and resorts it so that the lines are of roughly the same
|
||||
length, aiming optimally at $preferredLineLength, prefixing each line
|
||||
with $indent, placing $divider between each item in @input if they
|
||||
appear on the same line, and sticking $prefix at the start of it all on
|
||||
the first line. The $prefix may be undef.
|
||||
|
||||
Returns the result of all that.
|
||||
|
||||
This is what the 'help' command uses to pretty print its output.
|
||||
|
||||
Example:
|
||||
my @result = $self->prettyPrint($linelength, undef, 'Info: ', ' -- ', @infoItems);
|
||||
|
||||
|
||||
*** wordWrap($preferredLineLength, $prefix, $indent, $divider, @input)
|
||||
|
||||
Takes @input, and places each item sequentially on lines, aiming optimally
|
||||
at $preferredLineLength, prefixing each line with $indent, placing $divider
|
||||
between each item in @input if they appear on the same line, and sticking
|
||||
$prefix at the start of it all on the first line, without ever cutting
|
||||
items across lines. The $prefix may be undef.
|
||||
|
||||
Returns the result of all that.
|
||||
|
||||
Example:
|
||||
my @result = $self->wordWrap($linelength, undef, 'Info: ', ' ', split(/\s+/os, @lines);
|
||||
|
||||
|
||||
*** days($time)
|
||||
|
||||
Returns a string describing the length of time between $time and now.
|
||||
|
||||
Example:
|
||||
$self->debug('uptime: '.$self->days($^T));
|
||||
|
||||
|
||||
*** sanitizeRegexp($regexp)
|
||||
|
||||
Checks to see if $regexp is a valid regular expression. If it is, returns
|
||||
the argument unchanged. Otherwise, returns quotemeta($regexp), which should
|
||||
be safe to use in regular expressions as a plain text search string.
|
||||
|
||||
Do not add prefixes or suffixes to the pattern after sanitizing it.
|
||||
|
||||
Example:
|
||||
$pattern = $self->sanitizeRegexp($pattern);
|
||||
$data =~ /$pattern//gosi;
|
||||
|
||||
|
||||
-- end --
|
||||
@@ -1,368 +0,0 @@
|
||||
_ _
|
||||
m o z i l l a |.| o r g | |
|
||||
_ __ ___ ___ ___| |__ ___ | |_
|
||||
| '_ ` _ \ / _ \_ / '_ \ / _ \| __|
|
||||
| | | | | | (_) / /| |_) | (_) | |_
|
||||
|_| |_| |_|\___/___|_.__/ \___/ \__|
|
||||
=======================- 2 . 2 -==
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
You will need the following programs and libraries to run mozbot2:
|
||||
|
||||
perl
|
||||
wget
|
||||
Net::IRC
|
||||
Net::SMTP
|
||||
IO::Select
|
||||
IO::Pipe
|
||||
|
||||
These packages may have additional requirements of their own.
|
||||
|
||||
In order to do anything useful with mozbot2, you will need some Bot
|
||||
Modules. Several are included in this distribution, and they may have
|
||||
requirements above and beyond those given above.
|
||||
|
||||
Once you have set up all the packages on which mozbot2 depends, make
|
||||
mozbot.pl executable:
|
||||
|
||||
chmod +x mozbot.pl
|
||||
|
||||
This is needed since mozbot2 will occasionally attempt to restart
|
||||
itself (e.g. if its source code is changed).
|
||||
|
||||
Then, simply run mozbot.pl:
|
||||
|
||||
./mozbot.pl
|
||||
|
||||
Currently, you MUST run mozbot from the directory in which mozbot.pl
|
||||
is placed. This may be changed in a future version.
|
||||
|
||||
|
||||
SECURITY
|
||||
--------
|
||||
|
||||
Since mozbot interacts with the outside world, do not run it as a
|
||||
privileged user!!!
|
||||
|
||||
In addition, since mozbot calls external programs (currently perl and
|
||||
wget, possibly others in future versions) make sure that none of the
|
||||
directories on your path are writable by untrusted users! (e.g., do
|
||||
not put /tmp into your path!)
|
||||
|
||||
Make sure that '.' is not in your path! This is a security risk in a
|
||||
situation like this, and perl will rightly refuse to execute external
|
||||
programs (like wget, used to get remote URIs for many functions) if
|
||||
'.' is on your path.
|
||||
|
||||
Do not run the bot straight into a public channel on the first run!
|
||||
|
||||
One important reason not to load the bot straight into a public
|
||||
channel on the first run is that until it has been properly
|
||||
configured, it will have a well defined username and password to
|
||||
access all its admin functions. Thus a malicious user could hijack the
|
||||
bot the moment it joined the channel.
|
||||
|
||||
If this is a serious problem for you (e.g., your users are of a
|
||||
particularly high calibre and are doing regular polls of the /who
|
||||
command to see if any bots join) then use another server, such as one
|
||||
that you control, on localhost!
|
||||
|
||||
See the "Administration" section for instructions on how to change the
|
||||
administration password (important!).
|
||||
|
||||
Note: Passwords are printed in clear text on the console and in the
|
||||
log files. Secure them accordingly. Of course, IRC is an inherently
|
||||
insecure protocol anyway, and any machine between your IRC client's
|
||||
and your bot's, going through the IRC network's servers, will have
|
||||
access to the passwords. For this reason, change them often, and don't
|
||||
use passwords that you use for important things here.
|
||||
|
||||
The default setting is for mozbot to run with taint checking
|
||||
enabled. I *STRONGLY* recommend not changing this.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
When you start up mozbot for the first time, it will prompt you for
|
||||
the following information:
|
||||
|
||||
1. IRC server.
|
||||
What machine you want the bot to connect to. At the moment,
|
||||
mozbot only supports connecting to a single server at a time. It
|
||||
would require a *significant* amount of work to change this.
|
||||
|
||||
2. Port.
|
||||
What port to connect to on the IRC server. Typically, this will
|
||||
be 6667 or therabouts.
|
||||
|
||||
3. Channels.
|
||||
|
||||
What channels the bot should initially connect to. It is
|
||||
recommended that this just be a bot channel or a test channel,
|
||||
for example #mozbot, since running a bot for the first time
|
||||
before it is known to be ready is a bad idea. You can enter more
|
||||
than one channel, just hit enter after each one (leave a blank
|
||||
line when you have finished). (To make oopsbot join a keyed
|
||||
channel, you must first add the channel's key to the
|
||||
'channelKeys' variable. To do this, the bot will have to be on
|
||||
IRC first, so don't worry about it for now.)
|
||||
|
||||
4. Your e-mail address.
|
||||
In case of great difficulties, mozbot may try to e-mail you. If
|
||||
this happens, it will use the e-mail address you gave here. This
|
||||
only happens if (a) it absolutely cannot connect to the server
|
||||
you gave it, or (b) it cannot find a nick that is not in use.
|
||||
|
||||
5. SMTP server.
|
||||
The name of the SMTP server it should try to talk with in order
|
||||
to send you mail. If you type in an invalid server name, it will
|
||||
just fail to send mail and instead will complain bitterly to its
|
||||
console.
|
||||
|
||||
6. Nicks.
|
||||
Some nicks for IRC. For example, 'mozbot'. It is customary to
|
||||
clearly mark the bot as being non-human, for example by putting
|
||||
'bot' in the name. You should enter several possibilities
|
||||
here. Hit enter after each one. Leave a blank line to finish.
|
||||
|
||||
Once the bot is running, there are many other things that can be
|
||||
configured with it. See "variables".
|
||||
|
||||
Note. The bot will treat all channel names as lowercase to avoid case
|
||||
sensitivity issues.
|
||||
|
||||
|
||||
LOGGING
|
||||
-------
|
||||
|
||||
Normally, mozbot will output its complaints to the console
|
||||
(stdout). If you run mozbot in an xterm or screen session, you can
|
||||
therefore easily keep track of what is going on.
|
||||
|
||||
It will also continuously log output to ~/logs/$0.$$.log, where $0 is
|
||||
the file name and $$ is the PID. You may wish to set up a cron job to
|
||||
prune this file on a regular basis, it gets LARGE. However, it can
|
||||
sometimes be the only way to track down how your system was
|
||||
compromised if it turns out that mozbot has a security flaw.
|
||||
|
||||
Control over the logging is currently not available. This may change
|
||||
in future versions.
|
||||
|
||||
Note that when the bot forks and then outputs a message, which happens
|
||||
occasionally, it will therefore use a new log file for the forked
|
||||
process. This should only happen when something bad happens,
|
||||
e.g. something forces the bot to restart or the bot forks and then the
|
||||
child enters a bad state.
|
||||
|
||||
Note. Authentication passwords will be displayed in cleartext on the
|
||||
console and in the log files.
|
||||
|
||||
|
||||
ADMINISTRATION
|
||||
--------------
|
||||
|
||||
Once the bot is active and on the IRC server, it starts to listen to
|
||||
all messages seen on any channels on which it is present, and all
|
||||
messages sent to it using /msg.
|
||||
|
||||
Your first task should be to change the admin password. To do this,
|
||||
authenticate yourself using the "auth" command. The default username
|
||||
is "admin", and the default password is "password". If the bot is
|
||||
called "mozbot", then the command to authenticate would be as follows:
|
||||
|
||||
/msg mozbot auth admin password
|
||||
|
||||
The bot should respond with "Hi admin!".
|
||||
|
||||
Now create yourself an account by adding a username/password pair to
|
||||
the bot. You do this with the "newuser" command. Next, you should
|
||||
bless this new user, making it a bot administrator. This is done using
|
||||
these commands:
|
||||
|
||||
/msg mozbot newuser <username> <password> <password>
|
||||
/msg mozbot bless <username>
|
||||
|
||||
Now authenticate yourself again, as the new user:
|
||||
|
||||
/msg mozbot auth <username> <password>
|
||||
|
||||
The moment you authenticate as the new admin, the default admin
|
||||
account is deleted.
|
||||
|
||||
You are now in a position to add the modules you want and to put the
|
||||
bot in the channels you want it in.
|
||||
|
||||
To load modules is easy.
|
||||
|
||||
/msg mozbot load module
|
||||
|
||||
...where "module" is a module name, such as "HelloWorld" (note that
|
||||
the ".bm" extension is not included).
|
||||
|
||||
By default, modules will be enabled in all channels. See the
|
||||
"variables" section below to change this.
|
||||
|
||||
|
||||
HINTS
|
||||
-----
|
||||
|
||||
If the bot goes mad and starts flooding a channel -- e.g., if someone
|
||||
keeps asking it for information -- then authenticate and then send it
|
||||
the following message:
|
||||
|
||||
/msg mozbot shutup please
|
||||
|
||||
It should respond within a few seconds. You can authenticate while it
|
||||
is speaking, that's not a problem.
|
||||
|
||||
|
||||
VARIABLES
|
||||
---------
|
||||
|
||||
For information on changing variables on the fly, use the "vars"
|
||||
command:
|
||||
|
||||
/msg mozbot vars
|
||||
|
||||
Each module has several variables that you can change. You can see
|
||||
what they are by typing:
|
||||
|
||||
/msg mozbot vars module
|
||||
|
||||
...where module is the module in question. These always include
|
||||
"Admin" and "General". Admin is only available to authenticated users,
|
||||
and provides the commands such as "shutdown", "cycle", "leave",
|
||||
"password", and so on. "General" provides the "help" command to
|
||||
everyone.
|
||||
|
||||
The main variables are:
|
||||
|
||||
channels -- which channels the module should listen in, and which
|
||||
channels the module should send announcements to. Must be in
|
||||
lowercase!
|
||||
|
||||
channelKeys -- a mapping of (lowercase) channel names to keys. It
|
||||
is assumed that any channel without an entry in this variable has
|
||||
no key.
|
||||
|
||||
autojoin -- whether (1) or not (0) the module should automatically
|
||||
add a new channel to its "channels" list when the bot joins a new
|
||||
channel. If this is not enabled, then you will have to add new
|
||||
channels to the "channels" list each time.
|
||||
|
||||
channelsBlocked -- channels that will not be autojoined, so if the
|
||||
module has been disabled, it won't rejoin the channel if it is
|
||||
kicked then reinvited.
|
||||
|
||||
denyusers -- user@host regexp masks of users that should be
|
||||
completely ignored. The regexp will be placed between "^" and "$"
|
||||
modifiers, so do not include them, and *do* include everything
|
||||
required to make the whole user@host mask match.
|
||||
|
||||
allowusers -- identical in usage to denyusers, but checked first to
|
||||
override it. So to give access to everyone but a few people, leave
|
||||
allowusers blank and add some masks to denyusers, but to give
|
||||
access to only a few people, add their user@host masks to
|
||||
allowusers, and add ".*" to denyusers.
|
||||
|
||||
In addition, other modules may have extra variables.
|
||||
|
||||
The admin variable has quite a few variables, including all those that
|
||||
are prompted for during initial startup. The interesting ones are:
|
||||
|
||||
currentnick -- the nick. This can be changed on the fly.
|
||||
|
||||
server, port -- the server and port to connect to. If you change
|
||||
these and then cycle the bot (/msg mozbot cycle) then the bot will
|
||||
change servers without shutting down.
|
||||
|
||||
channels -- unlike other modules, the channels variable for the
|
||||
Admin module actually controls which channels the bot itself
|
||||
appears in. The preferred method for controlling this is using
|
||||
/invite and /kick or "go" and "leave", though (since editing the
|
||||
list directly will probably require a cycle of the bot to take
|
||||
effect).
|
||||
|
||||
admins -- the administrators. See "Administration" above.
|
||||
|
||||
allowInviting -- this controls whether the /invite IRC command will
|
||||
be obeyed or not.
|
||||
|
||||
allowChannelAdmin -- this controls whether or not the bot will
|
||||
accept admin commands that are given in a channel or not. In any
|
||||
case, the "auth" command is never accepted in a channel.
|
||||
|
||||
files -- this is a list of files whose timestamps are monitored to
|
||||
decide if the source code has changed. If it is established that
|
||||
any of these files have changed while the bot is running, then the
|
||||
bot will shutdown and restart itself. Modules are dealt with
|
||||
separately, and need not be listed here. (And when modules change,
|
||||
the whole bot is not restarted, only the module.)
|
||||
|
||||
sourceCodeCheckDelay -- number of seconds between checks of the bot
|
||||
and module sources. Note that changes will only take effect after
|
||||
the previous timer has passed, so changing it from 3600 (an hour)
|
||||
to 10 (10 seconds) may not be of much immediate use. In these
|
||||
cases, setting the variable to the new value then cycling the bot
|
||||
is a good plan.
|
||||
|
||||
Changes to variables are usually immediately recorded in the
|
||||
configuration file and will be saved for the next time the bot is
|
||||
loaded.
|
||||
|
||||
There are three types of editable variables: scalars, arrays of
|
||||
scalars, and hashes of scalars.
|
||||
|
||||
Scalars are easy, and lists are explained by the bot quite well, just
|
||||
try to set a list and it will tell you if you are doing something
|
||||
wrong!
|
||||
|
||||
To add a value to a hash, there is a more complex syntax. For example,
|
||||
to add a new site to the list of sites that the RDF module monitors,
|
||||
use the following command:
|
||||
|
||||
/msg mozbot vars RDF sites '+|slashdot|http://slashdot.org/slashdot.rdf'
|
||||
|
||||
First, note that the value is surrounded by quotes. You can nest
|
||||
quotes without any problems, the quotes are just needed to
|
||||
differentiate significant trailing whitespace from mistakes.
|
||||
|
||||
The "+" means you want to add a value to the hash (as you'll see in a
|
||||
minute, to remove an item you use "-"). Then, since a hash is a
|
||||
key/value pair, you have to delimit the two. In this case, we have
|
||||
used "|" as a delimiter. However, you could use anything. The first
|
||||
occurance tells mozbot what delimiter you have picked. The second
|
||||
separates the key (in this case the site nickname) from the value (in
|
||||
this case the URI). For example:
|
||||
|
||||
/msg mozbot vars RDF sites '+*key*value'
|
||||
|
||||
You could even use a letter as a delimiter, but since that is usually
|
||||
a sign that you have forgotten to declare which delimiter you are
|
||||
using, mozbot will warn you about this. For example (the 'users' hash,
|
||||
BTW, is the hash in which all the username/password pairs are kept):
|
||||
|
||||
/msg mozbot vars Admin users '+sarah|lisa'
|
||||
|
||||
...will be treated the same as:
|
||||
|
||||
/msg mozbot vars Admin users '+*arah|li*a'
|
||||
|
||||
..., I.e. the username added would be "arah|li" and the password would
|
||||
be "a". This is not a bug, it's a feature. It means you can include
|
||||
any character, including "'", "|", and so on, in the key, without fear
|
||||
of it being interpreted as a delimiter.
|
||||
|
||||
To remove a user, or any key/value pair in a hash, you use this
|
||||
syntax:
|
||||
|
||||
/msg mozbot vars Admin users '-admin'
|
||||
|
||||
That's it. No need to say what the value is, since each key in a hash
|
||||
has to be unique.
|
||||
|
||||
-- end --
|
||||
@@ -1,514 +0,0 @@
|
||||
_ _
|
||||
m o z i l l a |.| o r g | |
|
||||
_ __ ___ ___ ___| |__ ___ | |_
|
||||
| '_ ` _ \ / _ \_ / '_ \ / _ \| __|
|
||||
| | | | | | (_) / /| |_) | (_) | |_
|
||||
|_| |_| |_|\___/___|_.__/ \___/ \__|
|
||||
=======================- 2 . 2 -==
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
This was written as a living document. I (the author of mozbot 2.0)
|
||||
tried (successfully!) to set up mozbot in a secure environment,
|
||||
chrooted and setuided. This requires much more than a usual
|
||||
installation. So, without further ado, over to myself in the field:
|
||||
|
||||
|
||||
GETTING STARTED
|
||||
---------------
|
||||
|
||||
I will first be trying to install mozbot 2.0 on a SPARC machine
|
||||
running Sun Solaris. These instructions will probably work for any
|
||||
sane UNIX system. If you use Windows, see the INSTALL.WIN32 file.
|
||||
|
||||
<ianh:~> mkdir mozbot
|
||||
<ianh:~> cd mozbot
|
||||
<ianh:~/mozbot> version
|
||||
Machine hardware: sun4u
|
||||
OS version: 5.7
|
||||
Processor type: sparc
|
||||
Hardware: SUNW,Ultra-60
|
||||
|
||||
I already had Emacs 20.7 installed on the machine, for which I must
|
||||
thank Pavlov. You may, of course, use any editor of your choosing when
|
||||
doing this, although if you use vi or one of its siblings then don't
|
||||
even _think_ about asking me for help. (If you can understand vi I
|
||||
figure mozbot should no problem.)
|
||||
|
||||
<ianh:~> mkdir mozbot
|
||||
<ianh:~> cd mozbot
|
||||
|
||||
I also had several gigabytes of free disk space. You'll probably need
|
||||
several hundred megabytes to do all of this (including scratch space).
|
||||
(I believe the end result was around 30 megs for everything in the
|
||||
chroot jail directory.)
|
||||
|
||||
|
||||
PERL
|
||||
----
|
||||
|
||||
The first thing on my list was to install Perl.
|
||||
|
||||
<ianh:~/mozbot> mkdir resources
|
||||
<ianh:~/mozbot> cd resources
|
||||
<ianh:~/mozbot/resources> wget http://www.perl.com/CPAN/src/stable.tar.gz
|
||||
<ianh:~/mozbot/resources> tar xvfz stable.tar.gz
|
||||
|
||||
Next I read the README and INSTALL files:
|
||||
|
||||
<ianh:~/mozbot/resources> cd perl-5.6.0/
|
||||
<ianh:~/mozbot/resources/perl-5.6.0> emacs-20.7 README INSTALL
|
||||
|
||||
This told me how to do the next few bits.
|
||||
|
||||
<ianh:~/mozbot/resources/perl-5.6.0> rm -f config.sh Policy.sh
|
||||
<ianh:~/mozbot/resources/perl-5.6.0> sh Configure -Dprefix=/u/ianh/mozbot
|
||||
|
||||
By providing a prefix, the default installation directory for a lot of
|
||||
modules I am about to install is automatically set up correctly. So if
|
||||
you don't install Perl yourself, remember to take this into account!
|
||||
|
||||
Note: I didn't change any of the build options, so threads, debugging
|
||||
and the like are all disabled (or at their defaults). The only things
|
||||
I changed were that I answered 'n' to the question 'Binary
|
||||
compatibility with Perl 5.005?', which defaulted to 'y', and I told it
|
||||
not to install into '/usr/bin/perl'.
|
||||
|
||||
<ianh:~/mozbot/resources/perl-5.6.0> make
|
||||
<ianh:~/mozbot/resources/perl-5.6.0> make test
|
||||
<ianh:~/mozbot/resources/perl-5.6.0> make install
|
||||
<ianh:~/mozbot/resources/perl-5.6.0> cd ..
|
||||
|
||||
At this point I had Perl installed correctly in my mozbot directory.
|
||||
|
||||
|
||||
WGET
|
||||
----
|
||||
|
||||
The next thing to install was wget.
|
||||
|
||||
<ianh:~/mozbot/resources> wget ftp://ftp.gnu.org/pub/gnu/wget/wget-1.6.tar.gz
|
||||
<ianh:~/mozbot/resources> tar xvfz wget-1.6.tar.gz
|
||||
<ianh:~/mozbot/resources> cd wget-1.6
|
||||
<ianh:~/mozbot/resources/wget-1.6> emacs-20.7 README INSTALL
|
||||
<ianh:~/mozbot/resources/wget-1.6> ./configure --prefix=/u/ianh/mozbot
|
||||
<ianh:~/mozbot/resources/wget-1.6> make
|
||||
<ianh:~/mozbot/resources/wget-1.6> make install
|
||||
<ianh:~/mozbot/resources/wget-1.6> cd ..
|
||||
|
||||
No problems, no difficulties.
|
||||
|
||||
|
||||
MOZBOT
|
||||
------
|
||||
|
||||
Now, before going on any further with installing the required modules,
|
||||
I needed to find what those were. Ergo, the next thing to install was
|
||||
mozbot. Presumably you already have the relevant files, or know where
|
||||
to get them, since you are reading a file that comes with the source.
|
||||
|
||||
<ianh:~/mozbot/resources> wget http://www.damowmow.com/mozilla/mozbot/mozbot.tar.gz
|
||||
|
||||
There is no configuration, makefile or install script for mozbot,
|
||||
since there is nothing to compile or particularly install. So, I just
|
||||
extracted the mozbot tarball directly inside what would be the root of
|
||||
the file system when I eventually chroot()ed.
|
||||
|
||||
<ianh:~/mozbot/resources> cd ../..
|
||||
<ianh:~> tar xvfz mozbot/resources/mozbot.tar.gz
|
||||
|
||||
Like all shell scripts, one thing to change about it is the location
|
||||
of the Perl executable in the shebang.
|
||||
|
||||
<ianh:~> cd mozbot
|
||||
<ianh:~/mozbot> emacs-20.7 mozbot.pl
|
||||
|
||||
Since I'll be running it from the version of Perl I just installed, I
|
||||
changed the first line to read:
|
||||
|
||||
#!./bin/perl -wT
|
||||
|
||||
Note that this requires me to run mozbot from the mozbot directory. If
|
||||
you've read the README file, you'll know that this is a prerequisite
|
||||
of running mozbot anyway.
|
||||
|
||||
|
||||
Net::IRC
|
||||
--------
|
||||
|
||||
If you tried running mozbot now, you'd find it was missing
|
||||
Net::IRC. So, guess what I installed next? ;-)
|
||||
|
||||
<ianh:~/mozbot> cd resources
|
||||
<ianh:~/mozbot/resources> wget http://www.cpan.org/authors/id/FIMM/Net-IRC-0.70.tar.gz
|
||||
<ianh:~/mozbot/resources> tar xvfz Net-IRC-0.70.tar.gz
|
||||
<ianh:~/mozbot/resources> cd Net-IRC-0.70
|
||||
<ianh:~/mozbot/resources/Net-IRC-0.70> emacs-20.7 README
|
||||
<ianh:~/mozbot/resources/Net-IRC-0.70> ../../bin/perl Makefile.PL
|
||||
<ianh:~/mozbot/resources/Net-IRC-0.70> make
|
||||
<ianh:~/mozbot/resources/Net-IRC-0.70> make install
|
||||
<ianh:~/mozbot/resources/Net-IRC-0.70> cd ..
|
||||
|
||||
It is important to use the Perl we just installed and not any other
|
||||
Perl on the system, otherwise you'll get incorrect prefixes and
|
||||
stuff. (I didn't bother to use the wget I just installed...)
|
||||
|
||||
|
||||
Net::SMTP
|
||||
---------
|
||||
|
||||
Yup, you guessed it, Net::SMTP is next.
|
||||
|
||||
<ianh:~/mozbot/resources> wget http://www.cpan.org/authors/id/GBARR/libnet-1.0703.tar.gz
|
||||
<ianh:~/mozbot/resources> tar xvfz libnet-1.0703.tar.gz
|
||||
<ianh:~/mozbot/resources> cd libnet-1.0703
|
||||
<ianh:~/mozbot/resources/libnet-1.0703> emacs-20.7 README
|
||||
<ianh:~/mozbot/resources/libnet-1.0703> ../../bin/perl Makefile.PL
|
||||
|
||||
I answered 'y' to the question 'Do you want to modify/update your
|
||||
configuration (y|n) ? [no]', which was asked because the system
|
||||
had already had libnet installed once.
|
||||
|
||||
I kept the defaults for all the options though.
|
||||
|
||||
<ianh:~/mozbot/resources/libnet-1.0703> make
|
||||
<ianh:~/mozbot/resources/libnet-1.0703> make test
|
||||
<ianh:~/mozbot/resources/libnet-1.0703> make install
|
||||
<ianh:~/mozbot/resources/libnet-1.0703> cd ..
|
||||
|
||||
This also installed Net::FTP, which is required by some of the modules
|
||||
(in particular, the FTP module!).
|
||||
|
||||
|
||||
INITIAL CONFIGURATION
|
||||
---------------------
|
||||
|
||||
Now I needed to set up the environment for mozbot. The only real thing
|
||||
that needs setting up is the PATH variable. So:
|
||||
|
||||
<ianh:~/mozbot/resources> cd ..
|
||||
<ianh:~/mozbot> emacs-20.7 run-mozbot-chrooted
|
||||
|
||||
Here are the contents of my run-mozbot-chrooted script:
|
||||
|
||||
export PATH=/u/ianh/mozbot/bin
|
||||
./mozbot.pl
|
||||
|
||||
It is absolutely imperative that the path not contain '::' or '.'
|
||||
anywhere, as this will be treated as the current directory, which will
|
||||
then result in perl exiting with taint errors.
|
||||
|
||||
Now we make it executable:
|
||||
|
||||
<ianh:~/mozbot> chmod +x run-mozbot-chrooted
|
||||
|
||||
(Note. a sample run-mozbot-chrooted script is shipped with mozbot --
|
||||
it still requires you to follow all these steps though.)
|
||||
|
||||
|
||||
INITIAL RUN
|
||||
-----------
|
||||
|
||||
At this point, mozbot is runnable... so I ran it!
|
||||
|
||||
<ianh:~/mozbot> ./run-mozbot-chrooted
|
||||
|
||||
Note that I'm running it via my script and not directly. If you were
|
||||
not intending to run mozbot in a chroot() jail environment, then
|
||||
'./mozbot.pl' would be sufficient.
|
||||
|
||||
It prompted me for various things, like servers and so on. Then it
|
||||
connected without problems but with no modules set up, as I expected.
|
||||
|
||||
On IRC, I configured mozbot as I wanted it:
|
||||
|
||||
/query mozbot
|
||||
mozbot auth admin password
|
||||
newuser Hixie newpass newpass
|
||||
bless Hixie
|
||||
auth Hixie newpass
|
||||
|
||||
I also played a bit with the configuration variables:
|
||||
|
||||
vars Admin throttleTime '2.2'
|
||||
|
||||
This was all very well, but no modules makes mozbot a boring bot, so
|
||||
the next thing was...
|
||||
|
||||
|
||||
FILTERS
|
||||
-------
|
||||
|
||||
I shut down mozbot ('shutdown please') and installed the filters
|
||||
required by the 'Filters' BotModule.
|
||||
|
||||
<ianh:~/mozbot> cd resources
|
||||
<ianh:~/mozbot/resources> wget ftp://ftp.debian.org/pub/mirrors/debian/dists/potato/main/source/games/filters_2.9.tar.gz
|
||||
<ianh:~/mozbot/resources> tar xvfz filters_2.9.tar.gz
|
||||
<ianh:~/mozbot/resources> cd filters
|
||||
<ianh:~/mozbot/resources/filters> emacs-20.7 README
|
||||
<ianh:~/mozbot/resources/filters> make
|
||||
|
||||
At this point, I edited the Makefile to change /usr/.../ so as to
|
||||
point in the places we used for installing Perl.
|
||||
|
||||
<ianh:~/mozbot/resources/filters> make install PREFIX=/u/ianh/mozbot
|
||||
<ianh:~/mozbot/resources/filters> cd ..
|
||||
|
||||
I should point out that this didn't go too well and I had to hack
|
||||
about with the Makefile and my environment and so on, so good luck
|
||||
(admittedly, Pavlov happened to install a new compiler at the same
|
||||
time, and didn't bother to install a license for it, so I had a few
|
||||
more problems than you should, but...).
|
||||
|
||||
You should also make sure that the shebang lines in the five relevant
|
||||
perl scripts that you should make sure ended up in ~/mozbot/bin
|
||||
actually point to the right perl executable. I had to edit the files
|
||||
by hand.
|
||||
|
||||
|
||||
Net::Telnet
|
||||
-----------
|
||||
|
||||
In order to insult people, the Rude module needs to Telnet:
|
||||
|
||||
<ianh:~/mozbot/resources> wget http://www.cpan.org/authors/id/JROGERS/Net-Telnet-3.02.tar.gz
|
||||
<ianh:~/mozbot/resources> tar xvfz Net-Telnet-3.02.tar.gz
|
||||
<ianh:~/mozbot/resources> cd Net-Telnet-3.02
|
||||
<ianh:~/mozbot/resources/Net-Telnet-3.02> emacs-20.7 README
|
||||
<ianh:~/mozbot/resources/Net-Telnet-3.02> ../../bin/perl Makefile.PL
|
||||
<ianh:~/mozbot/resources/Net-Telnet-3.02> make
|
||||
<ianh:~/mozbot/resources/Net-Telnet-3.02> make test
|
||||
<ianh:~/mozbot/resources/Net-Telnet-3.02> make install
|
||||
<ianh:~/mozbot/resources/Net-Telnet-3.02> cd ..
|
||||
|
||||
That went a lot smoother than the filters installation, let me tell
|
||||
you! ;-)
|
||||
|
||||
|
||||
WWW::Babelfish
|
||||
--------------
|
||||
|
||||
The translation module requires a whole bunch of other modules, mainly
|
||||
due to its dependency on WWW::Babelfish, which requires half of libwww
|
||||
and also IO::String. libwww itself requires another half a dozen
|
||||
modules, namely URI, MIME-Base64, HTML::Parser, libnet (which I
|
||||
installed earlier, thankfully), and Digest::MD5. And HTML-Parser
|
||||
requires HTML-Tagset!
|
||||
|
||||
I found these dependencies out by browsing CPAN reading README files.
|
||||
|
||||
<ianh:~/mozbot/resources> lynx http://www.cpan.org/
|
||||
|
||||
Thankfully, they all installed rather smoothly. Here is the complete
|
||||
list of commands I used to install WWW::Babelfish (starting in the
|
||||
'resources' directory):
|
||||
|
||||
wget http://www.cpan.org/authors/id/GAAS/MIME-Base64-2.12.tar.gz
|
||||
tar xvfz MIME-Base64-2.12.tar.gz
|
||||
cd MIME-Base64-2.12
|
||||
../../bin/perl Makefile.PL
|
||||
make
|
||||
make test
|
||||
make install
|
||||
cd ..
|
||||
|
||||
wget http://www.cpan.org/authors/id/GAAS/URI-1.11.tar.gz
|
||||
tar xvfz URI-1.11.tar.gz
|
||||
cd URI-1.11
|
||||
../../bin/perl Makefile.PL
|
||||
make
|
||||
make test
|
||||
make install
|
||||
cd ..
|
||||
|
||||
wget http://www.cpan.org/authors/id/S/SB/SBURKE/HTML-Tagset-3.03.tar.gz
|
||||
tar xvfz HTML-Tagset-3.03.tar.gz
|
||||
cd HTML-Tagset-3.03
|
||||
../../bin/perl Makefile.PL
|
||||
make
|
||||
make test
|
||||
make install
|
||||
cd ..
|
||||
|
||||
wget http://www.cpan.org/authors/id/GAAS/HTML-Parser-3.19_91.tar.gz
|
||||
tar xvfz HTML-Parser-3.19_91.tar.gz
|
||||
cd HTML-Parser-3.1991
|
||||
../../bin/perl Makefile.PL
|
||||
make
|
||||
make test
|
||||
make install
|
||||
cd ..
|
||||
|
||||
wget http://www.cpan.org/authors/id/GAAS/Digest-MD5-2.13.tar.gz
|
||||
tar xvfz Digest-MD5-2.13.tar.gz
|
||||
cd Digest-MD5-2.13
|
||||
../../bin/perl Makefile.PL
|
||||
make
|
||||
make test
|
||||
make install
|
||||
cd ..
|
||||
|
||||
wget http://www.cpan.org/authors/id/GAAS/libwww-perl-5.51.tar.gz
|
||||
tar xvfz libwww-perl-5.51.tar.gz
|
||||
cd libwww-perl-5.51
|
||||
../../bin/perl Makefile.PL
|
||||
make
|
||||
make test
|
||||
make install
|
||||
cd ..
|
||||
|
||||
wget http://www.cpan.org/authors/id/GAAS/IO-String-1.01.tar.gz
|
||||
tar xvfz IO-String-1.01.tar.gz
|
||||
cd IO-String-1.01
|
||||
../../bin/perl Makefile.PL
|
||||
make
|
||||
make test
|
||||
make install
|
||||
cd ..
|
||||
|
||||
wget http://www.cpan.org/authors/id/D/DU/DURIST/WWW-Babelfish-0.09.tar.gz
|
||||
tar xvfz WWW-Babelfish-0.09.tar.gz
|
||||
cd WWW-Babelfish-0.09/
|
||||
../../bin/perl Makefile.PL
|
||||
make
|
||||
make test
|
||||
make install
|
||||
cd ..
|
||||
|
||||
Yes, this is surreal. I always knew languages were hard.
|
||||
|
||||
|
||||
UUIDGEN
|
||||
-------
|
||||
|
||||
The last module, the UUID generator, requires a program that you'll
|
||||
find along with mozbot in CVS. You may have this already. If you
|
||||
don't, then here's how I got my copy:
|
||||
|
||||
<ianh:~/mozbot/resources> export CVSROOT=:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot
|
||||
<ianh:~/mozbot/resources> cvs login
|
||||
|
||||
The password is 'anonymous'.
|
||||
|
||||
<ianh:~/mozbot/resources> cvs checkout mozilla/webtools/mozbot/uuidgen
|
||||
<ianh:~/mozbot/resources> cd mozilla/webtools/mozbot/uuidgen/
|
||||
<ianh:~/mozbot/resources/mozilla/webtools/mozbot/uuidgen> make
|
||||
<ianh:~/mozbot/resources/mozilla/webtools/mozbot/uuidgen> cp uuidgen ../../../../../bin
|
||||
<ianh:~/mozbot/resources/mozilla/webtools/mozbot/uuidgen> cd ../../../../../
|
||||
|
||||
At this point I think I had all the required programs.
|
||||
|
||||
|
||||
MORE THOROUGH CONFIGURATION
|
||||
---------------------------
|
||||
|
||||
Now that I'm ready to run mozbot chroot()ed, it is time to make the
|
||||
final preparations. Firts, I moved the resources directory out of the
|
||||
way, since I had finished with it:
|
||||
|
||||
<ianh:~/mozbot> mv resources ../installed-resources
|
||||
|
||||
Next I made sure all the rights were set to read-only for people other
|
||||
than the user:
|
||||
|
||||
<ianh:~/mozbot> chmod -R go-w .
|
||||
|
||||
At this point I wanted to make sure the bot started ok, so I ran the
|
||||
run-mozbot-chrooted script:
|
||||
|
||||
<ianh:~/mozbot> ./run-mozbot-chrooted
|
||||
|
||||
That worked. I changed the script to:
|
||||
|
||||
export PATH=/bin
|
||||
./mozbot.pl --chroot /config/default
|
||||
|
||||
What's this 'config' thing? Well, since we're about to chown() all the
|
||||
files to root and then setuid the script to nobody, the bot wouldn't
|
||||
be able to edit the config file if it was in the same directory as the
|
||||
source -- so I created a new directory with no rights restrictions,
|
||||
and moved the configuration file into it:
|
||||
|
||||
<ianh:~/mozbot> mkdir config
|
||||
<ianh:~/mozbot> mv mozbot.pl.cfg config/default
|
||||
<ianh:~/mozbot> chmod ugo=rwx config
|
||||
<ianh:~/mozbot> chmod ugo=rw config/default
|
||||
|
||||
In order to not have to change all the perl scripts, I gave them a
|
||||
fake 'mozbot' directory:
|
||||
|
||||
<ianh:~/mozbot> mkdir u
|
||||
<ianh:~/mozbot> mkdir u/ianh
|
||||
<ianh:~/mozbot> cd u/ianh
|
||||
<ianh:~/mozbot/u/ianh> ln -s / mozbot
|
||||
<ianh:~/mozbot/u/ianh> cd ../../
|
||||
|
||||
At this point I ran 'su' to drop down to a root shell. Be careful!
|
||||
|
||||
I had to copy several library files to a usr/lib directory. To do
|
||||
this, the 'truss' and 'ldd' tools came in very useful. In particular,
|
||||
I used 'truss' to watch what calls mozbot was attempting, and 'ldd' to
|
||||
find what modules dependencies Perl, wget, and the modules had.
|
||||
|
||||
Credit should be given to Pavlov for actually doing most of this for
|
||||
me... I didn't even know 'ldd' existed until he showed me. ;-)
|
||||
|
||||
Here is the list of the modules I copied:
|
||||
|
||||
usr/lib:
|
||||
ld.so.1 libdl.so.1 libgen.so.1 libmp.so.2
|
||||
libresolv.so.1 libsec.so.1 nscd_nischeck nss_files.so.1
|
||||
libc.so.1 libdoor.so.1 libld.so.2 libnsl.so.1
|
||||
libresolv.so.2 libsocket.so.1 nss_compat.so.1 nss_nis.so.1
|
||||
libcrypt_i.so.1 libelf.so.1 liblddbg.so.4 libpthread.so.1
|
||||
librtld.so.1 libthread.so.1 nss_dns.so.1 nss_nisplus.so.1
|
||||
|
||||
usr/platform/SUNW,Ultra-60:
|
||||
libc_psr.so.1
|
||||
|
||||
You may not need all of these.
|
||||
|
||||
I also had to copy /dev/null, /dev/zero, /dev/tcp, /dev/ticotsord and
|
||||
/dev/udp into a new dev/ directory (hint: use 'tar' to copy devices,
|
||||
it won't work if you try to do it with 'cp'). I may not have needed
|
||||
all of these (this was slightly complicated by the fact that on
|
||||
Solaris the /dev devices are symlinks; I used 'tar' to copy the real
|
||||
devices from /devices and renamed them when I extracted the tarball):
|
||||
|
||||
total 4
|
||||
drwxrwxr-x 2 root other 512 Mar 30 14:34 .
|
||||
drwxr-xr-x 16 root staff 512 Mar 30 15:47 ..
|
||||
crw-rw-r-- 1 root sys 13, 2 Mar 30 14:25 null
|
||||
crw-rw-rw- 1 root sys 11, 42 Jun 6 2000 tcp
|
||||
crw-rw-rw- 1 root sys 105, 1 Jun 6 2000 ticotsord
|
||||
crw-rw-rw- 1 root sys 11, 41 Jun 6 2000 udp
|
||||
crw-rw-r-- 1 root sys 13, 12 Jun 6 2000 zero
|
||||
|
||||
I had to copy several files from /etc into a new 'etc' directory, in
|
||||
particular:
|
||||
|
||||
etc:
|
||||
group hosts netconfig nsswitch.conf
|
||||
passwd protocols resolv.conf wgetrc
|
||||
|
||||
You may wish to sanitize your 'passwd' file. For the nsswitch.conf
|
||||
file you should use the 'nsswitch.dns' file (if you have one) -- make
|
||||
sure the DNS line is 'dns files' and not 'files dns'. (Profuse thanks
|
||||
go to rfm from Sun who helped me with this.)
|
||||
|
||||
Now I used 'chown' to make every file in /u/ianh/mozbot/ be owned by
|
||||
root, except the config directory. I also edited 'mozbot.pl' to ensure
|
||||
that the correct arguments were passed to 'setuid' and 'setgid' --
|
||||
search for 'setuid' in the source to find the right place.
|
||||
|
||||
With that all set up, I finally could run the bot safe in the
|
||||
knowledge that it was relatively secure:
|
||||
|
||||
<root:/u/ianh/mozbot> ./run-mozbot-chrooted
|
||||
|
||||
I hope this has helped you in some way!!!
|
||||
|
||||
-- end --
|
||||
@@ -1,15 +0,0 @@
|
||||
_ _
|
||||
m o z i l l a |.| o r g | |
|
||||
_ __ ___ ___ ___| |__ ___ | |_
|
||||
| '_ ` _ \ / _ \_ / '_ \ / _ \| __|
|
||||
| | | | | | (_) / /| |_) | (_) | |_
|
||||
|_| |_| |_|\___/___|_.__/ \___/ \__|
|
||||
=======================- 2 . 2 -==
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
Forget it.
|
||||
|
||||
-- end --
|
||||
@@ -1,2 +0,0 @@
|
||||
This is the source code for "mozbot", the IRC bot who hangs out in the
|
||||
#mozilla channel at irc.mozilla.org.
|
||||
@@ -1,130 +0,0 @@
|
||||
connectTimeout=120
|
||||
helpline=see http://www.mozilla.org/projects/mozbot/
|
||||
sleep=60
|
||||
throttleTime=2.2
|
||||
Admin::files=lib/Configuration.pm
|
||||
Admin::files=lib/Mails.pm
|
||||
Admin::files=mozbot.pl
|
||||
Admin::files=lib/IO/SecurePipe.pm
|
||||
Bugzilla::ignoreCommentsFrom=|
|
||||
FortuneCookies::bakingTime=20
|
||||
FortuneCookies::cookies=* UNIX is a Trademark of Bell Laboratories.
|
||||
FortuneCookies::cookies=/earth is 98% full ... please delete anyone you can.
|
||||
FortuneCookies::cookies=A man is not complete until he is married -- then he is finished.
|
||||
FortuneCookies::cookies=A man with his hands in pockets feels foolish, but a man with holes in pockets feels nuts.
|
||||
FortuneCookies::cookies=A meeting is an event at which the minutes are kept and the hours are lost.
|
||||
FortuneCookies::cookies=A modem is a baudy house.
|
||||
FortuneCookies::cookies=A thunderstorm in .nl here can startle a butterfly in .au
|
||||
FortuneCookies::cookies=Anyone can make an omelet with eggs. The trick is to make one with none.
|
||||
FortuneCookies::cookies=Best of all is never to have been born. Second best is to die soon.
|
||||
FortuneCookies::cookies=Better to sleep with chicken than to choke it.
|
||||
FortuneCookies::cookies=Confession is good for the soul, but bad for the career.
|
||||
FortuneCookies::cookies=Confucius not: know what to say!
|
||||
FortuneCookies::cookies=Confucius say: "Is more to running BBS than finding ON.
|
||||
FortuneCookies::cookies=Confucius say: A bird in hand makes hard to blow nose.
|
||||
FortuneCookies::cookies=Confucius say: Baby conceived in automatic car shiftless bastard.
|
||||
FortuneCookies::cookies=Confucius say: I didn't say that!
|
||||
FortuneCookies::cookies=Confucius say: Is stuffy inside fortune cookie.
|
||||
FortuneCookies::cookies=Confucius say: Man who Farts in Church sits in own pew.
|
||||
FortuneCookies::cookies=Confucius say: Man who pull out too fast leave rubber.
|
||||
FortuneCookies::cookies=Confucius say: Man who stand on toilet is high on pot.
|
||||
FortuneCookies::cookies=Confucius say: Man with hand in pocket is having a ball.
|
||||
FortuneCookies::cookies=Confucius say: Man with no legs bums around.
|
||||
FortuneCookies::cookies=Confucius say: Put Rooster in Freezer Get A Stiff Cock.
|
||||
FortuneCookies::cookies=Confucius say: Shit happens.
|
||||
FortuneCookies::cookies=Confucius say: Show off always shown up in showdown.
|
||||
FortuneCookies::cookies=Confucius say: Woman who cook carrots and peas in same pot not sanitary!
|
||||
FortuneCookies::cookies=Confucius say: `A Watched Tandy Never Boots!
|
||||
FortuneCookies::cookies=Confucius say: man who smoke pot choke on handle.
|
||||
FortuneCookies::cookies=Confucius say: nothing - Because he's dead!
|
||||
FortuneCookies::cookies=Confucius say: too damn much!
|
||||
FortuneCookies::cookies=Death is nature's way of telling you to slow down.
|
||||
FortuneCookies::cookies=Debug is human, de-fix divine.
|
||||
FortuneCookies::cookies=Despite all appearances, your boss is a thinking, feeling, human being.
|
||||
FortuneCookies::cookies=Do not drink coffee in early A.M. It will keep you awake until noon.
|
||||
FortuneCookies::cookies=Do not simplify the design of a program if a way can be found to make it complex and wonderful.
|
||||
FortuneCookies::cookies=Due to lack of disk space, this fortune database has been discontinued.
|
||||
FortuneCookies::cookies=Early to bed and early to rise and you'll be groggy when everyone else is wide awake.
|
||||
FortuneCookies::cookies=Every path has its puddle.
|
||||
FortuneCookies::cookies=Everything that you know is wrong, but you can be straightened out.
|
||||
FortuneCookies::cookies=Experience is the worst teacher. It always gives the test first and the instruction afterward.
|
||||
FortuneCookies::cookies=Future looks spotty. You will spill soup in late evening.
|
||||
FortuneCookies::cookies=God made machine language; all the rest is the work of man.
|
||||
FortuneCookies::cookies=He that teaches himself has a fool for a master.
|
||||
FortuneCookies::cookies=He who crosses the ocean twice without washing is a dirty double crosser.
|
||||
FortuneCookies::cookies=He who has a shady past knows that nice guys finish last.
|
||||
FortuneCookies::cookies=History repeats itself. That's one thing wrong with history.
|
||||
FortuneCookies::cookies=Hope that the day after you die is a nice day.
|
||||
FortuneCookies::cookies=House without toilet is uncanny.
|
||||
FortuneCookies::cookies=I have a theory that it's impossible to prove anything, but I can't prove it.
|
||||
FortuneCookies::cookies=I know you're in search of yourself, I just haven't seen you anywhere.
|
||||
FortuneCookies::cookies=If at first you don't succeed, redefine success.
|
||||
FortuneCookies::cookies=If life isn't what you wanted, have you asked for anything else?
|
||||
FortuneCookies::cookies=If this fortune didn't exist, somebody would have invented it.
|
||||
FortuneCookies::cookies=If we meet a man of rare intellect, we should ask him what book he reads.
|
||||
FortuneCookies::cookies=If you are too busy to read, then you are too busy.
|
||||
FortuneCookies::cookies=If you do something right once, someone will ask you to do it again.
|
||||
FortuneCookies::cookies=If you park, don't drink, accidents cause people.
|
||||
FortuneCookies::cookies=If your aim in life is nothing, you can't miss.
|
||||
FortuneCookies::cookies=In English, every word can be verbed. Would that it were so in our programming languages.
|
||||
FortuneCookies::cookies=In an orderly world, there's always a place for the disorderly.
|
||||
FortuneCookies::cookies=In the force if Yoda's so strong, construct a sentence with words in the proper order then why can't he?
|
||||
FortuneCookies::cookies=It is not well to be thought of as one who meekly submits to insolence and intimidation.
|
||||
FortuneCookies::cookies=It is very difficult to prophesy, especially when it pertains to the future.
|
||||
FortuneCookies::cookies=Life is too short to be taken seriously.
|
||||
FortuneCookies::cookies=Logic is a systematic method of coming to the wrong conclusion with confidence.
|
||||
FortuneCookies::cookies=Ma Bell is a mean mother!
|
||||
FortuneCookies::cookies=Man who arrives at party two hours late will find he has been beaten to the punch.
|
||||
FortuneCookies::cookies=Man who eat many prunes, sit on toilet many moons.
|
||||
FortuneCookies::cookies=Man who fight with wife all day, get no peace at night!
|
||||
FortuneCookies::cookies=Man who put head on Rail Road track to listen for train likely to end up with sudden splitting headache.
|
||||
FortuneCookies::cookies=May all your PUSHes be POPped.
|
||||
FortuneCookies::cookies=Measure with a micrometer. Mark with chalk. Cut with an axe.
|
||||
FortuneCookies::cookies=Message will arrive in the mail. Destroy, before the FBI sees it.
|
||||
FortuneCookies::cookies=Never trust a computer you can't repair yourself.
|
||||
FortuneCookies::cookies=Never underestimate the power of human stupidity.
|
||||
FortuneCookies::cookies=No matter what happens, there is always someone who knew it would.
|
||||
FortuneCookies::cookies=Nondeterminism means never having to say you are wrong.
|
||||
FortuneCookies::cookies=On the eighth day, God created FORTRAN.
|
||||
FortuneCookies::cookies=One person's error is another person's data.
|
||||
FortuneCookies::cookies=One possible reason that things aren't going according to plan is that there never was a plan in the first place.
|
||||
FortuneCookies::cookies=One seldom sees a monument to a committee.
|
||||
FortuneCookies::cookies=Others can stop you temporarily, only you can do it permanently.
|
||||
FortuneCookies::cookies=Overflow on /dev/null, please empty the bit bucket.
|
||||
FortuneCookies::cookies=Passwords are implemented as a result of insecurity.
|
||||
FortuneCookies::cookies=Pause for storage relocation.
|
||||
FortuneCookies::cookies=Pretend to spank me -- I'm a pseudo-masochist!
|
||||
FortuneCookies::cookies=Quantity is no substitute for quality, but its the only one we've got.
|
||||
FortuneCookies::cookies=Real computer scientists don't comment their code. The identifiers are so long they can't afford the disk space.
|
||||
FortuneCookies::cookies=Recursion is the root of computation since it trades description for time.
|
||||
FortuneCookies::cookies=Standards are crucial. And the best thing about standards is: there are so many to choose from!
|
||||
FortuneCookies::cookies=The first version always gets thrown away.
|
||||
FortuneCookies::cookies=The important thing is not to stop questioning.
|
||||
FortuneCookies::cookies=The light of a hundred stars does not equal the light of the moon.
|
||||
FortuneCookies::cookies=The meek shall inherit the earth; the rest of us will go to the stars.
|
||||
FortuneCookies::cookies=The more you sweat in peace, the less you bleed in war.
|
||||
FortuneCookies::cookies=The most important early product on the way to developing a good product is an imperfect version.
|
||||
FortuneCookies::cookies=The number of feet in a yard is directly proportional to the success of the barbecue.
|
||||
FortuneCookies::cookies=The only person who always got his work done by Friday was Robinson Crusoe.
|
||||
FortuneCookies::cookies=The sun will rise in the east today, indicating nothing in particular.
|
||||
FortuneCookies::cookies=The trouble with computers is that they do what you tell them, not what you want.
|
||||
FortuneCookies::cookies=There are two ways to write error-free programs; only the third one works.
|
||||
FortuneCookies::cookies=This life is yours. Some of it was given to you; the rest, you made yourself.
|
||||
FortuneCookies::cookies=This system will self-destruct in five minutes.
|
||||
FortuneCookies::cookies=This will be a memorable month -- no matter how hard you try to forget it.
|
||||
FortuneCookies::cookies=Those who do not understand Unix are condemned to reinvent it, poorly.
|
||||
FortuneCookies::cookies=Those who smile bring light to others
|
||||
FortuneCookies::cookies=Tomorrow will be cancelled due to lack of interest.
|
||||
FortuneCookies::cookies=War doesn't determine who's right, war determines who's left.
|
||||
FortuneCookies::cookies=War is peace. Freedom is slavery. Ketchup is a vegetable.
|
||||
FortuneCookies::cookies=We promise according to our hopes, and perform according to our fears.
|
||||
FortuneCookies::cookies=Wife who put husband in doghouse soon find him in cat house.
|
||||
FortuneCookies::cookies=You can always tell the people that are forging the new frontier. They're the ones with arrows sticking out of their backs.
|
||||
FortuneCookies::cookies=You have many friends and very few living enemies.
|
||||
FortuneCookies::cookies=You may attend a party where strange customs prevail.
|
||||
FortuneCookies::cookies=You might have mail.
|
||||
FortuneCookies::cookies=You will be advanced socially, without any special effort on your part.
|
||||
FortuneCookies::cookies=You're currently going through a difficult transition period called "Life."
|
||||
FortuneCookies::cookies=panic: kernel segmentation violation. core dumped (only kidding)
|
||||
FortuneCookies::cookiesIndex=38
|
||||
FortuneCookies::cookiesMax=10
|
||||
@@ -1,198 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Harrison Page <harrison@netscape.com>
|
||||
# Terry Weissman <terry@mozilla.org>
|
||||
# Ian Hickson <py8ieh=mozbot@bath.ac.uk>
|
||||
|
||||
package Configuration;
|
||||
use strict;
|
||||
use Carp;
|
||||
|
||||
sub Get {
|
||||
my ($file, $config) = @_;
|
||||
my %seen;
|
||||
open FILE, "<$file" or return 0;
|
||||
my $line = 0;
|
||||
while (<FILE>) {
|
||||
$line++; chomp;
|
||||
if (/^ *([^#;][^=\n\r]*)(?:=(.*))?$/os) {
|
||||
my $value = $$config{$1};
|
||||
if (defined($value)) {
|
||||
$value = $$value while ref($value) eq 'REF';
|
||||
if (ref($value) eq 'SCALAR') {
|
||||
$$value = $2;
|
||||
} elsif (ref($value) eq 'ARRAY') {
|
||||
unless ($seen{$1}) {
|
||||
@$value = ();
|
||||
}
|
||||
if (defined($2)) {
|
||||
push(@$value, $2);
|
||||
}
|
||||
} elsif (ref($value) eq 'HASH') {
|
||||
unless ($seen{$1}) {
|
||||
%$value = ();
|
||||
}
|
||||
if (defined($2)) {
|
||||
$2 =~ /^(.)(.*?)\1=>(.*)$/so;
|
||||
$$value{$2} = $3;
|
||||
}
|
||||
}
|
||||
} # else unknown variable, ignore
|
||||
$seen{$1} = 1;
|
||||
} # else ignore (probably comment)
|
||||
}
|
||||
close FILE;
|
||||
return $line;
|
||||
}
|
||||
|
||||
sub Save {
|
||||
my ($file, $config) = @_;
|
||||
local $_;
|
||||
|
||||
# Try to keep file structure if possible
|
||||
my @lines;
|
||||
if (open FILE, "<$file") {
|
||||
while (<FILE>) {
|
||||
push @lines, $_;
|
||||
}
|
||||
close FILE;
|
||||
}
|
||||
|
||||
# but make sure we put in all the data (dups are dealt with)
|
||||
foreach (sort keys %$config) {
|
||||
push @lines, "$_=";
|
||||
}
|
||||
|
||||
# Open file to which we are saving
|
||||
open FILE, ">$file.~$$~" or confess("Could not save configuration: $!");
|
||||
|
||||
# ok, save file back again
|
||||
# make sure we only write parameters once by
|
||||
# keeping a log of those done
|
||||
my %seen;
|
||||
foreach (@lines) {
|
||||
chomp;
|
||||
if (/^ *([^#;][^=\n\r]*)=(.*)$/os) {
|
||||
if (defined($$config{$1})) {
|
||||
unless ($seen{$1}) {
|
||||
my $value = $$config{$1};
|
||||
$value = $$value while ref($value) eq 'REF';
|
||||
if (ref($value) eq 'SCALAR') {
|
||||
if (defined($$value)) {
|
||||
print FILE $1.'='.$$value."\n";
|
||||
}
|
||||
} elsif (ref($value) eq 'HASH') {
|
||||
my @keys = keys %$value;
|
||||
if (@keys > 0) {
|
||||
foreach my $item (@keys) {
|
||||
my $data = $$value{$item};
|
||||
my $delimiter;
|
||||
foreach ('"','\'','|',':','#','*','<','>','/','[',']','{','}',
|
||||
'(',')','\\','=','-','@','!','\$','%','&',' ','\`','~') {
|
||||
if ($item !~ /\Q$_\E=>/os) {
|
||||
$delimiter = $_;
|
||||
last;
|
||||
}
|
||||
}
|
||||
print FILE "$1=$delimiter$item$delimiter=>$data\n" if defined($delimiter);
|
||||
# else, silent data loss... XXX
|
||||
}
|
||||
} else {
|
||||
print FILE "$1\n";
|
||||
}
|
||||
} elsif (ref($value) eq 'ARRAY') {
|
||||
if (@$value > 0) {
|
||||
foreach my $item (@$value) {
|
||||
if (defined($item)) {
|
||||
print FILE "$1=$item\n";
|
||||
} else {
|
||||
print FILE "$1=\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print FILE "$1\n";
|
||||
}
|
||||
} else {
|
||||
confess("Unsupported data type '".ref($value)."' writing $1 (".$$config{$1}.')');
|
||||
}
|
||||
$seen{$1} = 1;
|
||||
} # else seen it already
|
||||
} else { # unknown
|
||||
print FILE "$1=$2\n";
|
||||
}
|
||||
} else {
|
||||
# might be a comment
|
||||
print FILE $_."\n";
|
||||
}
|
||||
}
|
||||
# actually do make a change to the real file
|
||||
close FILE;
|
||||
|
||||
# -- #mozwebtools was here --
|
||||
# * Hixie is sad as his bot crashes.
|
||||
# * Hixie adds in a check to make sure that the file he tries
|
||||
# to delete actually exists first.
|
||||
# <timeless> delete??
|
||||
|
||||
unlink $file or confess("Could not delete $file: $!") if (-e $file);
|
||||
rename("$file.~$$~", $file) or confess("Could not rename to $file: $!");
|
||||
}
|
||||
|
||||
sub Ensure {
|
||||
my ($config) = @_;
|
||||
my $changed;
|
||||
foreach (@$config) {
|
||||
if (ref($$_[1]) eq 'SCALAR') {
|
||||
unless (defined(${$$_[1]})) {
|
||||
if (-t) {
|
||||
print $$_[0]. ' ';
|
||||
<> =~ /^(.*)$/os;
|
||||
${$$_[1]} = $1;
|
||||
${$$_[1]} = '' unless defined ${$$_[1]};
|
||||
chomp(${$$_[1]});
|
||||
$changed++;
|
||||
} else {
|
||||
confess("Terminal is not interactive, so could not ask '$$_[0]'. Gave up");
|
||||
}
|
||||
}
|
||||
} elsif (ref($$_[1]) eq 'ARRAY') {
|
||||
unless (defined(@{$$_[1]})) {
|
||||
if (-t) {
|
||||
print $$_[0]. " (enter a blank line to finish)\n";
|
||||
my $input;
|
||||
do {
|
||||
$input = <>;
|
||||
$input = '' unless defined $input;
|
||||
chomp($input);
|
||||
push @{$$_[1]}, $input if $input;
|
||||
$changed++;
|
||||
} while $input;
|
||||
} else {
|
||||
confess("Terminal is not interactive, so could not ask '$$_[0]'. Gave up");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
confess("Unsupported data type expected for question '$$_[0]'");
|
||||
}
|
||||
}
|
||||
return $changed;
|
||||
}
|
||||
|
||||
1; # end
|
||||
@@ -1,66 +0,0 @@
|
||||
# IO::SecurePipe.pm
|
||||
# Created by Ian Hickson to make exec() call if IO::Pipe more secure.
|
||||
# Distributed under exactly the same licence terms as IO::Pipe.
|
||||
|
||||
package IO::SecurePipe;
|
||||
use strict;
|
||||
#use Carp;
|
||||
use IO::Pipe;
|
||||
use vars qw(@ISA);
|
||||
@ISA = qw(IO::Pipe);
|
||||
|
||||
my $do_spawn = $^O eq 'os2';
|
||||
|
||||
sub croak {
|
||||
exec $0 ($0, 'ABORT'); # do not call shutdown handlers
|
||||
exit(); # exit (implicit in exec() actually)
|
||||
}
|
||||
|
||||
sub _doit {
|
||||
my $me = shift;
|
||||
my $rw = shift;
|
||||
|
||||
my $pid = $do_spawn ? 0 : fork();
|
||||
|
||||
if($pid) { # Parent
|
||||
return $pid;
|
||||
}
|
||||
elsif(defined $pid) { # Child or spawn
|
||||
my $fh;
|
||||
my $io = $rw ? \*STDIN : \*STDOUT;
|
||||
my ($mode, $save) = $rw ? "r" : "w";
|
||||
if ($do_spawn) {
|
||||
require Fcntl;
|
||||
$save = IO::Handle->new_from_fd($io, $mode);
|
||||
# Close in child:
|
||||
fcntl(shift, Fcntl::F_SETFD(), 1) or croak "fcntl: $!";
|
||||
$fh = $rw ? ${*$me}[0] : ${*$me}[1];
|
||||
} else {
|
||||
shift;
|
||||
$fh = $rw ? $me->reader() : $me->writer(); # close the other end
|
||||
}
|
||||
bless $io, "IO::Handle";
|
||||
$io->fdopen($fh, $mode);
|
||||
$fh->close;
|
||||
|
||||
if ($do_spawn) {
|
||||
$pid = eval { system 1, @_ }; # 1 == P_NOWAIT
|
||||
my $err = $!;
|
||||
|
||||
$io->fdopen($save, $mode);
|
||||
$save->close or croak "Cannot close $!";
|
||||
croak "IO::Pipe: Cannot spawn-NOWAIT: $err" if not $pid or $pid < 0;
|
||||
return $pid;
|
||||
} else {
|
||||
exec { $_[0] } @_ or # XXX change here
|
||||
croak "IO::Pipe: Cannot exec: $!";
|
||||
}
|
||||
}
|
||||
else {
|
||||
croak "IO::Pipe: Cannot fork: $!";
|
||||
}
|
||||
|
||||
# NOT Reached
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,196 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Harrison Page <harrison@netscape.com>
|
||||
# Terry Weissman <terry@mozilla.org>
|
||||
# Ian Hickson <mozbot@hixie.ch>
|
||||
|
||||
package Mails;
|
||||
use strict;
|
||||
use Carp;
|
||||
|
||||
# User must declare the following package global variables:
|
||||
# $Mails::owner = \'e-mail address of owner';
|
||||
# $Mails::smtphost = 'name of SMTP server';
|
||||
# $Mails::debug = \&function to print debug messages # better solutions welcome
|
||||
|
||||
# send mail to the owner
|
||||
sub mailowner {
|
||||
my ($subject, $text) = @_;
|
||||
&$Mails::debug('I am going to mail the owner!!!');
|
||||
return &sendmail($$Mails::owner, $0, $subject, $text);
|
||||
}
|
||||
|
||||
sub RFC822time {
|
||||
# Returns today's date as an RFC822 compliant string with the
|
||||
# exception that the year is returned as four digits. In my
|
||||
# extremely valuable opinion RFC822 was wrong to specify the year
|
||||
# as two digits. Many email systems generate four-digit years.
|
||||
|
||||
# Today is defined as the first parameter, if given, or else the
|
||||
# value that time() gives.
|
||||
|
||||
my ($tsec,$tmin,$thour,$tmday,$tmon,$tyear,$twday,$tyday,$tisdst) = gmtime(shift || time());
|
||||
$tyear += 1900; # as mentioned above, this is not RFC822 compliant, but is Y2K-safe.
|
||||
$tsec = "0$tsec" if $tsec < 10;
|
||||
$tmin = "0$tmin" if $tmin < 10;
|
||||
$thour = "0$thour" if $thour < 10;
|
||||
$tmon = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')[$tmon];
|
||||
$twday = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')[$twday];
|
||||
return "$twday, $tmday $tmon $tyear $thour:$tmin:$tsec GMT";
|
||||
|
||||
}
|
||||
|
||||
sub sendmail {
|
||||
my ($to, $from, $subject, $text, $sig) = (@_, $0);
|
||||
eval {
|
||||
use Net::SMTP;
|
||||
my $date = &RFC822time();
|
||||
my $smtp = Net::SMTP->new($Mails::smtphost) or confess("Could not create SMTP connection to $Mails::smtphost! Giving Up");
|
||||
$smtp->mail($ENV{USER}); # XXX ?
|
||||
$smtp->to($to);
|
||||
$smtp->data(<<end);
|
||||
X-Mailer: $0, Mails.pm; $$Mails::owner
|
||||
To: $to
|
||||
From: $from
|
||||
Subject: $subject
|
||||
Date: $date
|
||||
|
||||
$text
|
||||
--
|
||||
$sig
|
||||
end
|
||||
$smtp->quit;
|
||||
} or do {
|
||||
&$Mails::debug('Failed to send e-mail.');
|
||||
&$Mails::debug($@);
|
||||
&$Mails::debug('-'x40);
|
||||
&$Mails::debug("To: $to");
|
||||
&$Mails::debug("From: $from");
|
||||
&$Mails::debug("Subject: $subject");
|
||||
&$Mails::debug("\n$text\n-- \n$sig");
|
||||
&$Mails::debug('-'x40);
|
||||
return 0;
|
||||
};
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
##########################################################
|
||||
#### The Mails ##########################################
|
||||
##########################################################
|
||||
|
||||
sub ServerDown {
|
||||
my ($server, $port, $localAddr, $nick, $ircname, $username) = @_;
|
||||
my $localAddrMessage;
|
||||
if (defined($localAddr)) {
|
||||
$localAddrMessage = <<end;
|
||||
You've configured me to assume that '$localAddr' is the address of the
|
||||
network interface to use. If this is wrong, change the localAddr
|
||||
setting in the configuration file (or remove it to enable autodetect).
|
||||
end
|
||||
} else {
|
||||
$localAddrMessage = <<end;
|
||||
I'm currently autodetecting the address of the network interface to
|
||||
use. If this host has more than one interface, set the localAddr
|
||||
setting in the configuration file to the IP address of the outgoing
|
||||
connection I should use.
|
||||
end
|
||||
}
|
||||
return &mailowner("Help! I can't talk to $server:$port!", <<end);
|
||||
|
||||
Hello Sir or Madam!
|
||||
|
||||
I'm afraid I could not connect to the IRC server. I tried, and will
|
||||
try and try again (unless you kill me...) but it was fruitless.
|
||||
|
||||
Could you kick the IRC server for me? Give it a right ol' booting.
|
||||
And hit the network connection while you are at it, would you please?
|
||||
|
||||
Thanks.
|
||||
|
||||
Here is what I was trying to connect to:
|
||||
|
||||
Server: $server
|
||||
Port: $port
|
||||
Nick: $nick
|
||||
Ircname: $ircname
|
||||
Username: $username
|
||||
|
||||
$localAddrMessage
|
||||
|
||||
Hope that helps.
|
||||
|
||||
Cheers,
|
||||
end
|
||||
}
|
||||
|
||||
sub ServerUp {
|
||||
my ($server) = @_;
|
||||
return &mailowner("Woohoo! $server let me in!", <<end);
|
||||
|
||||
Hello again.
|
||||
|
||||
You'll be happy to know that everything turned out for the better.
|
||||
|
||||
Seeya later,
|
||||
end
|
||||
}
|
||||
|
||||
sub NickShortage {
|
||||
my ($cfgfile, $hostname, $port, $username, $ircname, @nicks) = @_;
|
||||
local $" = "\n ";
|
||||
return &mailowner('There is a nick shortage!', <<end);
|
||||
|
||||
Hello Sir or Madam.
|
||||
|
||||
I could not find an unused nick on IRC.
|
||||
|
||||
I tried all of these:
|
||||
@nicks
|
||||
|
||||
If you like you could add some more nicks manually by
|
||||
editing my configuration file, "$cfgfile"... *hint* *hint*
|
||||
|
||||
Here is what I think I am connected to:
|
||||
|
||||
Hostname: $hostname
|
||||
Port: $port
|
||||
Username: $username
|
||||
IRC Name: $ircname
|
||||
|
||||
I'll e-mail you again when I manage to get on.
|
||||
|
||||
Seeya,
|
||||
end
|
||||
}
|
||||
|
||||
sub NickOk {
|
||||
my ($nick) = @_;
|
||||
return &mailowner("It's ok, I'm now using $nick as my nick.", <<end);
|
||||
|
||||
Hello again.
|
||||
|
||||
You'll be happy to know that everything turned out for the better.
|
||||
|
||||
Seeya later,
|
||||
end
|
||||
}
|
||||
|
||||
1; # end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
export PATH=/bin
|
||||
./mozbot.pl --chroot /config/default
|
||||
|
||||
# NOTE. This file requires that you follow the steps described in the
|
||||
# included INSTALL file.
|
||||
@@ -1,17 +0,0 @@
|
||||
CFLAGS=-g
|
||||
|
||||
OBJS=md5.o token.o main.o
|
||||
|
||||
all: $(OBJS) uuidgen
|
||||
|
||||
uuidgen: $(OBJS)
|
||||
gcc -o uuidgen $(OBJS)
|
||||
|
||||
md5.o: md5.c md5.h
|
||||
|
||||
token.o: token.c token.h
|
||||
|
||||
main.o: main.c
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ core uuidgen
|
||||
@@ -1,17 +0,0 @@
|
||||
/* copyright? hah! it's 10 lines of code! */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "token.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uuid_state state;
|
||||
uuid_t uuid;
|
||||
char output[1024];
|
||||
|
||||
create_uuid_state(&state);
|
||||
create_token(&state, &uuid);
|
||||
format_token(output, &uuid);
|
||||
|
||||
printf("%s\n", output);
|
||||
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
|
||||
/* Brutally hacked by John Walker back from ANSI C to K&R (no
|
||||
prototypes) to maintain the tradition that Netfone will compile
|
||||
with Sun's original "cc". */
|
||||
|
||||
#include <memory.h> /* for memcpy() */
|
||||
#include "md5.h"
|
||||
|
||||
#ifdef sgi
|
||||
#define HIGHFIRST
|
||||
#endif
|
||||
|
||||
#ifdef sun
|
||||
#define HIGHFIRST
|
||||
#endif
|
||||
|
||||
#ifndef HIGHFIRST
|
||||
#define byteReverse(buf, len) /* Nothing */
|
||||
#else
|
||||
/*
|
||||
* Note: this code is harmless on little-endian machines.
|
||||
*/
|
||||
void byteReverse(buf, longs)
|
||||
unsigned char *buf; unsigned longs;
|
||||
{
|
||||
uint32 t;
|
||||
do {
|
||||
t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
|
||||
((unsigned) buf[1] << 8 | buf[0]);
|
||||
*(uint32 *) buf = t;
|
||||
buf += 4;
|
||||
} while (--longs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
void MD5Init(ctx)
|
||||
struct MD5Context *ctx;
|
||||
{
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
void MD5Update(ctx, buf, len)
|
||||
struct MD5Context *ctx; unsigned char *buf; unsigned len;
|
||||
{
|
||||
uint32 t;
|
||||
|
||||
/* Update bitcount */
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
|
||||
ctx->bits[1]++; /* Carry from low to high */
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if (t) {
|
||||
unsigned char *p = (unsigned char *) ctx->in + t;
|
||||
|
||||
t = 64 - t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *) ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *) ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
void MD5Final(digest, ctx)
|
||||
unsigned char digest[16]; struct MD5Context *ctx;
|
||||
{
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
count = 64 - 1 - count;
|
||||
|
||||
/* Pad out to 56 mod 64 */
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *) ctx->in);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count - 8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
((uint32 *) ctx->in)[14] = ctx->bits[0];
|
||||
((uint32 *) ctx->in)[15] = ctx->bits[1];
|
||||
|
||||
MD5Transform(ctx->buf, (uint32 *) ctx->in);
|
||||
byteReverse((unsigned char *) ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
|
||||
}
|
||||
|
||||
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
void MD5Transform(buf, in)
|
||||
uint32 buf[4]; uint32 in[16];
|
||||
{
|
||||
register uint32 a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef MD5_H
|
||||
#define MD5_H
|
||||
|
||||
#ifdef __alpha
|
||||
typedef unsigned int uint32;
|
||||
#else
|
||||
typedef unsigned long uint32;
|
||||
#endif
|
||||
|
||||
struct MD5Context {
|
||||
uint32 buf[4];
|
||||
uint32 bits[2];
|
||||
unsigned char in[64];
|
||||
};
|
||||
|
||||
extern void MD5Init();
|
||||
extern void MD5Update();
|
||||
extern void MD5Final();
|
||||
extern void MD5Transform();
|
||||
|
||||
/*
|
||||
* This is needed to make RSAREF happy on some MS-DOS compilers.
|
||||
*/
|
||||
typedef struct MD5Context MD5_CTX;
|
||||
|
||||
#endif /* !MD5_H */
|
||||
@@ -1,356 +0,0 @@
|
||||
/*
|
||||
** Copyright (C) 1998-1999 Greg Stein. All Rights Reserved.
|
||||
**
|
||||
** By using this file, you agree to the terms and conditions set forth in
|
||||
** the LICENSE.html file which can be found at the top level of the mod_dav
|
||||
** distribution or at http://www.webdav.org/mod_dav/license-1.html.
|
||||
**
|
||||
** Contact information:
|
||||
** Greg Stein, PO Box 3151, Redmond, WA, 98073
|
||||
** gstein@lyra.org, http://www.webdav.org/mod_dav/
|
||||
*/
|
||||
|
||||
/*
|
||||
** DAV opaquelocktoken scheme implementation
|
||||
**
|
||||
** Written 5/99 by Keith Wannamaker, wannamak@us.ibm.com
|
||||
** Adapted from ISO/DCE RPC spec and a former Internet Draft
|
||||
** by Leach and Salz:
|
||||
** http://www.ics.uci.edu/pub/ietf/webdav/uuid-guid/draft-leach-uuids-guids-01
|
||||
**
|
||||
** Portions of the code are covered by the following license:
|
||||
**
|
||||
** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
|
||||
** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
|
||||
** Digital Equipment Corporation, Maynard, Mass.
|
||||
** Copyright (c) 1998 Microsoft.
|
||||
** To anyone who acknowledges that this file is provided "AS IS"
|
||||
** without any express or implied warranty: permission to use, copy,
|
||||
** modify, and distribute this file for any purpose is hereby
|
||||
** granted without fee, provided that the above copyright notices and
|
||||
** this notice appears in all source code copies, and that none of
|
||||
** the names of Open Software Foundation, Inc., Hewlett-Packard
|
||||
** Company, or Digital Equipment Corporation be used in advertising
|
||||
** or publicity pertaining to distribution of the software without
|
||||
** specific, written prior permission. Neither Open Software
|
||||
** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment
|
||||
** Corporation makes any representations about the suitability of
|
||||
** this software for any purpose.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "md5.h"
|
||||
#include "token.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
/* set the following to the number of 100ns ticks of the actual resolution of
|
||||
your system's clock */
|
||||
#define UUIDS_PER_TICK 1024
|
||||
|
||||
/* Set this to what your compiler uses for 64 bit data type */
|
||||
#ifdef WIN32
|
||||
#define unsigned64_t unsigned __int64
|
||||
#define I64(C) C
|
||||
#else
|
||||
#define unsigned64_t unsigned long long
|
||||
#define I64(C) C##LL
|
||||
#endif
|
||||
|
||||
typedef unsigned64_t uuid_time_t;
|
||||
|
||||
const uuid_t null_locktoken = {0};
|
||||
|
||||
static void format_uuid_v1(uuid_t * uuid, unsigned16 clockseq, uuid_time_t timestamp, uuid_node_t node);
|
||||
static void get_current_time(uuid_time_t * timestamp);
|
||||
static unsigned16 true_random(void);
|
||||
static void get_pseudo_node_identifier(uuid_node_t *node);
|
||||
static void get_system_time(uuid_time_t *uuid_time);
|
||||
static void get_random_info(unsigned char seed[16]);
|
||||
|
||||
|
||||
/* dav_create_opaquelocktoken - generates a UUID version 1 token.
|
||||
* Clock_sequence and node_address set to pseudo-random
|
||||
* numbers during init.
|
||||
*
|
||||
* Should postpend pid to account for non-seralized creation?
|
||||
*/
|
||||
int create_token(uuid_state *st, uuid_t *u)
|
||||
{
|
||||
uuid_time_t timestamp;
|
||||
|
||||
get_current_time(×tamp);
|
||||
format_uuid_v1(u, st->cs, timestamp, st->node);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* dav_create_uuid_state - seed UUID state with pseudorandom data
|
||||
*/
|
||||
void create_uuid_state(uuid_state *st)
|
||||
{
|
||||
st->cs = true_random();
|
||||
get_pseudo_node_identifier(&st->node);
|
||||
}
|
||||
|
||||
/*
|
||||
* dav_format_opaquelocktoken - generates a text representation
|
||||
* of an opaquelocktoken
|
||||
*/
|
||||
void format_token(char *target, const uuid_t *u)
|
||||
{
|
||||
sprintf(target, "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
u->time_low, u->time_mid, u->time_hi_and_version,
|
||||
u->clock_seq_hi_and_reserved, u->clock_seq_low,
|
||||
u->node[0], u->node[1], u->node[2],
|
||||
u->node[3], u->node[4], u->node[5]);
|
||||
}
|
||||
|
||||
/* convert a pair of hex digits to an integer value [0,255] */
|
||||
static int dav_parse_hexpair(const char *s)
|
||||
{
|
||||
int result;
|
||||
int temp;
|
||||
|
||||
result = s[0] - '0';
|
||||
if (result > 48)
|
||||
result = (result - 39) << 4;
|
||||
else if (result > 16)
|
||||
result = (result - 7) << 4;
|
||||
else
|
||||
result = result << 4;
|
||||
|
||||
temp = s[1] - '0';
|
||||
if (temp > 48)
|
||||
result |= temp - 39;
|
||||
else if (temp > 16)
|
||||
result |= temp - 7;
|
||||
else
|
||||
result |= temp;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* dav_parse_locktoken: Parses string produced from
|
||||
* dav_format_opaquelocktoken back into a uuid_t
|
||||
* structure. On failure, return DAV_IF_ERROR_PARSE,
|
||||
* else DAV_IF_ERROR_NONE.
|
||||
*/
|
||||
int parse_token(const char *char_token, uuid_t *bin_token)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 36; ++i) {
|
||||
char c = char_token[i];
|
||||
if (!isxdigit(c) &&
|
||||
!(c == '-' && (i == 8 || i == 13 || i == 18 || i == 23)))
|
||||
return -1;
|
||||
}
|
||||
if (char_token[36] != '\0')
|
||||
return -1;
|
||||
|
||||
bin_token->time_low =
|
||||
(dav_parse_hexpair(&char_token[0]) << 24) |
|
||||
(dav_parse_hexpair(&char_token[2]) << 16) |
|
||||
(dav_parse_hexpair(&char_token[4]) << 8) |
|
||||
dav_parse_hexpair(&char_token[6]);
|
||||
|
||||
bin_token->time_mid =
|
||||
(dav_parse_hexpair(&char_token[9]) << 8) |
|
||||
dav_parse_hexpair(&char_token[11]);
|
||||
|
||||
bin_token->time_hi_and_version =
|
||||
(dav_parse_hexpair(&char_token[14]) << 8) |
|
||||
dav_parse_hexpair(&char_token[16]);
|
||||
|
||||
bin_token->clock_seq_hi_and_reserved = dav_parse_hexpair(&char_token[19]);
|
||||
bin_token->clock_seq_low = dav_parse_hexpair(&char_token[21]);
|
||||
|
||||
for (i = 6; i--;)
|
||||
bin_token->node[i] = dav_parse_hexpair(&char_token[i*2+24]);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* dav_compare_opaquelocktoken:
|
||||
* < 0 : a < b
|
||||
* == 0 : a = b
|
||||
* > 0 : a > b
|
||||
*/
|
||||
int compare_token(const uuid_t a, const uuid_t b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(uuid_t));
|
||||
}
|
||||
|
||||
/* format_uuid_v1 -- make a UUID from the timestamp, clockseq, and node ID */
|
||||
static void format_uuid_v1(uuid_t * uuid, unsigned16 clock_seq,
|
||||
uuid_time_t timestamp, uuid_node_t node)
|
||||
{
|
||||
/* Construct a version 1 uuid with the information we've gathered
|
||||
* plus a few constants. */
|
||||
uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF);
|
||||
uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF);
|
||||
uuid->time_hi_and_version = (unsigned short)((timestamp >> 48) & 0x0FFF);
|
||||
uuid->time_hi_and_version |= (1 << 12);
|
||||
uuid->clock_seq_low = clock_seq & 0xFF;
|
||||
uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
|
||||
uuid->clock_seq_hi_and_reserved |= 0x80;
|
||||
memcpy(&uuid->node, &node, sizeof uuid->node);
|
||||
}
|
||||
|
||||
/* get-current_time -- get time as 60 bit 100ns ticks since whenever.
|
||||
Compensate for the fact that real clock resolution is less than 100ns. */
|
||||
static void get_current_time(uuid_time_t * timestamp)
|
||||
{
|
||||
uuid_time_t time_now;
|
||||
static uuid_time_t time_last;
|
||||
static unsigned16 uuids_this_tick;
|
||||
static int inited = 0;
|
||||
|
||||
if (!inited) {
|
||||
get_system_time(&time_now);
|
||||
uuids_this_tick = UUIDS_PER_TICK;
|
||||
inited = 1;
|
||||
};
|
||||
|
||||
while (1) {
|
||||
get_system_time(&time_now);
|
||||
|
||||
/* if clock reading changed since last UUID generated... */
|
||||
if (time_last != time_now) {
|
||||
/* reset count of uuids gen'd with this clock reading */
|
||||
uuids_this_tick = 0;
|
||||
break;
|
||||
};
|
||||
if (uuids_this_tick < UUIDS_PER_TICK) {
|
||||
uuids_this_tick++;
|
||||
break;
|
||||
}; /* going too fast for our clock; spin */
|
||||
}; /* add the count of uuids to low order bits of the clock reading */
|
||||
|
||||
*timestamp = time_now + uuids_this_tick;
|
||||
}
|
||||
|
||||
/* true_random -- generate a crypto-quality random number.
|
||||
This sample doesn't do that. */
|
||||
static unsigned16 true_random(void)
|
||||
{
|
||||
uuid_time_t time_now;
|
||||
|
||||
get_system_time(&time_now);
|
||||
time_now = time_now/UUIDS_PER_TICK;
|
||||
srand((unsigned int)(((time_now >> 32) ^ time_now)&0xffffffff));
|
||||
|
||||
return rand();
|
||||
}
|
||||
|
||||
/* This sample implementation generates a random node ID *
|
||||
* in lieu of a system dependent call to get IEEE node ID. */
|
||||
static void get_pseudo_node_identifier(uuid_node_t *node)
|
||||
{
|
||||
unsigned char seed[16];
|
||||
|
||||
get_random_info(seed);
|
||||
seed[0] |= 0x80;
|
||||
memcpy(node, seed, sizeof(*node));
|
||||
}
|
||||
|
||||
/* system dependent call to get the current system time.
|
||||
Returned as 100ns ticks since Oct 15, 1582, but resolution may be
|
||||
less than 100ns. */
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
static void get_system_time(uuid_time_t *uuid_time)
|
||||
{
|
||||
ULARGE_INTEGER time;
|
||||
|
||||
GetSystemTimeAsFileTime((FILETIME *)&time);
|
||||
|
||||
/* NT keeps time in FILETIME format which is 100ns ticks since
|
||||
Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582.
|
||||
The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec)
|
||||
+ 18 years and 5 leap days. */
|
||||
|
||||
time.QuadPart +=
|
||||
(unsigned __int64) (1000*1000*10) // seconds
|
||||
* (unsigned __int64) (60 * 60 * 24) // days
|
||||
* (unsigned __int64) (17+30+31+365*18+5); // # of days
|
||||
*uuid_time = time.QuadPart;
|
||||
}
|
||||
|
||||
static void get_random_info(unsigned char seed[16])
|
||||
{
|
||||
MD5_CTX c;
|
||||
struct {
|
||||
MEMORYSTATUS m;
|
||||
SYSTEM_INFO s;
|
||||
FILETIME t;
|
||||
LARGE_INTEGER pc;
|
||||
DWORD tc;
|
||||
DWORD l;
|
||||
char hostname[MAX_COMPUTERNAME_LENGTH + 1];
|
||||
|
||||
} r;
|
||||
|
||||
MD5Init(&c); /* memory usage stats */
|
||||
GlobalMemoryStatus(&r.m); /* random system stats */
|
||||
GetSystemInfo(&r.s); /* 100ns resolution (nominally) time of day */
|
||||
GetSystemTimeAsFileTime(&r.t); /* high resolution performance counter */
|
||||
QueryPerformanceCounter(&r.pc); /* milliseconds since last boot */
|
||||
r.tc = GetTickCount();
|
||||
r.l = MAX_COMPUTERNAME_LENGTH + 1;
|
||||
|
||||
GetComputerName(r.hostname, &r.l );
|
||||
MD5Update(&c, (const unsigned char *) &r, sizeof(r));
|
||||
MD5Final(seed, &c);
|
||||
}
|
||||
|
||||
#else /* WIN32 */
|
||||
|
||||
static void get_system_time(uuid_time_t *uuid_time)
|
||||
{
|
||||
struct timeval tp;
|
||||
|
||||
gettimeofday(&tp, (struct timezone *)0);
|
||||
|
||||
/* Offset between UUID formatted times and Unix formatted times.
|
||||
UUID UTC base time is October 15, 1582.
|
||||
Unix base time is January 1, 1970. */
|
||||
*uuid_time = (tp.tv_sec * 10000000) + (tp.tv_usec * 10) +
|
||||
I64(0x01B21DD213814000);
|
||||
}
|
||||
|
||||
static void get_random_info(unsigned char seed[16])
|
||||
{
|
||||
MD5_CTX c;
|
||||
/* Leech & Salz use Linux-specific struct sysinfo;
|
||||
* replace with pid/tid for portability (in the spirit of mod_unique_id) */
|
||||
struct {
|
||||
/* Add thread id here, if applicable, when we get to pthread or apr */
|
||||
pid_t pid;
|
||||
struct timeval t;
|
||||
char hostname[257];
|
||||
|
||||
} r;
|
||||
|
||||
MD5Init(&c);
|
||||
r.pid = getpid();
|
||||
gettimeofday(&r.t, (struct timezone *)0);
|
||||
gethostname(r.hostname, 256);
|
||||
MD5Update(&c, (const unsigned char *)&r, sizeof(r));
|
||||
MD5Final(seed, &c);
|
||||
}
|
||||
|
||||
#endif /* WIN32 */
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
** Copyright (C) 1998-1999 Greg Stein. All Rights Reserved.
|
||||
**
|
||||
** By using this file, you agree to the terms and conditions set forth in
|
||||
** the LICENSE.html file which can be found at the top level of the mod_dav
|
||||
** distribution or at http://www.webdav.org/mod_dav/license-1.html.
|
||||
**
|
||||
** Contact information:
|
||||
** Greg Stein, PO Box 3151, Redmond, WA, 98073
|
||||
** gstein@lyra.org, http://www.webdav.org/mod_dav/
|
||||
*/
|
||||
|
||||
/*
|
||||
** DAV opaquelocktoken scheme implementation
|
||||
**
|
||||
** Written 5/99 by Keith Wannamaker, wannamak@us.ibm.com
|
||||
** Adapted from ISO/DCE RPC spec and a former Internet Draft
|
||||
** by Leach and Salz:
|
||||
** http://www.ics.uci.edu/pub/ietf/webdav/uuid-guid/draft-leach-uuids-guids-01
|
||||
**
|
||||
** Portions of the code are covered by the following license:
|
||||
**
|
||||
** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
|
||||
** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
|
||||
** Digital Equipment Corporation, Maynard, Mass.
|
||||
** Copyright (c) 1998 Microsoft.
|
||||
** To anyone who acknowledges that this file is provided "AS IS"
|
||||
** without any express or implied warranty: permission to use, copy,
|
||||
** modify, and distribute this file for any purpose is hereby
|
||||
** granted without fee, provided that the above copyright notices and
|
||||
** this notice appears in all source code copies, and that none of
|
||||
** the names of Open Software Foundation, Inc., Hewlett-Packard
|
||||
** Company, or Digital Equipment Corporation be used in advertising
|
||||
** or publicity pertaining to distribution of the software without
|
||||
** specific, written prior permission. Neither Open Software
|
||||
** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment
|
||||
** Corporation makes any representations about the suitability of
|
||||
** this software for any purpose.
|
||||
*/
|
||||
|
||||
#ifndef _TOKEN_H_
|
||||
#define _TOKEN_H_
|
||||
|
||||
typedef unsigned long unsigned32;
|
||||
typedef unsigned short unsigned16;
|
||||
typedef unsigned char unsigned8;
|
||||
|
||||
typedef struct {
|
||||
char nodeID[6];
|
||||
} uuid_node_t;
|
||||
|
||||
#undef uuid_t
|
||||
|
||||
typedef struct _uuid_t
|
||||
{
|
||||
unsigned32 time_low;
|
||||
unsigned16 time_mid;
|
||||
unsigned16 time_hi_and_version;
|
||||
unsigned8 clock_seq_hi_and_reserved;
|
||||
unsigned8 clock_seq_low;
|
||||
unsigned8 node[6];
|
||||
} uuid_t;
|
||||
|
||||
/* data type for UUID generator persistent state */
|
||||
|
||||
typedef struct {
|
||||
uuid_node_t node; /* saved node ID */
|
||||
unsigned16 cs; /* saved clock sequence */
|
||||
} uuid_state;
|
||||
|
||||
extern const uuid_t null_locktoken;
|
||||
|
||||
/* in dav_opaquelock.c */
|
||||
int create_token(uuid_state *st, uuid_t *u);
|
||||
void create_uuid_state(uuid_state *st);
|
||||
void format_token(char *target, const uuid_t *u);
|
||||
int compare_token(const uuid_t a, const uuid_t b);
|
||||
int parse_token(const char *char_token, uuid_t *bin_token);
|
||||
|
||||
#endif /* _TOKEN_H_ */
|
||||
39
mozilla/xpinstall/Makefile.in
Normal file
39
mozilla/xpinstall/Makefile.in
Normal file
@@ -0,0 +1,39 @@
|
||||
#!gmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Communicator client code,
|
||||
# released March 31, 1998.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
#
|
||||
# Contributors:
|
||||
# Daniel Veditz <dveditz@netscape.com>
|
||||
# Douglas Turner <dougt@netscape.com>
|
||||
|
||||
|
||||
DEPTH = ..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = public
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
||||
BIN
mozilla/xpinstall/macbuild/xpinstall.mcp
Normal file
BIN
mozilla/xpinstall/macbuild/xpinstall.mcp
Normal file
Binary file not shown.
BIN
mozilla/xpinstall/macbuild/xpinstallIDL.mcp
Normal file
BIN
mozilla/xpinstall/macbuild/xpinstallIDL.mcp
Normal file
Binary file not shown.
30
mozilla/xpinstall/makefile.win
Normal file
30
mozilla/xpinstall/makefile.win
Normal file
@@ -0,0 +1,30 @@
|
||||
#!nmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Communicator client code,
|
||||
# released March 31, 1998.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
#
|
||||
# Contributors:
|
||||
# Daniel Veditz <dveditz@netscape.com>
|
||||
# Douglas Turner <dougt@netscape.com>
|
||||
|
||||
|
||||
DEPTH=..
|
||||
|
||||
DIRS= public res src
|
||||
|
||||
include <$(DEPTH)\config\rules.mak>
|
||||
33
mozilla/xpinstall/notifier/SoftwareUpdate-Source-1.rdf
Normal file
33
mozilla/xpinstall/notifier/SoftwareUpdate-Source-1.rdf
Normal file
@@ -0,0 +1,33 @@
|
||||
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:NC="http://home.netscape.com/NC-rdf#">
|
||||
|
||||
<RDF:Bag ID="NC:SoftwareUpdateRoot">
|
||||
<RDF:li>
|
||||
<RDF:Bag ID="NC:NewSoftwareToday" NC:title="New Software">
|
||||
|
||||
<RDF:li>
|
||||
<RDF:Description ID="AimUpdate344">
|
||||
<NC:type resource="http://home.netscape.com/NC-rdf#SoftwarePackage" />
|
||||
<NC:title>AOL AIM</NC:title>
|
||||
<NC:description>An Instant Message Client</NC:description>
|
||||
<NC:version>3.4.1.12</NC:version>
|
||||
<NC:registryKey>/AOL/AIM/</NC:registryKey>
|
||||
<NC:url>http://home.netscape.com/index.html</NC:url>
|
||||
</RDF:Description>
|
||||
</RDF:li>
|
||||
|
||||
<RDF:li>
|
||||
<RDF:Description ID="PGPPlugin345">
|
||||
<NC:type resource="http://home.netscape.com/NC-rdf#SoftwarePackage" />
|
||||
<NC:title>PGP Plugin For Mozilla</NC:title>
|
||||
<NC:description>A high grade encryption plugin</NC:description>
|
||||
<NC:version>1.1.2.0</NC:version>
|
||||
<NC:registryKey>/PGP/ROCKS/</NC:registryKey>
|
||||
<NC:url>http://home.netscape.com/index.html</NC:url>
|
||||
</RDF:Description>
|
||||
</RDF:li>
|
||||
|
||||
</RDF:Bag>
|
||||
</RDF:li>
|
||||
</RDF:Bag>
|
||||
</RDF:RDF>
|
||||
57
mozilla/xpinstall/notifier/SoftwareUpdate.css
Normal file
57
mozilla/xpinstall/notifier/SoftwareUpdate.css
Normal file
@@ -0,0 +1,57 @@
|
||||
window {
|
||||
display: block;
|
||||
}
|
||||
|
||||
tree {
|
||||
display: table;
|
||||
background-color: #FFFFFF;
|
||||
border: none;
|
||||
border-spacing: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
treecol {
|
||||
display: table-column;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
treeitem {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
treehead {
|
||||
display: table-header-group;
|
||||
}
|
||||
|
||||
treebody {
|
||||
display: table-row-group;
|
||||
}
|
||||
|
||||
treecell {
|
||||
display: table-cell;
|
||||
font-family: Verdana, Sans-Serif;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
treecell[selectedcell] {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
|
||||
treehead treeitem treecell {
|
||||
background-color: #c0c0c0;
|
||||
border: outset 1px;
|
||||
border-color: white #707070 #707070 white;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
treeitem[type="http://home.netscape.com/NC-rdf#SoftwarePackage"] > treecell > titledbutton {
|
||||
list-style-image: url("resource:/res/rdf/SoftwareUpdatePackage.gif");
|
||||
}
|
||||
|
||||
treeitem[type="http://home.netscape.com/NC-rdf#Folder"] > treecell > titledbutton {
|
||||
list-style-image: url("resource:/res/rdf/bookmark-folder-closed.gif");
|
||||
|
||||
treeitem[type="http://home.netscape.com/NC-rdf#Folder"][open="true"] > treecell > titledbutton {
|
||||
list-style-image: url("resource:/res/rdf/bookmark-folder-open.gif");
|
||||
}
|
||||
123
mozilla/xpinstall/notifier/SoftwareUpdate.js
Normal file
123
mozilla/xpinstall/notifier/SoftwareUpdate.js
Normal file
@@ -0,0 +1,123 @@
|
||||
// the rdf service
|
||||
var RDF = Components.classes['component://netscape/rdf/rdf-service'].getService();
|
||||
RDF = RDF.QueryInterface(Components.interfaces.nsIRDFService);
|
||||
|
||||
function getAttr(registry,service,attr_name)
|
||||
{
|
||||
var attr = registry.GetTarget(service,
|
||||
RDF.GetResource('http://home.netscape.com/NC-rdf#' + attr_name),
|
||||
true);
|
||||
if (attr)
|
||||
attr = attr.QueryInterface(Components.interfaces.nsIRDFLiteral);
|
||||
|
||||
if (attr)
|
||||
attr = attr.Value;
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
function Init()
|
||||
{
|
||||
// this is the main rdf file.
|
||||
|
||||
var mainRegistry = RDF.GetDataSource('resource://res/rdf/SoftwareUpdates.rdf');
|
||||
|
||||
var mainContainer = Components.classes['component://netscape/rdf/container'].createInstance();
|
||||
mainContainer = mainContainer.QueryInterface(Components.interfaces.nsIRDFContainer);
|
||||
|
||||
mainContainer.Init(mainRegistry, RDF.GetResource('NC:SoftwareUpdateDataSources'));
|
||||
|
||||
// Now enumerate all of the softwareupdate datasources.
|
||||
var mainEnumerator = mainContainer.GetElements();
|
||||
while (mainEnumerator.HasMoreElements())
|
||||
{
|
||||
var aDistributor = mainEnumerator.GetNext();
|
||||
aDistributor = aDistributor.QueryInterface(Components.interfaces.nsIRDFResource);
|
||||
|
||||
var distributorContainer = Components.classes['component://netscape/rdf/container'].createInstance();
|
||||
distributorContainer = distributorContainer.QueryInterface(Components.interfaces.nsIRDFContainer);
|
||||
|
||||
var distributorRegistry = RDF.GetDataSource(aDistributor.Value);
|
||||
var distributorResource = RDF.GetResource('NC:SoftwareUpdateRoot');
|
||||
|
||||
distributorContainer.Init(distributorRegistry, distributorResource);
|
||||
|
||||
// Now enumerate all of the distributorContainer's packages.
|
||||
|
||||
var distributorEnumerator = distributorContainer.GetElements();
|
||||
|
||||
while (distributorEnumerator.HasMoreElements())
|
||||
{
|
||||
var aPackage = distributorEnumerator.GetNext();
|
||||
aPackage = aPackage.QueryInterface(Components.interfaces.nsIRDFResource);
|
||||
|
||||
// remove any that we do not want.
|
||||
|
||||
if (getAttr(distributorRegistry, aPackage, 'title') == "AOL AIM")
|
||||
{
|
||||
//distributorContainer.RemoveElement(aPackage, true);
|
||||
}
|
||||
}
|
||||
var tree = document.getElementById('tree');
|
||||
|
||||
// Add it to the tree control's composite datasource.
|
||||
tree.database.AddDataSource(distributorRegistry);
|
||||
|
||||
}
|
||||
|
||||
// Install all of the stylesheets in the softwareupdate Registry into the
|
||||
// panel.
|
||||
|
||||
// TODO
|
||||
|
||||
// XXX hack to force the tree to rebuild
|
||||
var treebody = document.getElementById('NC:SoftwareUpdateRoot');
|
||||
treebody.setAttribute('id', 'NC:SoftwareUpdateRoot');
|
||||
}
|
||||
|
||||
|
||||
function OpenURL(event, node)
|
||||
{
|
||||
if (node.getAttribute('type') == "http://home.netscape.com/NC-rdf#SoftwarePackage")
|
||||
{
|
||||
url = node.getAttribute('url');
|
||||
|
||||
/*window.open(url,'bookmarks');*/
|
||||
|
||||
var toolkitCore = XPAppCoresManager.Find("ToolkitCore");
|
||||
if (!toolkitCore)
|
||||
{
|
||||
toolkitCore = new ToolkitCore();
|
||||
if (toolkitCore)
|
||||
{
|
||||
toolkitCore.Init("ToolkitCore");
|
||||
}
|
||||
}
|
||||
|
||||
if (toolkitCore)
|
||||
{
|
||||
toolkitCore.ShowWindow(url,window);
|
||||
}
|
||||
|
||||
dump("OpenURL(" + url + ")\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// To get around "window.onload" not working in viewer.
|
||||
function Boot()
|
||||
{
|
||||
var tree = document.getElementById('tree');
|
||||
if (tree == null) {
|
||||
setTimeout(Boot, 0);
|
||||
}
|
||||
else {
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout('Boot()', 0);
|
||||
|
||||
30
mozilla/xpinstall/notifier/SoftwareUpdate.xul
Normal file
30
mozilla/xpinstall/notifier/SoftwareUpdate.xul
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="resource:/res/rdf/sidebar.css" type="text/css"?>
|
||||
<?xml-stylesheet href="resource:/res/rdf/SoftwareUpdate.css" type="text/css"?>
|
||||
<window
|
||||
xmlns:html="http://www.w3.org/TR/REC-html40"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<html:script src="SoftwareUpdate.js"/>
|
||||
|
||||
<tree id="tree"
|
||||
flex="100%"
|
||||
datasources="rdf:softwareupdates"
|
||||
ondblclick="return OpenURL(event, event.target.parentNode);">
|
||||
|
||||
<treecol rdf:resource="http://home.netscape.com/NC-rdf#title" />
|
||||
<treecol rdf:resource="http://home.netscape.com/NC-rdf#description" />
|
||||
<treecol rdf:resource="http://home.netscape.com/NC-rdf#version" />
|
||||
|
||||
<treehead>
|
||||
<treeitem>
|
||||
<treecell>Title</treecell>
|
||||
<treecell>Description</treecell>
|
||||
<treecell>Version</treecell>
|
||||
</treeitem>
|
||||
</treehead>
|
||||
|
||||
<treebody id="NC:SoftwareUpdateRoot" rdf:containment="http://home.netscape.com/NC-rdf#child" />
|
||||
</tree>
|
||||
</window>
|
||||
BIN
mozilla/xpinstall/notifier/SoftwareUpdatePackage.gif
Normal file
BIN
mozilla/xpinstall/notifier/SoftwareUpdatePackage.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 201 B |
7
mozilla/xpinstall/notifier/SoftwareUpdates.rdf
Normal file
7
mozilla/xpinstall/notifier/SoftwareUpdates.rdf
Normal file
@@ -0,0 +1,7 @@
|
||||
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:NC="http://home.netscape.com/softwareupdate-schema#">
|
||||
|
||||
<RDF:Bag ID="NC:SoftwareUpdateDataSources">
|
||||
<RDF:li resource="resource:/res/rdf/SoftwareUpdate-Source-1.rdf" />
|
||||
</RDF:Bag>
|
||||
</RDF:RDF>
|
||||
6
mozilla/xpinstall/public/MANIFEST
Normal file
6
mozilla/xpinstall/public/MANIFEST
Normal file
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# This is a list of local files which get copied to the mozilla:dist directory
|
||||
#
|
||||
|
||||
nsISoftwareUpdate.h
|
||||
nsSoftwareUpdateIIDs.h
|
||||
47
mozilla/xpinstall/public/Makefile.in
Normal file
47
mozilla/xpinstall/public/Makefile.in
Normal file
@@ -0,0 +1,47 @@
|
||||
#!gmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Communicator client code,
|
||||
# released March 31, 1998.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
#
|
||||
# Contributors:
|
||||
# Daniel Veditz <dveditz@netscape.com>
|
||||
# Douglas Turner <dougt@netscape.com>
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = xpinstall
|
||||
|
||||
XPIDLSRCS = nsIXPInstallProgress.idl
|
||||
|
||||
EXPORTS = \
|
||||
nsIDOMInstallTriggerGlobal.h \
|
||||
nsIDOMInstallVersion.h \
|
||||
nsSoftwareUpdateIIDs.h \
|
||||
nsISoftwareUpdate.h \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
113
mozilla/xpinstall/public/idl/Install.idl
Normal file
113
mozilla/xpinstall/public/idl/Install.idl
Normal file
@@ -0,0 +1,113 @@
|
||||
interface Install
|
||||
{
|
||||
/* IID: { 0x18c2f988, 0xb09f, 0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53}} */
|
||||
|
||||
const int BAD_PACKAGE_NAME = -200;
|
||||
const int UNEXPECTED_ERROR = -201;
|
||||
const int ACCESS_DENIED = -202;
|
||||
const int TOO_MANY_CERTIFICATES = -203; /* Installer file must have 1 certificate */
|
||||
const int NO_INSTALLER_CERTIFICATE = -204; /* Installer file must have a certificate */
|
||||
const int NO_CERTIFICATE = -205; /* Extracted file is not signed */
|
||||
const int NO_MATCHING_CERTIFICATE = -206; /* Extracted file does not match installer certificate */
|
||||
const int UNKNOWN_JAR_FILE = -207; /* JAR file has not been opened */
|
||||
const int INVALID_ARGUMENTS = -208; /* Bad arguments to a function */
|
||||
const int ILLEGAL_RELATIVE_PATH = -209; /* Illegal relative path */
|
||||
const int USER_CANCELLED = -210; /* User cancelled */
|
||||
const int INSTALL_NOT_STARTED = -211;
|
||||
const int SILENT_MODE_DENIED = -212;
|
||||
const int NO_SUCH_COMPONENT = -213; /* no such component in the registry. */
|
||||
const int FILE_DOES_NOT_EXIST = -214; /* File cannot be deleted as it does not exist */
|
||||
const int FILE_READ_ONLY = -215; /* File cannot be deleted as it is read only. */
|
||||
const int FILE_IS_DIRECTORY = -216; /* File cannot be deleted as it is a directory */
|
||||
const int NETWORK_FILE_IS_IN_USE = -217; /* File on the network is in-use */
|
||||
const int APPLE_SINGLE_ERR = -218; /* error in AppleSingle unpacking */
|
||||
const int INVALID_PATH_ERR = -219; /* GetFolder() did not like the folderID */
|
||||
const int PATCH_BAD_DIFF = -220; /* error in GDIFF patch */
|
||||
const int PATCH_BAD_CHECKSUM_TARGET = -221; /* source file doesn't checksum */
|
||||
const int PATCH_BAD_CHECKSUM_RESULT = -222; /* final patched file fails checksum */
|
||||
const int UNINSTALL_FAILED = -223; /* error while uninstalling a package */
|
||||
const int GESTALT_UNKNOWN_ERR = -5550;
|
||||
const int GESTALT_INVALID_ARGUMENT = -5551;
|
||||
|
||||
const int SUCCESS = 0;
|
||||
const int REBOOT_NEEDED = 999;
|
||||
|
||||
/* install types */
|
||||
const int LIMITED_INSTALL = 0;
|
||||
const int FULL_INSTALL = 1;
|
||||
const int NO_STATUS_DLG = 2;
|
||||
const int NO_FINALIZE_DLG = 4;
|
||||
|
||||
// these should not be public...
|
||||
/* message IDs*/
|
||||
const int SU_INSTALL_FILE_UNEXPECTED_MSG_ID = 0;
|
||||
const int SU_DETAILS_REPLACE_FILE_MSG_ID = 1;
|
||||
const int SU_DETAILS_INSTALL_FILE_MSG_ID = 2;
|
||||
//////////////////////////
|
||||
|
||||
readonly attribute wstring UserPackageName;
|
||||
readonly attribute wstring RegPackageName;
|
||||
|
||||
void Install();
|
||||
|
||||
void AbortInstall();
|
||||
|
||||
long AddDirectory( in wstring regName,
|
||||
in wstring version,
|
||||
in wstring jarSource,
|
||||
in InstallFolder folder,
|
||||
in wstring subdir,
|
||||
in boolean forceMode );
|
||||
|
||||
|
||||
long AddSubcomponent( in wstring regName,
|
||||
in wstring version,
|
||||
in wstring jarSource,
|
||||
in InstallFolder folder,
|
||||
in wstring targetName,
|
||||
in boolean forceMode );
|
||||
|
||||
long DeleteComponent( in wstring registryName);
|
||||
|
||||
long DeleteFile( in InstallFolder folder,
|
||||
in wstring relativeFileName );
|
||||
|
||||
long DiskSpaceAvailable( in InstallFolder folder );
|
||||
|
||||
long Execute(in wstring jarSource, in wstring args);
|
||||
|
||||
long FinalizeInstall();
|
||||
|
||||
long Gestalt (in wstring selector);
|
||||
|
||||
InstallFolder GetComponentFolder( in wstring regName,
|
||||
in wstring subdirectory);
|
||||
|
||||
InstallFolder GetFolder(in wstring targetFolder,
|
||||
in wstring subdirectory);
|
||||
|
||||
long GetLastError();
|
||||
|
||||
long GetWinProfile(in InstallFolder folder, in wstring file);
|
||||
|
||||
long GetWinRegistry();
|
||||
|
||||
long Patch( in wstring regName,
|
||||
in wstring version,
|
||||
in wstring jarSource,
|
||||
in InstallFolder folder,
|
||||
in wstring targetName );
|
||||
|
||||
void ResetError();
|
||||
|
||||
void SetPackageFolder( in InstallFolder folder );
|
||||
|
||||
long StartInstall( in wstring userPackageName,
|
||||
in wstring packageName,
|
||||
in wstring version,
|
||||
in long flags );
|
||||
|
||||
long Uninstall( in wstring packageName);
|
||||
|
||||
};
|
||||
24
mozilla/xpinstall/public/idl/InstallTrigger.idl
Normal file
24
mozilla/xpinstall/public/idl/InstallTrigger.idl
Normal file
@@ -0,0 +1,24 @@
|
||||
interface InstallTriggerGlobal
|
||||
{
|
||||
/* IID: { 0x18c2f987, 0xb09f, 0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53}} */
|
||||
|
||||
const int MAJOR_DIFF = 4;
|
||||
const int MINOR_DIFF = 3;
|
||||
const int REL_DIFF = 2;
|
||||
const int BLD_DIFF = 1;
|
||||
const int EQUAL = 0;
|
||||
|
||||
boolean UpdateEnabled ();
|
||||
|
||||
long StartSoftwareUpdate(in wstring URL);
|
||||
|
||||
long ConditionalSoftwareUpdate( in wstring URL,
|
||||
in wstring regName,
|
||||
in long diffLevel,
|
||||
in wstring version,
|
||||
in long mode);
|
||||
|
||||
long CompareVersion( in wstring regName, in wstring version );
|
||||
|
||||
};
|
||||
34
mozilla/xpinstall/public/idl/InstallVersion.idl
Normal file
34
mozilla/xpinstall/public/idl/InstallVersion.idl
Normal file
@@ -0,0 +1,34 @@
|
||||
interface InstallVersion
|
||||
{
|
||||
/* IID: { 0x18c2f986, 0xb09f, 0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53}} */
|
||||
|
||||
const int EQUAL = 0;
|
||||
const int BLD_DIFF = 1;
|
||||
const int BLD_DIFF_MINUS = -1;
|
||||
const int REL_DIFF = 2;
|
||||
const int REL_DIFF_MINUS = -2;
|
||||
const int MINOR_DIFF = 3;
|
||||
const int MINOR_DIFF_MINUS = -3;
|
||||
const int MAJOR_DIFF = 4;
|
||||
const int MAJOR_DIFF_MINUS = -4;
|
||||
|
||||
attribute int major;
|
||||
attribute int minor;
|
||||
attribute int release;
|
||||
attribute int build;
|
||||
|
||||
void InstallVersion();
|
||||
|
||||
void init(in wstring versionString);
|
||||
/*
|
||||
void init(in int major, in int minor, in int release, in int build);
|
||||
*/
|
||||
wstring toString();
|
||||
|
||||
/* int compareTo(in wstring version);
|
||||
int compareTo(in int major, in int minor, in int release, in int build);
|
||||
*/
|
||||
int compareTo(in InstallVersion versionObject);
|
||||
|
||||
};
|
||||
36
mozilla/xpinstall/public/makefile.win
Normal file
36
mozilla/xpinstall/public/makefile.win
Normal file
@@ -0,0 +1,36 @@
|
||||
#!nmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Communicator client code,
|
||||
# released March 31, 1998.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
#
|
||||
# Contributors:
|
||||
# Daniel Veditz <dveditz@netscape.com>
|
||||
# Douglas Turner <dougt@netscape.com>
|
||||
|
||||
MODULE=xpinstall
|
||||
DEPTH=..\..
|
||||
|
||||
EXPORTS= nsIDOMInstallTriggerGlobal.h \
|
||||
nsIDOMInstallVersion.h \
|
||||
nsSoftwareUpdateIIDs.h \
|
||||
nsISoftwareUpdate.h
|
||||
|
||||
XPIDLSRCS = .\nsIXPInstallProgress.idl
|
||||
|
||||
include <$(DEPTH)\config\config.mak>
|
||||
include <$(DEPTH)\config\rules.mak>
|
||||
1
mozilla/xpinstall/public/nsIDOMInstall.h
Normal file
1
mozilla/xpinstall/public/nsIDOMInstall.h
Normal file
@@ -0,0 +1 @@
|
||||
#error
|
||||
96
mozilla/xpinstall/public/nsIDOMInstallTriggerGlobal.h
Normal file
96
mozilla/xpinstall/public/nsIDOMInstallTriggerGlobal.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
/* AUTO-GENERATED. DO NOT EDIT!!! */
|
||||
|
||||
#ifndef nsIDOMInstallTriggerGlobal_h__
|
||||
#define nsIDOMInstallTriggerGlobal_h__
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIScriptContext.h"
|
||||
|
||||
|
||||
#define NS_IDOMINSTALLTRIGGERGLOBAL_IID \
|
||||
{ 0x18c2f987, 0xb09f, 0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53}}
|
||||
|
||||
class nsIDOMInstallTriggerGlobal : public nsISupports {
|
||||
public:
|
||||
static const nsIID& IID() { static nsIID iid = NS_IDOMINSTALLTRIGGERGLOBAL_IID; return iid; }
|
||||
enum {
|
||||
MAJOR_DIFF = 4,
|
||||
MINOR_DIFF = 3,
|
||||
REL_DIFF = 2,
|
||||
BLD_DIFF = 1,
|
||||
EQUAL = 0
|
||||
};
|
||||
|
||||
NS_IMETHOD UpdateEnabled(PRBool* aReturn)=0;
|
||||
|
||||
NS_IMETHOD StartSoftwareUpdate(const nsString& aURL, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD StartSoftwareUpdate(const nsString& aURL, PRInt32 aFlags, PRInt32* aReturn)=0;
|
||||
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, PRInt32 aDiffLevel, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, PRInt32 aDiffLevel, nsIDOMInstallVersion* aVersion, PRInt32 aMode, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32 aMode, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn)=0;
|
||||
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, PRInt32 aMajor, PRInt32 aMinor, PRInt32 aRelease, PRInt32 aBuild, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn)=0;
|
||||
};
|
||||
|
||||
|
||||
#define NS_DECL_IDOMINSTALLTRIGGERGLOBAL \
|
||||
NS_IMETHOD UpdateEnabled(PRBool* aReturn); \
|
||||
NS_IMETHOD StartSoftwareUpdate(const nsString& aURL, PRInt32 aFlags, PRInt32* aReturn); \
|
||||
NS_IMETHOD StartSoftwareUpdate(const nsString& aURL, PRInt32* aReturn); \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, PRInt32 aDiffLevel, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn); \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, PRInt32 aDiffLevel, nsIDOMInstallVersion* aVersion, PRInt32 aMode, PRInt32* aReturn); \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32 aMode, PRInt32* aReturn); \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn); \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn); \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn); \
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, PRInt32 aMajor, PRInt32 aMinor, PRInt32 aRelease, PRInt32 aBuild, PRInt32* aReturn); \
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn); \
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn); \
|
||||
|
||||
|
||||
|
||||
#define NS_FORWARD_IDOMINSTALLTRIGGERGLOBAL(_to) \
|
||||
NS_IMETHOD UpdateEnabled(PRBool* aReturn) { return _to##UpdateEnabled(aReturn); } \
|
||||
NS_IMETHOD StartSoftwareUpdate(const nsString& aURL, PRInt32 aFlags, PRInt32* aReturn) { return _to##StartSoftwareUpdate(aURL, aFlags, aReturn); } \
|
||||
NS_IMETHOD StartSoftwareUpdate(const nsString& aURL, PRInt32* aReturn) { return _to##StartSoftwareUpdate(aURL, aReturn); } \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, PRInt32 aDiffLevel, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn) { return _to##ConditionalSoftwareUpdate(aURL, aRegName, aDiffLevel, aVersion, aMode, aReturn); } \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, PRInt32 aDiffLevel, nsIDOMInstallVersion* aVersion, PRInt32 aMode, PRInt32* aReturn) { return _to##ConditionalSoftwareUpdate(aURL, aRegName, aDiffLevel, aVersion, aMode, aReturn); } \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, nsIDOMInstallVersion* aRegName, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn) { return _to##ConditionalSoftwareUpdate(aURL, aDiffLevel, aVersion, aMode, aReturn); } \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn) { return _to##ConditionalSoftwareUpdate(aURL, aDiffLevel, aVersion, aMode, aReturn); } \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn) { return _to##ConditionalSoftwareUpdate(aURL, aDiffLevel, aVersion, aReturn); } \
|
||||
NS_IMETHOD ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn) { return _to##ConditionalSoftwareUpdate(aURL, aDiffLevel, aVersion, aReturn); } \
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, PRInt32 aMajor, PRInt32 aMinor, PRInt32 aRelease, PRInt32 aBuild, PRInt32* aReturn) { return _to##CompareVersion(aRegName, aMajor, aMinor, aRelease, aBuild, aReturn); } \
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn) { return _to##CompareVersion(aRegName, aVersion, aReturn); } \
|
||||
NS_IMETHOD CompareVersion(const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn) { return _to##CompareVersion(aRegName, aVersion, aReturn); } \
|
||||
|
||||
|
||||
extern nsresult NS_InitInstallTriggerGlobalClass(nsIScriptContext *aContext, void **aPrototype);
|
||||
|
||||
extern "C" NS_DOM nsresult NS_NewScriptInstallTriggerGlobal(nsIScriptContext *aContext, nsISupports *aSupports, nsISupports *aParent, void **aReturn);
|
||||
|
||||
#endif // nsIDOMInstallTriggerGlobal_h__
|
||||
107
mozilla/xpinstall/public/nsIDOMInstallVersion.h
Normal file
107
mozilla/xpinstall/public/nsIDOMInstallVersion.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
/* AUTO-GENERATED. DO NOT EDIT!!! */
|
||||
|
||||
#ifndef nsIDOMInstallVersion_h__
|
||||
#define nsIDOMInstallVersion_h__
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIScriptContext.h"
|
||||
|
||||
class nsIDOMInstallVersion;
|
||||
|
||||
#define NS_IDOMINSTALLVERSION_IID \
|
||||
{ 0x18c2f986, 0xb09f, 0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53}}
|
||||
|
||||
class nsIDOMInstallVersion : public nsISupports {
|
||||
public:
|
||||
static const nsIID& IID() { static nsIID iid = NS_IDOMINSTALLVERSION_IID; return iid; }
|
||||
enum {
|
||||
EQUAL = 0,
|
||||
BLD_DIFF = 1,
|
||||
BLD_DIFF_MINUS = -1,
|
||||
REL_DIFF = 2,
|
||||
REL_DIFF_MINUS = -2,
|
||||
MINOR_DIFF = 3,
|
||||
MINOR_DIFF_MINUS = -3,
|
||||
MAJOR_DIFF = 4,
|
||||
MAJOR_DIFF_MINUS = -4
|
||||
};
|
||||
|
||||
NS_IMETHOD GetMajor(PRInt32* aMajor)=0;
|
||||
NS_IMETHOD SetMajor(PRInt32 aMajor)=0;
|
||||
|
||||
NS_IMETHOD GetMinor(PRInt32* aMinor)=0;
|
||||
NS_IMETHOD SetMinor(PRInt32 aMinor)=0;
|
||||
|
||||
NS_IMETHOD GetRelease(PRInt32* aRelease)=0;
|
||||
NS_IMETHOD SetRelease(PRInt32 aRelease)=0;
|
||||
|
||||
NS_IMETHOD GetBuild(PRInt32* aBuild)=0;
|
||||
NS_IMETHOD SetBuild(PRInt32 aBuild)=0;
|
||||
|
||||
NS_IMETHOD Init(const nsString& aVersionString)=0;
|
||||
|
||||
NS_IMETHOD ToString(nsString& aReturn)=0;
|
||||
|
||||
NS_IMETHOD CompareTo(nsIDOMInstallVersion* aVersionObject, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD CompareTo(const nsString& aString, PRInt32* aReturn)=0;
|
||||
NS_IMETHOD CompareTo(PRInt32 aMajor, PRInt32 aMinor, PRInt32 aRelease, PRInt32 aBuild, PRInt32* aReturn)=0;
|
||||
};
|
||||
|
||||
|
||||
#define NS_DECL_IDOMINSTALLVERSION \
|
||||
NS_IMETHOD GetMajor(PRInt32* aMajor); \
|
||||
NS_IMETHOD SetMajor(PRInt32 aMajor); \
|
||||
NS_IMETHOD GetMinor(PRInt32* aMinor); \
|
||||
NS_IMETHOD SetMinor(PRInt32 aMinor); \
|
||||
NS_IMETHOD GetRelease(PRInt32* aRelease); \
|
||||
NS_IMETHOD SetRelease(PRInt32 aRelease); \
|
||||
NS_IMETHOD GetBuild(PRInt32* aBuild); \
|
||||
NS_IMETHOD SetBuild(PRInt32 aBuild); \
|
||||
NS_IMETHOD Init(const nsString& aVersionString); \
|
||||
NS_IMETHOD ToString(nsString& aReturn); \
|
||||
NS_IMETHOD CompareTo(nsIDOMInstallVersion* aVersionObject, PRInt32* aReturn); \
|
||||
NS_IMETHOD CompareTo(const nsString& aString, PRInt32* aReturn); \
|
||||
NS_IMETHOD CompareTo(PRInt32 aMajor, PRInt32 aMinor, PRInt32 aRelease, PRInt32 aBuild, PRInt32* aReturn); \
|
||||
|
||||
|
||||
|
||||
#define NS_FORWARD_IDOMINSTALLVERSION(_to) \
|
||||
NS_IMETHOD GetMajor(PRInt32* aMajor) { return _to##GetMajor(aMajor); } \
|
||||
NS_IMETHOD SetMajor(PRInt32 aMajor) { return _to##SetMajor(aMajor); } \
|
||||
NS_IMETHOD GetMinor(PRInt32* aMinor) { return _to##GetMinor(aMinor); } \
|
||||
NS_IMETHOD SetMinor(PRInt32 aMinor) { return _to##SetMinor(aMinor); } \
|
||||
NS_IMETHOD GetRelease(PRInt32* aRelease) { return _to##GetRelease(aRelease); } \
|
||||
NS_IMETHOD SetRelease(PRInt32 aRelease) { return _to##SetRelease(aRelease); } \
|
||||
NS_IMETHOD GetBuild(PRInt32* aBuild) { return _to##GetBuild(aBuild); } \
|
||||
NS_IMETHOD SetBuild(PRInt32 aBuild) { return _to##SetBuild(aBuild); } \
|
||||
NS_IMETHOD Init(const nsString& aVersionString) { return _to##Init(aVersionString); } \
|
||||
NS_IMETHOD ToString(nsString& aReturn) { return _to##ToString(aReturn); } \
|
||||
NS_IMETHOD CompareTo(nsIDOMInstallVersion* aVersionObject, PRInt32* aReturn) { return _to##CompareTo(aVersionObject, aReturn); } \
|
||||
NS_IMETHOD CompareTo(const nsString& aString, PRInt32* aReturn) { return _to##CompareTo(aString, aReturn); } \
|
||||
NS_IMETHOD CompareTo(PRInt32 aMajor, PRInt32 aMinor, PRInt32 aRelease, PRInt32 aBuild, PRInt32* aReturn) { return _to##CompareTo(aMajor, aMinor, aRelease, aBuild, aReturn); } \
|
||||
|
||||
|
||||
extern nsresult NS_InitInstallVersionClass(nsIScriptContext *aContext, void **aPrototype);
|
||||
|
||||
extern "C" NS_DOM nsresult NS_NewScriptInstallVersion(nsIScriptContext *aContext, nsISupports *aSupports, nsISupports *aParent, void **aReturn);
|
||||
|
||||
#endif // nsIDOMInstallVersion_h__
|
||||
85
mozilla/xpinstall/public/nsISoftwareUpdate.h
Normal file
85
mozilla/xpinstall/public/nsISoftwareUpdate.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
#ifndef nsISoftwareUpdate_h__
|
||||
#define nsISoftwareUpdate_h__
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsIFactory.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "nsIXPInstallProgress.h"
|
||||
|
||||
#define NS_IXPINSTALLCOMPONENT_PROGID NS_IAPPSHELLCOMPONENT_PROGID "/xpinstall"
|
||||
#define NS_IXPINSTALLCOMPONENT_CLASSNAME "Mozilla XPInstall Component"
|
||||
|
||||
|
||||
#define NS_ISOFTWAREUPDATE_IID \
|
||||
{ 0x18c2f992, \
|
||||
0xb09f, \
|
||||
0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53}\
|
||||
}
|
||||
|
||||
|
||||
class nsISoftwareUpdate : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISOFTWAREUPDATE_IID)
|
||||
|
||||
NS_IMETHOD InstallJar(const nsString& fromURL,
|
||||
const nsString& localFile,
|
||||
long flags) = 0;
|
||||
|
||||
NS_IMETHOD RegisterNotifier(nsIXPInstallProgress *notifier) = 0;
|
||||
|
||||
NS_IMETHOD InstallPending(void) = 0;
|
||||
|
||||
/* FIX: these should be in a private interface */
|
||||
NS_IMETHOD InstallJarCallBack() = 0;
|
||||
NS_IMETHOD GetTopLevelNotifier(nsIXPInstallProgress **notifier) = 0;
|
||||
};
|
||||
|
||||
|
||||
class nsSoftwareUpdateFactory : public nsIFactory
|
||||
{
|
||||
public:
|
||||
|
||||
nsSoftwareUpdateFactory();
|
||||
virtual ~nsSoftwareUpdateFactory();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD CreateInstance(nsISupports *aOuter,
|
||||
REFNSIID aIID,
|
||||
void **aResult);
|
||||
|
||||
NS_IMETHOD LockFactory(PRBool aLock);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // nsISoftwareUpdate_h__
|
||||
|
||||
30
mozilla/xpinstall/public/nsIXPInstallProgress.idl
Normal file
30
mozilla/xpinstall/public/nsIXPInstallProgress.idl
Normal file
@@ -0,0 +1,30 @@
|
||||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[uuid(eea90d40-b059-11d2-915e-c12b696c9333)]
|
||||
interface nsIXPInstallProgress : nsISupports
|
||||
{
|
||||
void BeforeJavascriptEvaluation();
|
||||
void AfterJavascriptEvaluation();
|
||||
void InstallStarted([const] in string UIPackageName);
|
||||
void ItemScheduled([const] in string message );
|
||||
void InstallFinalization([const] in string message, in long itemNum, in long totNum );
|
||||
void InstallAborted();
|
||||
};
|
||||
93
mozilla/xpinstall/public/nsIXPInstallProgressNotifier.h
Normal file
93
mozilla/xpinstall/public/nsIXPInstallProgressNotifier.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef nsIXPInstallProgressNotifier_h__
|
||||
#define nsIXPInstallProgressNotifier_h__
|
||||
|
||||
|
||||
class nsIXPInstallProgressNotifier
|
||||
{
|
||||
public:
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function name : BeforeJavascriptEvaluation
|
||||
// Description : This will be called when prior to the install script being evaluate
|
||||
// Return type : void
|
||||
// Argument : void
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void BeforeJavascriptEvaluation(void) = 0;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function name : AfterJavascriptEvaluation
|
||||
// Description : This will be called after the install script has being evaluated
|
||||
// Return type : void
|
||||
// Argument : void
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void AfterJavascriptEvaluation(void) = 0;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function name : InstallStarted
|
||||
// Description : This will be called when StartInstall has been called
|
||||
// Return type : void
|
||||
// Argument : char* UIPackageName - User Package Name
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void InstallStarted(const char* UIPackageName) = 0;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function name : ItemScheduled
|
||||
// Description : This will be called when items are being scheduled
|
||||
// Return type : Any value returned other than zero, will be treated as an error and the script will be aborted
|
||||
// Argument : The message that should be displayed to the user
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual long ItemScheduled( const char* message ) = 0;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function name : InstallFinalization
|
||||
// Description : This will be called when the installation is in its Finalize stage
|
||||
// Return type : void
|
||||
// Argument : char* message - The message that should be displayed to the user
|
||||
// Argument : long itemNum - This is the current item number
|
||||
// Argument : long totNum - This is the total number of items
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void InstallFinalization( const char* message, long itemNum, long totNum ) = 0;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function name : InstallAborted
|
||||
// Description : This will be called when the install is aborted
|
||||
// Return type : void
|
||||
// Argument : void
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void InstallAborted(void) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
64
mozilla/xpinstall/public/nsSoftwareUpdateIIDs.h
Normal file
64
mozilla/xpinstall/public/nsSoftwareUpdateIIDs.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
#ifndef nsSoftwareUpdateIIDs_h___
|
||||
#define nsSoftwareUpdateIIDs_h___
|
||||
|
||||
#define NS_SoftwareUpdate_CID \
|
||||
{ /* 18c2f989-b09f-11d2-bcde-00805f0e1353 */ \
|
||||
0x18c2f989, \
|
||||
0xb09f, \
|
||||
0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53} \
|
||||
}
|
||||
|
||||
#define NS_SoftwareUpdateInstall_CID \
|
||||
{ /* 18c2f98b-b09f-11d2-bcde-00805f0e1353 */ \
|
||||
0x18c2f98b, \
|
||||
0xb09f, \
|
||||
0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53} \
|
||||
}
|
||||
|
||||
#define NS_SoftwareUpdateInstallTrigger_CID \
|
||||
{ /* 18c2f98d-b09f-11d2-bcde-00805f0e1353 */ \
|
||||
0x18c2f98d, \
|
||||
0xb09f, \
|
||||
0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53} \
|
||||
}
|
||||
|
||||
#define NS_SoftwareUpdateInstallVersion_CID \
|
||||
{ /* 18c2f98f-b09f-11d2-bcde-00805f0e1353 */ \
|
||||
0x18c2f98f, \
|
||||
0xb09f, \
|
||||
0x11d2, \
|
||||
{0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53} \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* nsSoftwareUpdateIIDs_h___ */
|
||||
|
||||
3
mozilla/xpinstall/res/MANIFEST
Normal file
3
mozilla/xpinstall/res/MANIFEST
Normal file
@@ -0,0 +1,3 @@
|
||||
progress.xul
|
||||
progress.css
|
||||
progress.html
|
||||
34
mozilla/xpinstall/res/Makefile.in
Normal file
34
mozilla/xpinstall/res/Makefile.in
Normal file
@@ -0,0 +1,34 @@
|
||||
#!gmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "NPL"); you may not use this file except in
|
||||
# compliance with the NPL. You may obtain a copy of the NPL at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
# for the specific language governing rights and limitations under the
|
||||
# NPL.
|
||||
#
|
||||
# The Initial Developer of this code under the NPL is Netscape
|
||||
# Communications Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
|
||||
DEPTH=../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/config.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
EXPORT_RESOURCE_XPINSTALL = \
|
||||
$(srcdir)/progress.xul \
|
||||
$(srcdir)/progress.html \
|
||||
$(srcdir)/progress.css \
|
||||
$(NULL)
|
||||
|
||||
install::
|
||||
$(INSTALL) $(EXPORT_RESOURCE_XPINSTALL) $(DIST)/bin/res/xpinstall
|
||||
31
mozilla/xpinstall/res/makefile.win
Normal file
31
mozilla/xpinstall/res/makefile.win
Normal file
@@ -0,0 +1,31 @@
|
||||
#!nmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "NPL"); you may not use this file except in
|
||||
# compliance with the NPL. You may obtain a copy of the NPL at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
# for the specific language governing rights and limitations under the
|
||||
# NPL.
|
||||
#
|
||||
# The Initial Developer of this code under the NPL is Netscape
|
||||
# Communications Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
|
||||
DEPTH=..\..
|
||||
IGNORE_MANIFEST=1
|
||||
|
||||
include <$(DEPTH)\config\rules.mak>
|
||||
|
||||
install:: $(DLL)
|
||||
$(MAKE_INSTALL) progress.xul $(DIST)\bin\res\xpinstall
|
||||
$(MAKE_INSTALL) progress.css $(DIST)\bin\res\xpinstall
|
||||
$(MAKE_INSTALL) progress.html $(DIST)\bin\res\xpinstall
|
||||
|
||||
clobber::
|
||||
rm -f $(DIST)\res\xpinstall\progress.xul
|
||||
rm -f $(DIST)\res\xpinstall\progress.css
|
||||
rm -f $(DIST)\res\xpinstall\progress.html
|
||||
3
mozilla/xpinstall/res/progress.css
Normal file
3
mozilla/xpinstall/res/progress.css
Normal file
@@ -0,0 +1,3 @@
|
||||
TD {
|
||||
font: 10pt sans-serif;
|
||||
}
|
||||
16
mozilla/xpinstall/res/progress.html
Normal file
16
mozilla/xpinstall/res/progress.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
|
||||
<html>
|
||||
<body bgcolor="#C0C0C0" style="overflow:visible; margin: 0px; color-background: rgb(192,192,192);">
|
||||
<center>
|
||||
<table BORDER COLS=5 WIDTH="99%" style="color-background:rgb(192,192,192);">
|
||||
<tr>
|
||||
<td WIDTH="3%" NOWRAP style="border: 1px inset rgb(192,192,192);"> </td>
|
||||
<td WIDTH="3%" NOWRAP style="border: 1px inset rgb(192,192,192);"> </td>
|
||||
<td WIDTH="3%" NOWRAP style="border: 1px inset rgb(192,192,192);"> </td>
|
||||
<td WIDTH="10%" NOWRAP style="border: 1px inset rgb(192,192,192);"> </td>
|
||||
<td WIDTH="81%" NOWRAP style="border: 1px inset rgb(192,192,192);"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
67
mozilla/xpinstall/res/progress.xul
Normal file
67
mozilla/xpinstall/res/progress.xul
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="../samples/xul.css" type="text/css"?>
|
||||
<?xml-stylesheet href="progress.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window
|
||||
[
|
||||
<!ENTITY downloadWindow.title "XPInstall Progress">
|
||||
<!ENTITY status "Status:">
|
||||
<!ENTITY cancelButtonTitle "Cancel">
|
||||
]
|
||||
>
|
||||
|
||||
<window xmlns:html="http://www.w3.org/TR/REC-html40"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="XPInstall Progress"
|
||||
width="425"
|
||||
height="225">
|
||||
|
||||
<data>
|
||||
<broadcaster id="data.canceled" type="string" value="false"/>
|
||||
</data>
|
||||
|
||||
|
||||
<html:script>
|
||||
|
||||
function cancelInstall()
|
||||
{
|
||||
var cancelData = document.getElementById("data.canceled");
|
||||
cancelData.setAttribute( "value", "true");
|
||||
}
|
||||
|
||||
</html:script>
|
||||
|
||||
<html:center>
|
||||
<html:table style="width:100%;">
|
||||
|
||||
<html:tr>
|
||||
<html:td align="center">
|
||||
<html:input id="dialog.uiPackageName" readonly="" style="background-color:lightgray;width:300px;"/>
|
||||
</html:td>
|
||||
</html:tr>
|
||||
|
||||
<html:tr>
|
||||
<html:td nowrap="" style="border: 1px rgb(192,192,192);" align="center">
|
||||
<html:input id="dialog.currentAction" readonly="" style="background-color:lightgray;width:450px;"/>
|
||||
</html:td>
|
||||
</html:tr>
|
||||
|
||||
|
||||
<html:tr>
|
||||
<html:td align="center" width="15%" nowrap="" style="border: 1px rgb(192,192,192);">
|
||||
<progressmeter id="dialog.progress" mode="undetermined" style="width:300px;height:16px;">
|
||||
</progressmeter>
|
||||
</html:td>
|
||||
</html:tr>
|
||||
|
||||
<html:tr>
|
||||
<html:td align="center" width="3%" nowrap="" style="border: 1px rgb(192,192,192);">
|
||||
<html:button onclick="cancelInstall()" height="12">
|
||||
&cancelButtonTitle;
|
||||
</html:button>
|
||||
</html:td>
|
||||
</html:tr>
|
||||
</html:table>
|
||||
|
||||
</html:center>
|
||||
</window>
|
||||
61
mozilla/xpinstall/src/Makefile.in
Normal file
61
mozilla/xpinstall/src/Makefile.in
Normal file
@@ -0,0 +1,61 @@
|
||||
#!gmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Communicator client code,
|
||||
# released March 31, 1998.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
#
|
||||
# Contributors:
|
||||
# Daniel Veditz <dveditz@netscape.com>
|
||||
# Douglas Turner <dougt@netscape.com>
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
srcdir = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = xpinstall
|
||||
LIBRARY_NAME = xpinstall
|
||||
IS_COMPONENT = 1
|
||||
REQUIRES = dom js netlib raptor xpcom
|
||||
|
||||
CPPSRCS = \
|
||||
nsSoftwareUpdate.cpp \
|
||||
nsInstall.cpp \
|
||||
nsInstallDelete.cpp \
|
||||
nsInstallExecute.cpp \
|
||||
nsInstallFile.cpp \
|
||||
nsInstallFolder.cpp \
|
||||
nsInstallPatch.cpp \
|
||||
nsInstallUninstall.cpp \
|
||||
nsInstallTrigger.cpp \
|
||||
nsInstallResources.cpp \
|
||||
nsJSInstall.cpp \
|
||||
nsJSInstallTriggerGlobal.cpp\
|
||||
nsSoftwareUpdateRun.cpp \
|
||||
nsSoftwareUpdateStream.cpp \
|
||||
nsTopProgressNotifier.cpp \
|
||||
nsLoggingProgressNotifier \
|
||||
ScheduledTasks.cpp \
|
||||
nsInstallFileOpItem.cpp \
|
||||
$(NULL)
|
||||
|
||||
INCLUDES += -I$(srcdir)/../public
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
1854
mozilla/xpinstall/src/PatchableAppleSingle.cpp
Normal file
1854
mozilla/xpinstall/src/PatchableAppleSingle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
233
mozilla/xpinstall/src/PatchableAppleSingle.h
Normal file
233
mozilla/xpinstall/src/PatchableAppleSingle.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
/*
|
||||
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
|
||||
* http://www.mozilla.org/NPL/
|
||||
|
||||
*
|
||||
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
|
||||
* for the specific language governing rights and limitations under the
|
||||
|
||||
* License.
|
||||
|
||||
*
|
||||
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
|
||||
* released March 31, 1998.
|
||||
|
||||
*
|
||||
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
|
||||
* Corporation. Portions created by Netscape are
|
||||
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
|
||||
* Reserved.
|
||||
|
||||
*
|
||||
|
||||
* Contributors:
|
||||
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef SU_PAS_H
|
||||
|
||||
#define SU_PAS_H
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <Errors.h>
|
||||
|
||||
#include <Types.h>
|
||||
|
||||
#include <Files.h>
|
||||
|
||||
#include <Script.h>
|
||||
|
||||
#include <Resources.h>
|
||||
|
||||
|
||||
|
||||
typedef struct PASHeader /* header portion of Patchable AppleSingle */
|
||||
|
||||
{
|
||||
|
||||
UInt32 magicNum; /* internal file type tag = 0x00244200*/
|
||||
|
||||
UInt32 versionNum; /* format version: 1 = 0x00010000 */
|
||||
|
||||
UInt8 filler[16]; /* filler */
|
||||
|
||||
UInt16 numEntries; /* number of entries which follow */
|
||||
|
||||
} PASHeader ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct PASEntry /* one Patchable AppleSingle entry descriptor */
|
||||
|
||||
{
|
||||
|
||||
UInt32 entryID; /* entry type: see list, 0 invalid */
|
||||
|
||||
UInt32 entryOffset; /* offset, in bytes, from beginning */
|
||||
|
||||
/* of file to this entry's data */
|
||||
|
||||
UInt32 entryLength; /* length of data in octets */
|
||||
|
||||
|
||||
|
||||
} PASEntry;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct PASMiscInfo
|
||||
|
||||
{
|
||||
|
||||
short fileHasResFork;
|
||||
|
||||
short fileResAttrs;
|
||||
|
||||
OSType fileType;
|
||||
|
||||
OSType fileCreator;
|
||||
|
||||
UInt32 fileFlags;
|
||||
|
||||
|
||||
|
||||
} PASMiscInfo;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct PASResFork
|
||||
|
||||
{
|
||||
|
||||
short NumberOfTypes;
|
||||
|
||||
|
||||
|
||||
} PASResFork;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct PASResource
|
||||
|
||||
{
|
||||
|
||||
short attr;
|
||||
|
||||
short attrID;
|
||||
|
||||
OSType attrType;
|
||||
|
||||
Str255 attrName;
|
||||
|
||||
unsigned long length;
|
||||
|
||||
|
||||
|
||||
} PASResource;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if PRAGMA_ALIGN_SUPPORTED
|
||||
|
||||
#pragma options align=reset
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define kCreator 'MOSS'
|
||||
|
||||
#define kType 'PASf'
|
||||
|
||||
#define PAS_BUFFER_SIZE (1024*512)
|
||||
|
||||
|
||||
|
||||
#define PAS_MAGIC_NUM (0x00244200)
|
||||
|
||||
#define PAS_VERSION (0x00010000)
|
||||
|
||||
|
||||
|
||||
enum
|
||||
|
||||
{
|
||||
|
||||
ePas_Data = 1,
|
||||
|
||||
ePas_Misc,
|
||||
|
||||
ePas_Resource
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
extern "C" {
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
OSErr PAS_EncodeFile(FSSpec *inSpec, FSSpec *outSpec);
|
||||
|
||||
OSErr PAS_DecodeFile(FSSpec *inSpec, FSSpec *outSpec);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* SU_PAS_H */
|
||||
380
mozilla/xpinstall/src/ScheduledTasks.cpp
Normal file
380
mozilla/xpinstall/src/ScheduledTasks.cpp
Normal file
@@ -0,0 +1,380 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
#include "nscore.h"
|
||||
#include "NSReg.h"
|
||||
#include "nsFileSpec.h"
|
||||
#include "nsFileStream.h"
|
||||
#include "nsInstall.h" // for error codes
|
||||
#include "prmem.h"
|
||||
#include "ScheduledTasks.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <sys/stat.h>
|
||||
#include <windows.h>
|
||||
|
||||
BOOL WIN32_IsMoveFileExBroken()
|
||||
{
|
||||
/* the NT option MOVEFILE_DELAY_UNTIL_REBOOT is broken on
|
||||
* Windows NT 3.51 Service Pack 4 and NT 4.0 before Service Pack 2
|
||||
*/
|
||||
BOOL broken = FALSE;
|
||||
OSVERSIONINFO osinfo;
|
||||
|
||||
// they *all* appear broken--better to have one way that works.
|
||||
return TRUE;
|
||||
|
||||
osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
if (GetVersionEx(&osinfo) && osinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
||||
{
|
||||
if ( osinfo.dwMajorVersion == 3 && osinfo.dwMinorVersion == 51 )
|
||||
{
|
||||
if ( 0 == stricmp(osinfo.szCSDVersion,"Service Pack 4"))
|
||||
{
|
||||
broken = TRUE;
|
||||
}
|
||||
}
|
||||
else if ( osinfo.dwMajorVersion == 4 )
|
||||
{
|
||||
if (osinfo.szCSDVersion[0] == '\0' ||
|
||||
(0 == stricmp(osinfo.szCSDVersion,"Service Pack 1")))
|
||||
{
|
||||
broken = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return broken;
|
||||
}
|
||||
|
||||
|
||||
PRInt32 DoWindowsReplaceExistingFileStuff(const char* currentName, const char* finalName)
|
||||
{
|
||||
PRInt32 err = 0;
|
||||
|
||||
char* final = strdup(finalName);
|
||||
char* current = strdup(currentName);
|
||||
|
||||
/* couldn't delete, probably in use. Schedule for later */
|
||||
DWORD dwVersion, dwWindowsMajorVersion;
|
||||
|
||||
/* Get OS version info */
|
||||
dwVersion = GetVersion();
|
||||
dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
|
||||
|
||||
/* Get build numbers for Windows NT or Win32s */
|
||||
|
||||
if (dwVersion < 0x80000000) // Windows NT
|
||||
{
|
||||
/* On Windows NT */
|
||||
if ( WIN32_IsMoveFileExBroken() )
|
||||
{
|
||||
/* the MOVEFILE_DELAY_UNTIL_REBOOT option doesn't work on
|
||||
* NT 3.51 SP4 or on NT 4.0 until SP2
|
||||
*/
|
||||
struct stat statbuf;
|
||||
PRBool nameFound = PR_FALSE;
|
||||
char tmpname[_MAX_PATH];
|
||||
|
||||
strncpy( tmpname, finalName, _MAX_PATH );
|
||||
int len = strlen(tmpname);
|
||||
while (!nameFound && len < _MAX_PATH )
|
||||
{
|
||||
tmpname[len-1] = '~';
|
||||
tmpname[len] = '\0';
|
||||
if ( stat(tmpname, &statbuf) != 0 )
|
||||
nameFound = TRUE;
|
||||
else
|
||||
len++;
|
||||
}
|
||||
|
||||
if ( nameFound )
|
||||
{
|
||||
if ( MoveFile( finalName, tmpname ) )
|
||||
{
|
||||
if ( MoveFile( currentName, finalName ) )
|
||||
{
|
||||
DeleteFileNowOrSchedule(nsFileSpec(tmpname));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 2nd move failed, put old file back */
|
||||
MoveFile( tmpname, finalName );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* non-executable in use; schedule for later */
|
||||
return -1; // let the start registry stuff do our work!
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( MoveFileEx(currentName, finalName, MOVEFILE_DELAY_UNTIL_REBOOT) )
|
||||
{
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
else // Windows 95 or Win16
|
||||
{
|
||||
/*
|
||||
* Place an entry in the WININIT.INI file in the Windows directory
|
||||
* to delete finalName and rename currentName to be finalName at reboot
|
||||
*/
|
||||
|
||||
int strlen;
|
||||
char Src[_MAX_PATH]; // 8.3 name
|
||||
char Dest[_MAX_PATH]; // 8.3 name
|
||||
|
||||
strlen = GetShortPathName( (LPCTSTR)currentName, (LPTSTR)Src, (DWORD)sizeof(Src) );
|
||||
if ( strlen > 0 )
|
||||
{
|
||||
free(current);
|
||||
current = strdup(Src);
|
||||
}
|
||||
|
||||
strlen = GetShortPathName( (LPCTSTR) finalName, (LPTSTR) Dest, (DWORD) sizeof(Dest));
|
||||
if ( strlen > 0 )
|
||||
{
|
||||
free(final);
|
||||
final = strdup(Dest);
|
||||
}
|
||||
|
||||
/* NOTE: use OEM filenames! Even though it looks like a Windows
|
||||
* .INI file, WININIT.INI is processed under DOS
|
||||
*/
|
||||
|
||||
AnsiToOem( final, final );
|
||||
AnsiToOem( current, current );
|
||||
|
||||
if ( WritePrivateProfileString( "Rename", final, current, "WININIT.INI" ) )
|
||||
err = 0;
|
||||
}
|
||||
|
||||
free(final);
|
||||
free(current);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
REGERR DeleteFileNowOrSchedule(nsFileSpec& filename)
|
||||
{
|
||||
|
||||
REGERR result = 0;
|
||||
|
||||
filename.Delete(false);
|
||||
|
||||
if (filename.Exists())
|
||||
{
|
||||
RKEY newkey;
|
||||
HREG reg;
|
||||
if ( REGERR_OK == NR_RegOpen("", ®) )
|
||||
{
|
||||
if (REGERR_OK == NR_RegAddKey( reg, ROOTKEY_PRIVATE, REG_DELETE_LIST_KEY, &newkey) )
|
||||
{
|
||||
// FIX should be using nsPersistentFileDescriptor!!!
|
||||
|
||||
result = NR_RegSetEntry( reg, newkey, (char*)(const char*)filename.GetNativePathCString(), REGTYPE_ENTRY_FILE, nsnull, 0);
|
||||
if (result == REGERR_OK)
|
||||
result = nsInstall::REBOOT_NEEDED;
|
||||
}
|
||||
|
||||
NR_RegClose(reg);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
/* tmp file is the bad one that we want to replace with target. */
|
||||
|
||||
REGERR ReplaceFileNowOrSchedule(nsFileSpec& replacementFile, nsFileSpec& doomedFile )
|
||||
{
|
||||
REGERR result = 0;
|
||||
|
||||
if(replacementFile == doomedFile)
|
||||
{
|
||||
/* do not have to do anything */
|
||||
return result;
|
||||
}
|
||||
|
||||
doomedFile.Delete(false);
|
||||
|
||||
if (! doomedFile.Exists() )
|
||||
{
|
||||
// Now that we have move the existing file, we can move the mExtracedFile into place.
|
||||
nsFileSpec parentofFinalFile;
|
||||
|
||||
doomedFile.GetParent(parentofFinalFile);
|
||||
result = replacementFile.Move(parentofFinalFile);
|
||||
if ( NS_SUCCEEDED(result) )
|
||||
{
|
||||
char* leafName = doomedFile.GetLeafName();
|
||||
replacementFile.Rename(leafName);
|
||||
nsCRT::free(leafName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WINDOWS
|
||||
if (DoWindowsReplaceExistingFileStuff(replacementFile.GetNativePathCString(), doomedFile.GetNativePathCString()) == 0)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
RKEY newkey;
|
||||
HREG reg;
|
||||
|
||||
if ( REGERR_OK == NR_RegOpen("", ®) )
|
||||
{
|
||||
result = NR_RegAddKey( reg, ROOTKEY_PRIVATE, REG_REPLACE_LIST_KEY, &newkey);
|
||||
if ( result == REGERR_OK )
|
||||
{
|
||||
char* replacementFileName = (char*)(const char*)replacementFile.GetNativePathCString();
|
||||
result = NR_RegSetEntry( reg, newkey, (char*)(const char*)doomedFile.GetNativePathCString(), REGTYPE_ENTRY_FILE, replacementFileName, strlen(replacementFileName));
|
||||
if (result == REGERR_OK)
|
||||
result = nsInstall::REBOOT_NEEDED;
|
||||
}
|
||||
|
||||
NR_RegClose(reg);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DeleteScheduledFiles(void);
|
||||
void ReplaceScheduledFiles(void);
|
||||
|
||||
extern "C" void PerformScheduledTasks(void *data)
|
||||
{
|
||||
DeleteScheduledFiles();
|
||||
ReplaceScheduledFiles();
|
||||
}
|
||||
|
||||
|
||||
void DeleteScheduledFiles(void)
|
||||
{
|
||||
HREG reg;
|
||||
|
||||
if (REGERR_OK == NR_RegOpen("", ®))
|
||||
{
|
||||
RKEY key;
|
||||
REGENUM state;
|
||||
|
||||
/* perform scheduled file deletions and replacements (PC only) */
|
||||
if (REGERR_OK == NR_RegGetKey(reg, ROOTKEY_PRIVATE, REG_DELETE_LIST_KEY,&key))
|
||||
{
|
||||
char buf[MAXREGNAMELEN];
|
||||
|
||||
while (REGERR_OK == NR_RegEnumEntries(reg, key, &state, buf, sizeof(buf), NULL ))
|
||||
{
|
||||
nsFileSpec doomedFile(buf);
|
||||
|
||||
doomedFile.Delete(PR_FALSE);
|
||||
|
||||
if (! doomedFile.Exists())
|
||||
{
|
||||
NR_RegDeleteEntry( reg, key, buf );
|
||||
}
|
||||
}
|
||||
|
||||
/* delete list node if empty */
|
||||
if (REGERR_NOMORE == NR_RegEnumEntries( reg, key, &state, buf, sizeof(buf), NULL ))
|
||||
{
|
||||
NR_RegDeleteKey(reg, ROOTKEY_PRIVATE, REG_DELETE_LIST_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
NR_RegClose(reg);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceScheduledFiles(void)
|
||||
{
|
||||
HREG reg;
|
||||
|
||||
if (REGERR_OK == NR_RegOpen("", ®))
|
||||
{
|
||||
RKEY key;
|
||||
REGENUM state;
|
||||
|
||||
/* replace files if any listed */
|
||||
if (REGERR_OK == NR_RegGetKey(reg, ROOTKEY_PRIVATE, REG_REPLACE_LIST_KEY, &key))
|
||||
{
|
||||
char tmpfile[MAXREGNAMELEN];
|
||||
char target[MAXREGNAMELEN];
|
||||
|
||||
state = 0;
|
||||
while (REGERR_OK == NR_RegEnumEntries(reg, key, &state, tmpfile, sizeof(tmpfile), NULL ))
|
||||
{
|
||||
|
||||
nsFileSpec replaceFile(tmpfile);
|
||||
|
||||
if (! replaceFile.Exists() )
|
||||
{
|
||||
NR_RegDeleteEntry( reg, key, tmpfile );
|
||||
}
|
||||
else if ( REGERR_OK != NR_RegGetEntryString( reg, key, tmpfile, target, sizeof(target) ) )
|
||||
{
|
||||
/* can't read target filename, corruption? */
|
||||
NR_RegDeleteEntry( reg, key, tmpfile );
|
||||
}
|
||||
else
|
||||
{
|
||||
nsFileSpec targetFile(target);
|
||||
|
||||
targetFile.Delete(PR_FALSE);
|
||||
|
||||
if (!targetFile.Exists())
|
||||
{
|
||||
nsFileSpec parentofTarget;
|
||||
targetFile.GetParent(parentofTarget);
|
||||
|
||||
nsresult result = replaceFile.Move(parentofTarget);
|
||||
if ( NS_SUCCEEDED(result) )
|
||||
{
|
||||
char* leafName = targetFile.GetLeafName();
|
||||
replaceFile.Rename(leafName);
|
||||
nsCRT::free(leafName);
|
||||
|
||||
NR_RegDeleteEntry( reg, key, tmpfile );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* delete list node if empty */
|
||||
if (REGERR_NOMORE == NR_RegEnumEntries(reg, key, &state, tmpfile, sizeof(tmpfile), NULL ))
|
||||
{
|
||||
NR_RegDeleteKey(reg, ROOTKEY_PRIVATE, REG_REPLACE_LIST_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
NR_RegClose(reg);
|
||||
}
|
||||
}
|
||||
42
mozilla/xpinstall/src/ScheduledTasks.h
Normal file
42
mozilla/xpinstall/src/ScheduledTasks.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __SCHEDULEDTASKS_H__
|
||||
#define __SCHEDULEDTASKS_H__
|
||||
|
||||
|
||||
#include "NSReg.h"
|
||||
#include "nsFileSpec.h"
|
||||
|
||||
|
||||
REGERR DeleteFileNowOrSchedule(nsFileSpec& filename);
|
||||
REGERR ReplaceFileNowOrSchedule(nsFileSpec& tmpfile, nsFileSpec& target );
|
||||
|
||||
|
||||
extern "C" void PerformScheduledTasks(void *data);
|
||||
|
||||
|
||||
#endif
|
||||
135
mozilla/xpinstall/src/gdiff.h
Normal file
135
mozilla/xpinstall/src/gdiff.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
/*--------------------------------------------------------------
|
||||
* GDIFF.H
|
||||
*
|
||||
* Constants used in processing the GDIFF format
|
||||
*--------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "prio.h"
|
||||
#include "nsFileSpec.h"
|
||||
|
||||
#define GDIFF_MAGIC "\xD1\xFF\xD1\xFF"
|
||||
#define GDIFF_MAGIC_LEN 4
|
||||
#define GDIFF_VER 5
|
||||
#define GDIFF_EOF "\0"
|
||||
|
||||
#define GDIFF_VER_POS 4
|
||||
#define GDIFF_CS_POS 5
|
||||
#define GDIFF_CSLEN_POS 6
|
||||
|
||||
#define GDIFF_HEADERSIZE 7
|
||||
#define GDIFF_APPDATALEN 4
|
||||
|
||||
#define GDIFF_CS_NONE 0
|
||||
#define GDIFF_CS_MD5 1
|
||||
#define GDIFF_CS_SHA 2
|
||||
#define GDIFF_CS_CRC32 32
|
||||
|
||||
#define CRC32_LEN 4
|
||||
|
||||
/*--------------------------------------
|
||||
* GDIFF opcodes
|
||||
*------------------------------------*/
|
||||
#define ENDDIFF 0
|
||||
#define ADD8MAX 246
|
||||
#define ADD16 247
|
||||
#define ADD32 248
|
||||
#define COPY16BYTE 249
|
||||
#define COPY16SHORT 250
|
||||
#define COPY16LONG 251
|
||||
#define COPY32BYTE 252
|
||||
#define COPY32SHORT 253
|
||||
#define COPY32LONG 254
|
||||
#define COPY64 255
|
||||
|
||||
/* instruction sizes */
|
||||
#define ADD16SIZE 2
|
||||
#define ADD32SIZE 4
|
||||
#define COPY16BYTESIZE 3
|
||||
#define COPY16SHORTSIZE 4
|
||||
#define COPY16LONGSIZE 6
|
||||
#define COPY32BYTESIZE 5
|
||||
#define COPY32SHORTSIZE 6
|
||||
#define COPY32LONGSIZE 8
|
||||
#define COPY64SIZE 12
|
||||
|
||||
|
||||
/*--------------------------------------
|
||||
* error codes
|
||||
*------------------------------------*/
|
||||
#define GDIFF_OK 0
|
||||
#define GDIFF_ERR_UNKNOWN -1
|
||||
#define GDIFF_ERR_ARGS -2
|
||||
#define GDIFF_ERR_ACCESS -3
|
||||
#define GDIFF_ERR_MEM -4
|
||||
#define GDIFF_ERR_HEADER -5
|
||||
#define GDIFF_ERR_BADDIFF -6
|
||||
#define GDIFF_ERR_OPCODE -7
|
||||
#define GDIFF_ERR_OLDFILE -8
|
||||
#define GDIFF_ERR_CHKSUMTYPE -9
|
||||
#define GDIFF_ERR_CHECKSUM -10
|
||||
#define GDIFF_ERR_CHECKSUM_TARGET -11
|
||||
#define GDIFF_ERR_CHECKSUM_RESULT -12
|
||||
|
||||
|
||||
/*--------------------------------------
|
||||
* types
|
||||
*------------------------------------*/
|
||||
#ifndef AIX
|
||||
#ifdef OSF1
|
||||
#include <sys/types.h>
|
||||
#else
|
||||
typedef unsigned char uchar;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct _diffdata {
|
||||
PRFileDesc* fSrc;
|
||||
PRFileDesc* fOut;
|
||||
PRFileDesc* fDiff;
|
||||
uint8 checksumType;
|
||||
uint8 checksumLength;
|
||||
uchar* oldChecksum;
|
||||
uchar* newChecksum;
|
||||
PRBool bMacAppleSingle;
|
||||
PRBool bWin32BoundImage;
|
||||
uchar* databuf;
|
||||
uint32 bufsize;
|
||||
} DIFFDATA;
|
||||
|
||||
typedef DIFFDATA* pDIFFDATA;
|
||||
|
||||
|
||||
/*--------------------------------------
|
||||
* miscellaneous
|
||||
*------------------------------------*/
|
||||
|
||||
#define APPFLAG_W32BOUND "autoinstall:Win32PE"
|
||||
#define APPFLAG_APPLESINGLE "autoinstall:AppleSingle"
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
|
||||
111
mozilla/xpinstall/src/makefile.win
Normal file
111
mozilla/xpinstall/src/makefile.win
Normal file
@@ -0,0 +1,111 @@
|
||||
#!nmake
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public License
|
||||
# Version 1.0 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Communicator client code,
|
||||
# released March 31, 1998.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
# Reserved.
|
||||
#
|
||||
# Contributors:
|
||||
# Daniel Veditz <dveditz@netscape.com>
|
||||
# Douglas Turner <dougt@netscape.com>
|
||||
|
||||
|
||||
DEPTH=..\..
|
||||
IGNORE_MANIFEST=1
|
||||
|
||||
MAKE_OBJ_TYPE = DLL
|
||||
MODULE=xpinstall
|
||||
|
||||
DLL=.\$(OBJDIR)\$(MODULE).dll
|
||||
|
||||
DEFINES=-D_IMPL_NS_DOM -DWIN32_LEAN_AND_MEAN
|
||||
|
||||
LCFLAGS = \
|
||||
$(LCFLAGS) \
|
||||
$(DEFINES) \
|
||||
$(NULL)
|
||||
|
||||
LINCS= \
|
||||
-I$(PUBLIC)\xpinstall \
|
||||
-I$(PUBLIC)\jar \
|
||||
-I$(PUBLIC)\libreg \
|
||||
-I$(PUBLIC)\netlib \
|
||||
-I$(PUBLIC)\xpcom \
|
||||
-I$(PUBLIC)\pref \
|
||||
-I$(PUBLIC)\rdf \
|
||||
-I$(PUBLIC)\js \
|
||||
-I$(PUBLIC)\dom \
|
||||
-I$(PUBLIC)\raptor \
|
||||
-I$(PUBLIC)\nspr2 \
|
||||
-I$(PUBLIC)\zlib \
|
||||
-I$(PUBLIC)\xpfe\components \
|
||||
$(NULL)
|
||||
|
||||
LLIBS = \
|
||||
$(DIST)\lib\jar50.lib \
|
||||
$(DIST)\lib\libreg32.lib \
|
||||
$(DIST)\lib\netlib.lib \
|
||||
$(DIST)\lib\xpcom.lib \
|
||||
$(DIST)\lib\js3250.lib \
|
||||
$(DIST)\lib\jsdombase_s.lib \
|
||||
$(DIST)\lib\jsdomevents_s.lib \
|
||||
$(DIST)\lib\zlib.lib \
|
||||
$(DIST)\lib\plc3.lib \
|
||||
$(LIBNSPR) \
|
||||
$(NULL)
|
||||
|
||||
|
||||
OBJS = \
|
||||
.\$(OBJDIR)\nsInstall.obj \
|
||||
.\$(OBJDIR)\nsInstallTrigger.obj \
|
||||
.\$(OBJDIR)\nsInstallVersion.obj \
|
||||
.\$(OBJDIR)\nsInstallFolder.obj \
|
||||
.\$(OBJDIR)\nsJSInstall.obj \
|
||||
.\$(OBJDIR)\nsJSInstallTriggerGlobal.obj \
|
||||
.\$(OBJDIR)\nsJSInstallVersion.obj \
|
||||
.\$(OBJDIR)\nsSoftwareUpdate.obj \
|
||||
.\$(OBJDIR)\nsSoftwareUpdateRun.obj \
|
||||
.\$(OBJDIR)\nsSoftwareUpdateStream.obj \
|
||||
.\$(OBJDIR)\nsInstallFile.obj \
|
||||
.\$(OBJDIR)\nsInstallDelete.obj \
|
||||
.\$(OBJDIR)\nsInstallExecute.obj \
|
||||
.\$(OBJDIR)\nsInstallPatch.obj \
|
||||
.\$(OBJDIR)\nsInstallUninstall.obj \
|
||||
.\$(OBJDIR)\nsInstallResources.obj \
|
||||
.\$(OBJDIR)\nsTopProgressNotifier.obj \
|
||||
.\$(OBJDIR)\nsLoggingProgressNotifier.obj\
|
||||
.\$(OBJDIR)\ScheduledTasks.obj \
|
||||
.\$(OBJDIR)\nsWinReg.obj \
|
||||
.\$(OBJDIR)\nsJSWinReg.obj \
|
||||
.\$(OBJDIR)\nsWinRegItem.obj \
|
||||
.\$(OBJDIR)\nsWinRegValue.obj \
|
||||
.\$(OBJDIR)\nsWinProfile.obj \
|
||||
.\$(OBJDIR)\nsJSWinProfile.obj \
|
||||
.\$(OBJDIR)\nsWinProfileItem.obj \
|
||||
.\$(OBJDIR)\nsInstallProgressDialog.obj \
|
||||
.\$(OBJDIR)\nsInstallFileOpItem.obj \
|
||||
$(NULL)
|
||||
|
||||
include <$(DEPTH)\config\rules.mak>
|
||||
|
||||
install:: $(DLL)
|
||||
$(MAKE_INSTALL) .\$(OBJDIR)\$(MODULE).lib $(DIST)\lib
|
||||
$(MAKE_INSTALL) .\$(OBJDIR)\$(MODULE).dll $(DIST)\bin\components
|
||||
|
||||
clobber::
|
||||
rm -f $(DIST)\lib\$(MODULE).lib
|
||||
rm -f $(DIST)\bin\components\$(MODULE).dll
|
||||
|
||||
1692
mozilla/xpinstall/src/nsInstall.cpp
Normal file
1692
mozilla/xpinstall/src/nsInstall.cpp
Normal file
File diff suppressed because it is too large
Load Diff
265
mozilla/xpinstall/src/nsInstall.h
Normal file
265
mozilla/xpinstall/src/nsInstall.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __NS_INSTALL_H__
|
||||
#define __NS_INSTALL_H__
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsISupports.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "plevent.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsFileSpec.h"
|
||||
#include "nsVector.h"
|
||||
#include "nsHashtable.h"
|
||||
|
||||
#include "nsSoftwareUpdate.h"
|
||||
|
||||
#include "nsInstallObject.h"
|
||||
#include "nsInstallVersion.h"
|
||||
|
||||
#include "nsIXPInstallProgress.h"
|
||||
|
||||
|
||||
class nsInstallInfo
|
||||
{
|
||||
public:
|
||||
|
||||
nsInstallInfo(const nsString& fromURL, const nsString& localFile, long flags);
|
||||
|
||||
nsInstallInfo(nsVector* fromURL, nsVector* localFiles, long flags);
|
||||
|
||||
virtual ~nsInstallInfo();
|
||||
|
||||
nsString& GetFromURL(PRUint32 index = 0);
|
||||
|
||||
nsString& GetLocalFile(PRUint32 index = 0);
|
||||
|
||||
void GetArguments(nsString& args, PRUint32 index = 0);
|
||||
|
||||
long GetFlags();
|
||||
|
||||
PRBool IsMultipleTrigger();
|
||||
|
||||
static void DeleteVector(nsVector* vector);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
PRBool mMultipleTrigger;
|
||||
nsresult mError;
|
||||
|
||||
long mFlags;
|
||||
nsVector *mFromURLs;
|
||||
nsVector *mLocalFiles;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class nsInstall
|
||||
{
|
||||
friend class nsWinReg;
|
||||
friend class nsWinProfile;
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
BAD_PACKAGE_NAME = -200,
|
||||
UNEXPECTED_ERROR = -201,
|
||||
ACCESS_DENIED = -202,
|
||||
TOO_MANY_CERTIFICATES = -203,
|
||||
NO_INSTALLER_CERTIFICATE = -204,
|
||||
NO_CERTIFICATE = -205,
|
||||
NO_MATCHING_CERTIFICATE = -206,
|
||||
UNKNOWN_JAR_FILE = -207,
|
||||
INVALID_ARGUMENTS = -208,
|
||||
ILLEGAL_RELATIVE_PATH = -209,
|
||||
USER_CANCELLED = -210,
|
||||
INSTALL_NOT_STARTED = -211,
|
||||
SILENT_MODE_DENIED = -212,
|
||||
NO_SUCH_COMPONENT = -213,
|
||||
FILE_DOES_NOT_EXIST = -214,
|
||||
FILE_READ_ONLY = -215,
|
||||
FILE_IS_DIRECTORY = -216,
|
||||
NETWORK_FILE_IS_IN_USE = -217,
|
||||
APPLE_SINGLE_ERR = -218,
|
||||
INVALID_PATH_ERR = -219,
|
||||
PATCH_BAD_DIFF = -220,
|
||||
PATCH_BAD_CHECKSUM_TARGET = -221,
|
||||
PATCH_BAD_CHECKSUM_RESULT = -222,
|
||||
UNINSTALL_FAILED = -223,
|
||||
GESTALT_UNKNOWN_ERR = -5550,
|
||||
GESTALT_INVALID_ARGUMENT = -5551,
|
||||
|
||||
SUCCESS = 0,
|
||||
REBOOT_NEEDED = 999,
|
||||
|
||||
LIMITED_INSTALL = 0,
|
||||
FULL_INSTALL = 1,
|
||||
NO_STATUS_DLG = 2,
|
||||
NO_FINALIZE_DLG = 4,
|
||||
|
||||
INSTALL_FILE_UNEXPECTED_MSG_ID = 0,
|
||||
DETAILS_REPLACE_FILE_MSG_ID = 1,
|
||||
DETAILS_INSTALL_FILE_MSG_ID = 2
|
||||
};
|
||||
|
||||
|
||||
nsInstall();
|
||||
virtual ~nsInstall();
|
||||
|
||||
PRInt32 SetScriptObject(void* aScriptObject);
|
||||
|
||||
PRInt32 SaveWinRegPrototype(void* aScriptObject);
|
||||
PRInt32 SaveWinProfilePrototype(void* aScriptObject);
|
||||
|
||||
JSObject* RetrieveWinRegPrototype(void);
|
||||
JSObject* RetrieveWinProfilePrototype(void);
|
||||
|
||||
PRInt32 GetUserPackageName(nsString& aUserPackageName);
|
||||
PRInt32 GetRegPackageName(nsString& aRegPackageName);
|
||||
|
||||
PRInt32 AbortInstall();
|
||||
|
||||
PRInt32 AddDirectory(const nsString& aRegName, const nsString& aVersion, const nsString& aJarSource, const nsString& aFolder, const nsString& aSubdir, PRBool aForceMode, PRInt32* aReturn);
|
||||
PRInt32 AddDirectory(const nsString& aRegName, const nsString& aVersion, const nsString& aJarSource, const nsString& aFolder, const nsString& aSubdir, PRInt32* aReturn);
|
||||
PRInt32 AddDirectory(const nsString& aRegName, const nsString& aJarSource, const nsString& aFolder, const nsString& aSubdir, PRInt32* aReturn);
|
||||
PRInt32 AddDirectory(const nsString& aJarSource, PRInt32* aReturn);
|
||||
|
||||
PRInt32 AddSubcomponent(const nsString& aRegName, const nsString& aVersion, const nsString& aJarSource, const nsString& aFolder, const nsString& aTargetName, PRBool aForceMode, PRInt32* aReturn);
|
||||
PRInt32 AddSubcomponent(const nsString& aRegName, const nsString& aVersion, const nsString& aJarSource, const nsString& aFolder, const nsString& aTargetName, PRInt32* aReturn);
|
||||
PRInt32 AddSubcomponent(const nsString& aRegName, const nsString& aJarSource, const nsString& aFolder, const nsString& aTargetName, PRInt32* aReturn);
|
||||
PRInt32 AddSubcomponent(const nsString& aJarSource, PRInt32* aReturn);
|
||||
|
||||
PRInt32 DeleteComponent(const nsString& aRegistryName, PRInt32* aReturn);
|
||||
PRInt32 DeleteFile(const nsString& aFolder, const nsString& aRelativeFileName, PRInt32* aReturn);
|
||||
PRInt32 DiskSpaceAvailable(const nsString& aFolder, PRInt32* aReturn);
|
||||
PRInt32 Execute(const nsString& aJarSource, const nsString& aArgs, PRInt32* aReturn);
|
||||
PRInt32 Execute(const nsString& aJarSource, PRInt32* aReturn);
|
||||
PRInt32 FinalizeInstall(PRInt32* aReturn);
|
||||
PRInt32 Gestalt(const nsString& aSelector, PRInt32* aReturn);
|
||||
PRInt32 GetComponentFolder(const nsString& aComponentName, const nsString& aSubdirectory, nsString** aFolder);
|
||||
PRInt32 GetComponentFolder(const nsString& aComponentName, nsString** aFolder);
|
||||
PRInt32 GetFolder(const nsString& aTargetFolder, const nsString& aSubdirectory, nsString** aFolder);
|
||||
PRInt32 GetFolder(const nsString& aTargetFolder, nsString** aFolder);
|
||||
PRInt32 GetLastError(PRInt32* aReturn);
|
||||
PRInt32 GetWinProfile(const nsString& aFolder, const nsString& aFile, JSContext* jscontext, JSClass* WinProfileClass, jsval* aReturn);
|
||||
PRInt32 GetWinRegistry(JSContext* jscontext, JSClass* WinRegClass, jsval* aReturn);
|
||||
PRInt32 Patch(const nsString& aRegName, const nsString& aVersion, const nsString& aJarSource, const nsString& aFolder, const nsString& aTargetName, PRInt32* aReturn);
|
||||
PRInt32 Patch(const nsString& aRegName, const nsString& aJarSource, const nsString& aFolder, const nsString& aTargetName, PRInt32* aReturn);
|
||||
PRInt32 ResetError();
|
||||
PRInt32 SetPackageFolder(const nsString& aFolder);
|
||||
PRInt32 StartInstall(const nsString& aUserPackageName, const nsString& aPackageName, const nsString& aVersion, PRInt32* aReturn);
|
||||
PRInt32 Uninstall(const nsString& aPackageName, PRInt32* aReturn);
|
||||
|
||||
PRInt32 FileOpDirCreate(nsFileSpec& aTarget, PRInt32* aReturn);
|
||||
PRInt32 FileOpDirGetParent(nsFileSpec& aTarget, nsFileSpec* aReturn);
|
||||
PRInt32 FileOpDirRemove(nsFileSpec& aTarget, PRInt32 aFlags, PRInt32* aReturn);
|
||||
PRInt32 FileOpDirRename(nsFileSpec& aSrc, nsFileSpec& aTarget, PRInt32* aReturn);
|
||||
PRInt32 FileOpFileCopy(nsFileSpec& aSrc, nsFileSpec& aTarget, PRInt32* aReturn);
|
||||
PRInt32 FileOpFileDelete(nsFileSpec& aTarget, PRInt32 aFlags, PRInt32* aReturn);
|
||||
PRInt32 FileOpFileExists(nsFileSpec& aTarget, PRBool* aReturn);
|
||||
PRInt32 FileOpFileExecute(nsFileSpec& aTarget, nsString& aParams, PRInt32* aReturn);
|
||||
PRInt32 FileOpFileGetNativeVersion(nsFileSpec& aTarget, nsString* aReturn);
|
||||
PRInt32 FileOpFileGetDiskSpaceAvailable(nsFileSpec& aTarget, PRUint32* aReturn);
|
||||
PRInt32 FileOpFileGetModDate(nsFileSpec& aTarget, nsFileSpec::TimeStamp* aReturn);
|
||||
PRInt32 FileOpFileGetSize(nsFileSpec& aTarget, PRUint32* aReturn);
|
||||
PRInt32 FileOpFileIsDirectory(nsFileSpec& aTarget, PRBool* aReturn);
|
||||
PRInt32 FileOpFileIsFile(nsFileSpec& aTarget, PRBool* aReturn);
|
||||
PRInt32 FileOpFileModDateChanged(nsFileSpec& aTarget, nsFileSpec::TimeStamp& aOldStamp, PRBool* aReturn);
|
||||
PRInt32 FileOpFileMove(nsFileSpec& aSrc, nsFileSpec& aTarget, PRInt32* aReturn);
|
||||
PRInt32 FileOpFileRename(nsFileSpec& aSrc, nsFileSpec& aTarget, PRInt32* aReturn);
|
||||
PRInt32 FileOpFileWinShortcutCreate(nsFileSpec& aTarget, PRInt32 aFlags, PRInt32* aReturn);
|
||||
PRInt32 FileOpFileMacAliasCreate(nsFileSpec& aTarget, PRInt32 aFlags, PRInt32* aReturn);
|
||||
PRInt32 FileOpFileUnixLinkCreate(nsFileSpec& aTarget, PRInt32 aFlags, PRInt32* aReturn);
|
||||
|
||||
PRInt32 ExtractFileFromJar(const nsString& aJarfile, nsFileSpec* aSuggestedName, nsFileSpec** aRealName);
|
||||
void AddPatch(nsHashKey *aKey, nsFileSpec* fileName);
|
||||
void GetPatch(nsHashKey *aKey, nsFileSpec* fileName);
|
||||
|
||||
void GetJarFileLocation(nsString& aFile);
|
||||
void SetJarFileLocation(const nsString& aFile);
|
||||
|
||||
void GetInstallArguments(nsString& args);
|
||||
void SetInstallArguments(const nsString& args);
|
||||
|
||||
|
||||
private:
|
||||
JSObject* mScriptObject;
|
||||
|
||||
JSObject* mWinRegObject;
|
||||
JSObject* mWinProfileObject;
|
||||
|
||||
nsString mJarFileLocation;
|
||||
void* mJarFileData;
|
||||
|
||||
nsString mInstallArguments;
|
||||
|
||||
PRBool mUserCancelled;
|
||||
|
||||
PRBool mUninstallPackage;
|
||||
PRBool mRegisterPackage;
|
||||
|
||||
nsString mRegistryPackageName; /* Name of the package we are installing */
|
||||
nsString mUIName; /* User-readable package name */
|
||||
|
||||
nsInstallVersion* mVersionInfo; /* Component version info */
|
||||
|
||||
nsVector* mInstalledFiles;
|
||||
nsHashtable* mPatchList;
|
||||
|
||||
nsIXPInstallProgress *mNotifier;
|
||||
|
||||
PRInt32 mLastError;
|
||||
|
||||
void ParseFlags(int flags);
|
||||
PRInt32 SanityCheck(void);
|
||||
void GetTime(nsString &aString);
|
||||
|
||||
|
||||
PRInt32 GetQualifiedRegName(const nsString& name, nsString& qualifiedRegName );
|
||||
PRInt32 GetQualifiedPackageName( const nsString& name, nsString& qualifiedName );
|
||||
|
||||
void CurrentUserNode(nsString& userRegNode);
|
||||
PRBool BadRegName(const nsString& regName);
|
||||
PRInt32 SaveError(PRInt32 errcode);
|
||||
|
||||
void CleanUp();
|
||||
|
||||
PRInt32 OpenJARFile(void);
|
||||
void CloseJARFile(void);
|
||||
PRInt32 ExtractDirEntries(const nsString& directory, nsVector *paths);
|
||||
|
||||
PRInt32 ScheduleForInstall(nsInstallObject* ob);
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
235
mozilla/xpinstall/src/nsInstallDelete.cpp
Normal file
235
mozilla/xpinstall/src/nsInstallDelete.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "prmem.h"
|
||||
|
||||
#include "nsFileSpec.h"
|
||||
|
||||
#include "VerReg.h"
|
||||
#include "ScheduledTasks.h"
|
||||
#include "nsInstallDelete.h"
|
||||
#include "nsInstallResources.h"
|
||||
|
||||
#include "nsInstall.h"
|
||||
#include "nsIDOMInstallVersion.h"
|
||||
|
||||
nsInstallDelete::nsInstallDelete( nsInstall* inInstall,
|
||||
const nsString& folderSpec,
|
||||
const nsString& inPartialPath,
|
||||
PRInt32 *error)
|
||||
|
||||
: nsInstallObject(inInstall)
|
||||
{
|
||||
if ((folderSpec == "null") || (inInstall == NULL))
|
||||
{
|
||||
*error = nsInstall::INVALID_ARGUMENTS;
|
||||
return;
|
||||
}
|
||||
|
||||
mDeleteStatus = DELETE_FILE;
|
||||
mFinalFile = nsnull;
|
||||
mRegistryName = "";
|
||||
|
||||
|
||||
mFinalFile = new nsFileSpec(folderSpec);
|
||||
*mFinalFile += inPartialPath;
|
||||
|
||||
*error = ProcessInstallDelete();
|
||||
}
|
||||
|
||||
nsInstallDelete::nsInstallDelete( nsInstall* inInstall,
|
||||
const nsString& inComponentName,
|
||||
PRInt32 *error)
|
||||
|
||||
: nsInstallObject(inInstall)
|
||||
{
|
||||
if (inInstall == NULL)
|
||||
{
|
||||
*error = nsInstall::INVALID_ARGUMENTS;
|
||||
return;
|
||||
}
|
||||
|
||||
mDeleteStatus = DELETE_COMPONENT;
|
||||
mFinalFile = nsnull;
|
||||
mRegistryName = inComponentName;
|
||||
|
||||
*error = ProcessInstallDelete();
|
||||
}
|
||||
|
||||
|
||||
nsInstallDelete::~nsInstallDelete()
|
||||
{
|
||||
if (mFinalFile == nsnull)
|
||||
delete mFinalFile;
|
||||
}
|
||||
|
||||
|
||||
PRInt32 nsInstallDelete::Prepare()
|
||||
{
|
||||
// no set-up necessary
|
||||
return nsInstall::SUCCESS;
|
||||
}
|
||||
|
||||
PRInt32 nsInstallDelete::Complete()
|
||||
{
|
||||
PRInt32 err = nsInstall::SUCCESS;
|
||||
|
||||
if (mInstall == NULL)
|
||||
return nsInstall::INVALID_ARGUMENTS;
|
||||
|
||||
if (mDeleteStatus == DELETE_COMPONENT)
|
||||
{
|
||||
char* temp = mRegistryName.ToNewCString();
|
||||
err = VR_Remove(temp);
|
||||
delete [] temp;
|
||||
}
|
||||
|
||||
if ((mDeleteStatus == DELETE_FILE) || (err == REGERR_OK))
|
||||
{
|
||||
err = NativeComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
err = nsInstall::UNEXPECTED_ERROR;
|
||||
}
|
||||
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void nsInstallDelete::Abort()
|
||||
{
|
||||
}
|
||||
|
||||
char* nsInstallDelete::toString()
|
||||
{
|
||||
char* buffer = new char[1024];
|
||||
|
||||
if (mDeleteStatus == DELETE_COMPONENT)
|
||||
{
|
||||
sprintf( buffer, nsInstallResources::GetDeleteComponentString(), nsAutoCString(mRegistryName));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mFinalFile)
|
||||
sprintf( buffer, nsInstallResources::GetDeleteFileString(), mFinalFile->GetCString());
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
PRBool
|
||||
nsInstallDelete::CanUninstall()
|
||||
{
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsInstallDelete::RegisterPackageNode()
|
||||
{
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
|
||||
PRInt32 nsInstallDelete::ProcessInstallDelete()
|
||||
{
|
||||
PRInt32 err;
|
||||
|
||||
char* tempCString = nsnull;
|
||||
|
||||
if (mDeleteStatus == DELETE_COMPONENT)
|
||||
{
|
||||
/* Check if the component is in the registry */
|
||||
tempCString = mRegistryName.ToNewCString();
|
||||
|
||||
err = VR_InRegistry( tempCString );
|
||||
|
||||
if (err != REGERR_OK)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
else
|
||||
{
|
||||
char* tempRegistryString;
|
||||
|
||||
tempRegistryString = (char*)PR_Calloc(MAXREGPATHLEN, sizeof(char));
|
||||
|
||||
err = VR_GetPath( tempCString , MAXREGPATHLEN, tempRegistryString);
|
||||
|
||||
if (err == REGERR_OK)
|
||||
{
|
||||
if (mFinalFile)
|
||||
delete mFinalFile;
|
||||
|
||||
mFinalFile = new nsFileSpec(tempRegistryString);
|
||||
}
|
||||
|
||||
PR_FREEIF(tempRegistryString);
|
||||
}
|
||||
}
|
||||
|
||||
if(tempCString)
|
||||
delete [] tempCString;
|
||||
|
||||
if (mFinalFile->Exists())
|
||||
{
|
||||
if (mFinalFile->IsFile())
|
||||
{
|
||||
err = nsInstall::SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = nsInstall::FILE_IS_DIRECTORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err = nsInstall::FILE_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PRInt32 nsInstallDelete::NativeComplete()
|
||||
{
|
||||
if (mFinalFile->Exists())
|
||||
{
|
||||
if (mFinalFile->IsFile())
|
||||
{
|
||||
return DeleteFileNowOrSchedule(*mFinalFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nsInstall::FILE_IS_DIRECTORY;
|
||||
}
|
||||
}
|
||||
|
||||
return nsInstall::FILE_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
78
mozilla/xpinstall/src/nsInstallDelete.h
Normal file
78
mozilla/xpinstall/src/nsInstallDelete.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef nsInstallDelete_h__
|
||||
#define nsInstallDelete_h__
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "nsInstallObject.h"
|
||||
|
||||
#include "nsInstall.h"
|
||||
|
||||
#define DELETE_COMPONENT 1
|
||||
#define DELETE_FILE 2
|
||||
|
||||
class nsInstallDelete : public nsInstallObject
|
||||
{
|
||||
public:
|
||||
|
||||
nsInstallDelete( nsInstall* inInstall,
|
||||
const nsString& folderSpec,
|
||||
const nsString& inPartialPath,
|
||||
PRInt32 *error);
|
||||
|
||||
nsInstallDelete( nsInstall* inInstall,
|
||||
const nsString& ,
|
||||
PRInt32 *error);
|
||||
|
||||
virtual ~nsInstallDelete();
|
||||
|
||||
PRInt32 Prepare();
|
||||
PRInt32 Complete();
|
||||
void Abort();
|
||||
char* toString();
|
||||
|
||||
PRBool CanUninstall();
|
||||
PRBool RegisterPackageNode();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/* Private Fields */
|
||||
|
||||
nsFileSpec* mFinalFile;
|
||||
|
||||
nsString mRegistryName;
|
||||
PRInt32 mDeleteStatus;
|
||||
|
||||
PRInt32 ProcessInstallDelete();
|
||||
PRInt32 NativeComplete();
|
||||
|
||||
};
|
||||
|
||||
#endif /* nsInstallDelete_h__ */
|
||||
136
mozilla/xpinstall/src/nsInstallExecute.cpp
Normal file
136
mozilla/xpinstall/src/nsInstallExecute.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "prmem.h"
|
||||
|
||||
#include "nsFileSpec.h"
|
||||
|
||||
#include "VerReg.h"
|
||||
#include "nsInstallExecute.h"
|
||||
#include "nsInstallResources.h"
|
||||
#include "ScheduledTasks.h"
|
||||
|
||||
#include "nsInstall.h"
|
||||
#include "nsIDOMInstallVersion.h"
|
||||
|
||||
nsInstallExecute:: nsInstallExecute( nsInstall* inInstall,
|
||||
const nsString& inJarLocation,
|
||||
const nsString& inArgs,
|
||||
PRInt32 *error)
|
||||
|
||||
: nsInstallObject(inInstall)
|
||||
{
|
||||
if ((inInstall == nsnull) || (inJarLocation == "null"))
|
||||
{
|
||||
*error = nsInstall::INVALID_ARGUMENTS;
|
||||
return;
|
||||
}
|
||||
|
||||
mJarLocation = inJarLocation;
|
||||
mArgs = inArgs;
|
||||
mExecutableFile = nsnull;
|
||||
|
||||
}
|
||||
|
||||
|
||||
nsInstallExecute::~nsInstallExecute()
|
||||
{
|
||||
if (mExecutableFile)
|
||||
delete mExecutableFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PRInt32 nsInstallExecute::Prepare()
|
||||
{
|
||||
if (mInstall == NULL || mJarLocation == "null")
|
||||
return nsInstall::INVALID_ARGUMENTS;
|
||||
|
||||
return mInstall->ExtractFileFromJar(mJarLocation, nsnull, &mExecutableFile);
|
||||
}
|
||||
|
||||
PRInt32 nsInstallExecute::Complete()
|
||||
{
|
||||
if (mExecutableFile == nsnull)
|
||||
return nsInstall::INVALID_ARGUMENTS;
|
||||
|
||||
nsFileSpec app( *mExecutableFile);
|
||||
|
||||
if (!app.Exists())
|
||||
{
|
||||
return nsInstall::INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
PRInt32 result = app.Execute( mArgs );
|
||||
|
||||
DeleteFileNowOrSchedule( app );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void nsInstallExecute::Abort()
|
||||
{
|
||||
/* Get the names */
|
||||
if (mExecutableFile == nsnull)
|
||||
return;
|
||||
|
||||
DeleteFileNowOrSchedule(*mExecutableFile);
|
||||
}
|
||||
|
||||
char* nsInstallExecute::toString()
|
||||
{
|
||||
char* buffer = new char[1024];
|
||||
|
||||
// if the FileSpec is NULL, just us the in jar file name.
|
||||
|
||||
if (mExecutableFile == nsnull)
|
||||
{
|
||||
char *tempString = mJarLocation.ToNewCString();
|
||||
sprintf( buffer, nsInstallResources::GetExecuteString(), tempString);
|
||||
delete [] tempString;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf( buffer, nsInstallResources::GetExecuteString(), mExecutableFile->GetCString());
|
||||
}
|
||||
return buffer;
|
||||
|
||||
}
|
||||
|
||||
|
||||
PRBool
|
||||
nsInstallExecute::CanUninstall()
|
||||
{
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsInstallExecute::RegisterPackageNode()
|
||||
{
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
73
mozilla/xpinstall/src/nsInstallExecute.h
Normal file
73
mozilla/xpinstall/src/nsInstallExecute.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef nsInstallExecute_h__
|
||||
#define nsInstallExecute_h__
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "nsInstallObject.h"
|
||||
|
||||
#include "nsInstall.h"
|
||||
#include "nsIDOMInstallVersion.h"
|
||||
|
||||
|
||||
class nsInstallExecute : public nsInstallObject
|
||||
{
|
||||
public:
|
||||
|
||||
nsInstallExecute( nsInstall* inInstall,
|
||||
const nsString& inJarLocation,
|
||||
const nsString& inArgs,
|
||||
PRInt32 *error);
|
||||
|
||||
|
||||
virtual ~nsInstallExecute();
|
||||
|
||||
PRInt32 Prepare();
|
||||
PRInt32 Complete();
|
||||
void Abort();
|
||||
char* toString();
|
||||
|
||||
PRBool CanUninstall();
|
||||
PRBool RegisterPackageNode();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
nsString mJarLocation; // Location in the JAR
|
||||
nsString mArgs; // command line arguments
|
||||
|
||||
nsFileSpec *mExecutableFile; // temporary file location
|
||||
|
||||
|
||||
PRInt32 NativeComplete(void);
|
||||
void NativeAbort(void);
|
||||
|
||||
};
|
||||
|
||||
#endif /* nsInstallExecute_h__ */
|
||||
366
mozilla/xpinstall/src/nsInstallFile.cpp
Normal file
366
mozilla/xpinstall/src/nsInstallFile.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
#include "nsInstallFile.h"
|
||||
#include "nsFileSpec.h"
|
||||
#include "VerReg.h"
|
||||
#include "ScheduledTasks.h"
|
||||
#include "nsInstall.h"
|
||||
#include "nsIDOMInstallVersion.h"
|
||||
#include "nsInstallResources.h"
|
||||
|
||||
/* Public Methods */
|
||||
|
||||
/* Constructor
|
||||
inInstall - softUpdate object we belong to
|
||||
inComponentName - full path of the registry component
|
||||
inVInfo - full version info
|
||||
inJarLocation - location inside the JAR file
|
||||
inFinalFileSpec - final location on disk
|
||||
*/
|
||||
|
||||
|
||||
nsInstallFile::nsInstallFile(nsInstall* inInstall,
|
||||
const nsString& inComponentName,
|
||||
const nsString& inVInfo,
|
||||
const nsString& inJarLocation,
|
||||
const nsString& folderSpec,
|
||||
const nsString& inPartialPath,
|
||||
PRBool forceInstall,
|
||||
PRInt32 *error)
|
||||
: nsInstallObject(inInstall)
|
||||
{
|
||||
mVersionRegistryName = nsnull;
|
||||
mJarLocation = nsnull;
|
||||
mExtracedFile = nsnull;
|
||||
mFinalFile = nsnull;
|
||||
mVersionInfo = nsnull;
|
||||
|
||||
mUpgradeFile = PR_FALSE;
|
||||
|
||||
if ((folderSpec == "null") || (inInstall == NULL))
|
||||
{
|
||||
*error = nsInstall::INVALID_ARGUMENTS;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for existence of the newer version */
|
||||
|
||||
PRBool versionNewer = PR_FALSE; // Is this a newer version
|
||||
char* qualifiedRegNameString = inComponentName.ToNewCString();
|
||||
|
||||
if ( (forceInstall == PR_FALSE ) && (inVInfo != "") && ( VR_ValidateComponent( qualifiedRegNameString ) == 0 ) )
|
||||
{
|
||||
nsInstallVersion *newVersion = new nsInstallVersion();
|
||||
newVersion->Init(inVInfo);
|
||||
|
||||
VERSION versionStruct;
|
||||
|
||||
VR_GetVersion( qualifiedRegNameString, &versionStruct );
|
||||
|
||||
nsInstallVersion* oldVersion = new nsInstallVersion();
|
||||
|
||||
oldVersion->Init(versionStruct.major,
|
||||
versionStruct.minor,
|
||||
versionStruct.release,
|
||||
versionStruct.build);
|
||||
|
||||
PRInt32 areTheyEqual;
|
||||
newVersion->CompareTo(oldVersion, &areTheyEqual);
|
||||
|
||||
delete oldVersion;
|
||||
delete newVersion;
|
||||
|
||||
if (areTheyEqual == nsIDOMInstallVersion::MAJOR_DIFF_MINUS ||
|
||||
areTheyEqual == nsIDOMInstallVersion::MINOR_DIFF_MINUS ||
|
||||
areTheyEqual == nsIDOMInstallVersion::REL_DIFF_MINUS ||
|
||||
areTheyEqual == nsIDOMInstallVersion::BLD_DIFF_MINUS )
|
||||
{
|
||||
// the file to be installed is OLDER than what is on disk. Return error
|
||||
delete qualifiedRegNameString;
|
||||
*error = areTheyEqual;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
delete qualifiedRegNameString;
|
||||
|
||||
|
||||
mFinalFile = new nsFileSpec(folderSpec);
|
||||
*mFinalFile += inPartialPath;
|
||||
|
||||
mReplaceFile = mFinalFile->Exists();
|
||||
|
||||
if (mReplaceFile == PR_FALSE)
|
||||
{
|
||||
nsFileSpec parent;
|
||||
mFinalFile->GetParent(parent);
|
||||
nsFileSpec makeDirs(parent.GetCString(), PR_TRUE);
|
||||
}
|
||||
|
||||
mForceInstall = forceInstall;
|
||||
|
||||
mVersionRegistryName = new nsString(inComponentName);
|
||||
mJarLocation = new nsString(inJarLocation);
|
||||
mVersionInfo = new nsString(inVInfo);
|
||||
|
||||
nsString regPackageName;
|
||||
mInstall->GetRegPackageName(regPackageName);
|
||||
|
||||
// determine Child status
|
||||
if ( regPackageName == "" )
|
||||
{
|
||||
// in the "current communicator package" absolute pathnames (start
|
||||
// with slash) indicate shared files -- all others are children
|
||||
mChildFile = ( mVersionRegistryName->CharAt(0) != '/' );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// there is no "starts with" api in nsString. LAME!
|
||||
nsString startsWith;
|
||||
mVersionRegistryName->Left(startsWith, regPackageName.Length());
|
||||
|
||||
if (startsWith.Equals(regPackageName))
|
||||
{
|
||||
mChildFile = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mChildFile = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsInstallFile::~nsInstallFile()
|
||||
{
|
||||
if (mVersionRegistryName)
|
||||
delete mVersionRegistryName;
|
||||
|
||||
if (mJarLocation)
|
||||
delete mJarLocation;
|
||||
|
||||
if (mExtracedFile)
|
||||
delete mExtracedFile;
|
||||
|
||||
if (mFinalFile)
|
||||
delete mFinalFile;
|
||||
|
||||
if (mVersionInfo)
|
||||
delete mVersionInfo;
|
||||
}
|
||||
|
||||
/* Prepare
|
||||
* Extracts file out of the JAR archive
|
||||
*/
|
||||
PRInt32 nsInstallFile::Prepare()
|
||||
{
|
||||
if (mInstall == nsnull || mFinalFile == nsnull || mJarLocation == nsnull )
|
||||
return nsInstall::INVALID_ARGUMENTS;
|
||||
|
||||
return mInstall->ExtractFileFromJar(*mJarLocation, mFinalFile, &mExtracedFile);
|
||||
}
|
||||
|
||||
/* Complete
|
||||
* Completes the install:
|
||||
* - move the downloaded file to the final location
|
||||
* - updates the registry
|
||||
*/
|
||||
PRInt32 nsInstallFile::Complete()
|
||||
{
|
||||
PRInt32 err;
|
||||
|
||||
if (mInstall == nsnull || mVersionRegistryName == nsnull || mFinalFile == nsnull )
|
||||
{
|
||||
return nsInstall::INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
err = CompleteFileMove();
|
||||
|
||||
if ( 0 == err || nsInstall::REBOOT_NEEDED == err )
|
||||
{
|
||||
err = RegisterInVersionRegistry();
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
void nsInstallFile::Abort()
|
||||
{
|
||||
if (mExtracedFile != nsnull)
|
||||
mExtracedFile->Delete(PR_FALSE);
|
||||
}
|
||||
|
||||
char* nsInstallFile::toString()
|
||||
{
|
||||
char* buffer = new char[1024];
|
||||
|
||||
if (mFinalFile == nsnull)
|
||||
{
|
||||
sprintf( buffer, nsInstallResources::GetInstallFileString(), nsnull);
|
||||
}
|
||||
else if (mReplaceFile)
|
||||
{
|
||||
// we are replacing this file.
|
||||
|
||||
sprintf( buffer, nsInstallResources::GetReplaceFileString(), mFinalFile->GetCString());
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf( buffer, nsInstallResources::GetInstallFileString(), mFinalFile->GetCString());
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
PRInt32 nsInstallFile::CompleteFileMove()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (mExtracedFile == nsnull)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( *mExtracedFile == *mFinalFile )
|
||||
{
|
||||
/* No need to rename, they are the same */
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ReplaceFileNowOrSchedule(*mExtracedFile, *mFinalFile );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFile::RegisterInVersionRegistry()
|
||||
{
|
||||
int refCount;
|
||||
nsString regPackageName;
|
||||
mInstall->GetRegPackageName(regPackageName);
|
||||
|
||||
|
||||
// Register file and log for Uninstall
|
||||
|
||||
if (!mChildFile)
|
||||
{
|
||||
int found;
|
||||
if (regPackageName != "")
|
||||
{
|
||||
found = VR_UninstallFileExistsInList( (char*)(const char*)nsAutoCString(regPackageName) ,
|
||||
(char*)(const char*)nsAutoCString(*mVersionRegistryName));
|
||||
}
|
||||
else
|
||||
{
|
||||
found = VR_UninstallFileExistsInList( "", (char*)(const char*)nsAutoCString(*mVersionRegistryName) );
|
||||
}
|
||||
|
||||
if (found != REGERR_OK)
|
||||
mUpgradeFile = PR_FALSE;
|
||||
else
|
||||
mUpgradeFile = PR_TRUE;
|
||||
}
|
||||
else if (REGERR_OK == VR_InRegistry( (char*)(const char*)nsAutoCString(*mVersionRegistryName)))
|
||||
{
|
||||
mUpgradeFile = PR_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mUpgradeFile = PR_FALSE;
|
||||
}
|
||||
|
||||
if ( REGERR_OK != VR_GetRefCount( (char*)(const char*)nsAutoCString(*mVersionRegistryName), &refCount ))
|
||||
{
|
||||
refCount = 0;
|
||||
}
|
||||
|
||||
VR_Install( (char*)(const char*)nsAutoCString(*mVersionRegistryName),
|
||||
(char*)(const char*)mFinalFile->GetNativePathCString(), // DO NOT CHANGE THIS.
|
||||
(char*)(const char*)nsAutoCString(*mVersionInfo),
|
||||
PR_FALSE );
|
||||
|
||||
if (mUpgradeFile)
|
||||
{
|
||||
if (refCount == 0)
|
||||
VR_SetRefCount( (char*)(const char*)nsAutoCString(*mVersionRegistryName), 1 );
|
||||
else
|
||||
VR_SetRefCount( (char*)(const char*)nsAutoCString(*mVersionRegistryName), refCount ); //FIX?? what should the ref count be/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (refCount != 0)
|
||||
{
|
||||
VR_SetRefCount( (char*)(const char*)nsAutoCString(*mVersionRegistryName), refCount + 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mReplaceFile)
|
||||
VR_SetRefCount( (char*)(const char*)nsAutoCString(*mVersionRegistryName), 2 );
|
||||
else
|
||||
VR_SetRefCount( (char*)(const char*)nsAutoCString(*mVersionRegistryName), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !mChildFile && !mUpgradeFile )
|
||||
{
|
||||
if (regPackageName != "")
|
||||
{
|
||||
VR_UninstallAddFileToList( (char*)(const char*)nsAutoCString(regPackageName),
|
||||
(char*)(const char*)nsAutoCString(*mVersionRegistryName));
|
||||
}
|
||||
else
|
||||
{
|
||||
VR_UninstallAddFileToList( "", (char*)(const char*)nsAutoCString(*mVersionRegistryName) );
|
||||
}
|
||||
}
|
||||
|
||||
return nsInstall::SUCCESS;
|
||||
}
|
||||
|
||||
/* CanUninstall
|
||||
* InstallFile() installs files which can be uninstalled,
|
||||
* hence this function returns true.
|
||||
*/
|
||||
PRBool
|
||||
nsInstallFile::CanUninstall()
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* RegisterPackageNode
|
||||
* InstallFile() installs files which need to be registered,
|
||||
* hence this function returns true.
|
||||
*/
|
||||
PRBool
|
||||
nsInstallFile::RegisterPackageNode()
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
93
mozilla/xpinstall/src/nsInstallFile.h
Normal file
93
mozilla/xpinstall/src/nsInstallFile.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef nsInstallFile_h__
|
||||
#define nsInstallFile_h__
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "nsInstallObject.h"
|
||||
|
||||
#include "nsInstall.h"
|
||||
#include "nsInstallVersion.h"
|
||||
|
||||
class nsInstallFile : public nsInstallObject
|
||||
{
|
||||
public:
|
||||
|
||||
/*************************************************************
|
||||
* Public Methods
|
||||
*
|
||||
* Constructor
|
||||
* inSoftUpdate - softUpdate object we belong to
|
||||
* inComponentName - full path of the registry component
|
||||
* inVInfo - full version info
|
||||
* inJarLocation - location inside the JAR file
|
||||
* inFinalFileSpec - final location on disk
|
||||
*************************************************************/
|
||||
nsInstallFile( nsInstall* inInstall,
|
||||
const nsString& inVRName,
|
||||
const nsString& inVInfo,
|
||||
const nsString& inJarLocation,
|
||||
const nsString& folderSpec,
|
||||
const nsString& inPartialPath,
|
||||
PRBool forceInstall,
|
||||
PRInt32 *error);
|
||||
|
||||
virtual ~nsInstallFile();
|
||||
|
||||
PRInt32 Prepare();
|
||||
PRInt32 Complete();
|
||||
void Abort();
|
||||
char* toString();
|
||||
|
||||
PRBool CanUninstall();
|
||||
PRBool RegisterPackageNode();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/* Private Fields */
|
||||
nsString* mVersionInfo; /* Version info for this file*/
|
||||
|
||||
nsString* mJarLocation; /* Location in the JAR */
|
||||
nsFileSpec* mExtracedFile; /* temporary file location */
|
||||
nsFileSpec* mFinalFile; /* final file destination */
|
||||
|
||||
nsString* mVersionRegistryName; /* full version path */
|
||||
|
||||
PRBool mForceInstall; /* whether install is forced */
|
||||
PRBool mReplaceFile; /* whether file exists */
|
||||
PRBool mChildFile; /* whether file is a child */
|
||||
PRBool mUpgradeFile; /* whether file is an upgrade */
|
||||
|
||||
|
||||
PRInt32 CompleteFileMove();
|
||||
PRInt32 RegisterInVersionRegistry();
|
||||
};
|
||||
|
||||
#endif /* nsInstallFile_h__ */
|
||||
38
mozilla/xpinstall/src/nsInstallFileOpEnums.h
Normal file
38
mozilla/xpinstall/src/nsInstallFileOpEnums.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifndef nsInstallFileOpEnums_h__
|
||||
#define nsInstallFileOpEnums_h__
|
||||
|
||||
typedef enum nsInstallFileOpEnums {
|
||||
NS_FOP_DIR_CREATE = 0,
|
||||
NS_FOP_DIR_REMOVE = 1,
|
||||
NS_FOP_DIR_RENAME = 2,
|
||||
NS_FOP_FILE_COPY = 3,
|
||||
NS_FOP_FILE_DELETE = 4,
|
||||
NS_FOP_FILE_EXECUTE = 5,
|
||||
NS_FOP_FILE_MOVE = 6,
|
||||
NS_FOP_FILE_RENAME = 7,
|
||||
NS_FOP_WIN_SHORTCUT_CREATE = 8,
|
||||
NS_FOP_MAC_ALIAS_CREATE = 9,
|
||||
NS_FOP_UNIX_LINK_CREATE = 10,
|
||||
NS_FOP_FILE_SET_STAT = 11
|
||||
|
||||
} nsInstallFileOpEnums;
|
||||
|
||||
#endif /* nsInstallFileOpEnums_h__ */
|
||||
316
mozilla/xpinstall/src/nsInstallFileOpItem.cpp
Normal file
316
mozilla/xpinstall/src/nsInstallFileOpItem.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#include "nspr.h"
|
||||
#include "nsInstall.h"
|
||||
#include "nsInstallFileOpEnums.h"
|
||||
#include "nsInstallFileOpItem.h"
|
||||
|
||||
/* Public Methods */
|
||||
|
||||
nsInstallFileOpItem::nsInstallFileOpItem(nsInstall* aInstallObj,
|
||||
PRInt32 aCommand,
|
||||
nsFileSpec& aTarget,
|
||||
PRInt32 aFlags,
|
||||
PRInt32* aReturn)
|
||||
:nsInstallObject(aInstallObj)
|
||||
{
|
||||
mIObj = aInstallObj;
|
||||
mCommand = aCommand;
|
||||
mFlags = aFlags;
|
||||
mSrc = nsnull;
|
||||
mParams = nsnull;
|
||||
mTarget = new nsFileSpec(aTarget);
|
||||
|
||||
*aReturn = NS_OK;
|
||||
}
|
||||
|
||||
nsInstallFileOpItem::nsInstallFileOpItem(nsInstall* aInstallObj,
|
||||
PRInt32 aCommand,
|
||||
nsFileSpec& aSrc,
|
||||
nsFileSpec& aTarget,
|
||||
PRInt32* aReturn)
|
||||
:nsInstallObject(aInstallObj)
|
||||
{
|
||||
mIObj = aInstallObj;
|
||||
mCommand = aCommand;
|
||||
mFlags = 0;
|
||||
mSrc = new nsFileSpec(aSrc);
|
||||
mParams = nsnull;
|
||||
mTarget = new nsFileSpec(aTarget);
|
||||
|
||||
*aReturn = NS_OK;
|
||||
}
|
||||
|
||||
nsInstallFileOpItem::nsInstallFileOpItem(nsInstall* aInstallObj,
|
||||
PRInt32 aCommand,
|
||||
nsFileSpec& aTarget,
|
||||
PRInt32* aReturn)
|
||||
:nsInstallObject(aInstallObj)
|
||||
{
|
||||
mIObj = aInstallObj;
|
||||
mCommand = aCommand;
|
||||
mFlags = 0;
|
||||
mSrc = nsnull;
|
||||
mParams = nsnull;
|
||||
mTarget = new nsFileSpec(aTarget);
|
||||
|
||||
*aReturn = NS_OK;
|
||||
}
|
||||
|
||||
nsInstallFileOpItem::nsInstallFileOpItem(nsInstall* aInstallObj,
|
||||
PRInt32 aCommand,
|
||||
nsFileSpec& aTarget,
|
||||
nsString& aParams,
|
||||
PRInt32* aReturn)
|
||||
:nsInstallObject(aInstallObj)
|
||||
{
|
||||
mIObj = aInstallObj;
|
||||
mCommand = aCommand;
|
||||
mFlags = 0;
|
||||
mSrc = nsnull;
|
||||
mParams = new nsString(aParams);
|
||||
mTarget = new nsFileSpec(aTarget);
|
||||
|
||||
*aReturn = NS_OK;
|
||||
}
|
||||
|
||||
nsInstallFileOpItem::~nsInstallFileOpItem()
|
||||
{
|
||||
if(mSrc)
|
||||
delete mSrc;
|
||||
if(mTarget)
|
||||
delete mTarget;
|
||||
}
|
||||
|
||||
PRInt32 nsInstallFileOpItem::Complete()
|
||||
{
|
||||
PRInt32 aReturn = NS_OK;
|
||||
|
||||
switch(mCommand)
|
||||
{
|
||||
case NS_FOP_DIR_CREATE:
|
||||
NativeFileOpDirCreate(mTarget);
|
||||
break;
|
||||
case NS_FOP_DIR_REMOVE:
|
||||
NativeFileOpDirRemove(mTarget, mFlags);
|
||||
break;
|
||||
case NS_FOP_DIR_RENAME:
|
||||
NativeFileOpDirRename(mSrc, mTarget);
|
||||
break;
|
||||
case NS_FOP_FILE_COPY:
|
||||
NativeFileOpFileCopy(mSrc, mTarget);
|
||||
break;
|
||||
case NS_FOP_FILE_DELETE:
|
||||
NativeFileOpFileDelete(mTarget, mFlags);
|
||||
break;
|
||||
case NS_FOP_FILE_EXECUTE:
|
||||
NativeFileOpFileExecute(mTarget, mParams);
|
||||
break;
|
||||
case NS_FOP_FILE_MOVE:
|
||||
NativeFileOpFileMove(mSrc, mTarget);
|
||||
break;
|
||||
case NS_FOP_FILE_RENAME:
|
||||
NativeFileOpFileRename(mSrc, mTarget);
|
||||
break;
|
||||
case NS_FOP_WIN_SHORTCUT_CREATE:
|
||||
NativeFileOpWinShortcutCreate();
|
||||
break;
|
||||
case NS_FOP_MAC_ALIAS_CREATE:
|
||||
NativeFileOpMacAliasCreate();
|
||||
break;
|
||||
case NS_FOP_UNIX_LINK_CREATE:
|
||||
NativeFileOpUnixLinkCreate();
|
||||
break;
|
||||
}
|
||||
return aReturn;
|
||||
}
|
||||
|
||||
float nsInstallFileOpItem::GetInstallOrder()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
char* nsInstallFileOpItem::toString()
|
||||
{
|
||||
nsString result;
|
||||
char* resultCString;
|
||||
|
||||
switch(mCommand)
|
||||
{
|
||||
case NS_FOP_FILE_COPY:
|
||||
result = "Copy file: ";
|
||||
result.Append(mSrc->GetNativePathCString());
|
||||
result.Append(" to ");
|
||||
result.Append(mTarget->GetNativePathCString());
|
||||
resultCString = result.ToNewCString();
|
||||
break;
|
||||
case NS_FOP_FILE_DELETE:
|
||||
result = "Delete file: ";
|
||||
result.Append(mTarget->GetNativePathCString());
|
||||
resultCString = result.ToNewCString();
|
||||
break;
|
||||
case NS_FOP_FILE_MOVE:
|
||||
result = "Move file: ";
|
||||
result.Append(mSrc->GetNativePathCString());
|
||||
result.Append(" to ");
|
||||
result.Append(mTarget->GetNativePathCString());
|
||||
resultCString = result.ToNewCString();
|
||||
break;
|
||||
case NS_FOP_FILE_RENAME:
|
||||
result = "Rename file: ";
|
||||
result.Append(mTarget->GetNativePathCString());
|
||||
resultCString = result.ToNewCString();
|
||||
break;
|
||||
case NS_FOP_DIR_CREATE:
|
||||
result = "Create Folder: ";
|
||||
result.Append(mTarget->GetNativePathCString());
|
||||
resultCString = result.ToNewCString();
|
||||
break;
|
||||
case NS_FOP_DIR_REMOVE:
|
||||
result = "Remove Folder: ";
|
||||
result.Append(mTarget->GetNativePathCString());
|
||||
resultCString = result.ToNewCString();
|
||||
break;
|
||||
case NS_FOP_WIN_SHORTCUT_CREATE:
|
||||
break;
|
||||
case NS_FOP_MAC_ALIAS_CREATE:
|
||||
break;
|
||||
case NS_FOP_UNIX_LINK_CREATE:
|
||||
break;
|
||||
case NS_FOP_FILE_SET_STAT:
|
||||
result = "Set file stat: ";
|
||||
result.Append(mTarget->GetNativePathCString());
|
||||
resultCString = result.ToNewCString();
|
||||
break;
|
||||
default:
|
||||
result = "Unkown file operation command!";
|
||||
resultCString = result.ToNewCString();
|
||||
break;
|
||||
}
|
||||
return resultCString;
|
||||
}
|
||||
|
||||
PRInt32 nsInstallFileOpItem::Prepare()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void nsInstallFileOpItem::Abort()
|
||||
{
|
||||
}
|
||||
|
||||
/* Private Methods */
|
||||
|
||||
/* CanUninstall
|
||||
* InstallFileOpItem() does not install any files which can be uninstalled,
|
||||
* hence this function returns false.
|
||||
*/
|
||||
PRBool
|
||||
nsInstallFileOpItem::CanUninstall()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* RegisterPackageNode
|
||||
* InstallFileOpItem() does notinstall files which need to be registered,
|
||||
* hence this function returns false.
|
||||
*/
|
||||
PRBool
|
||||
nsInstallFileOpItem::RegisterPackageNode()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// File operation functions begin here
|
||||
//
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpDirCreate(nsFileSpec* aTarget)
|
||||
{
|
||||
aTarget->CreateDirectory();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpDirRemove(nsFileSpec* aTarget, PRInt32 aFlags)
|
||||
{
|
||||
aTarget->Delete(aFlags);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpDirRename(nsFileSpec* aSrc, nsFileSpec* aTarget)
|
||||
{
|
||||
aSrc->Rename(*aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpFileCopy(nsFileSpec* aSrc, nsFileSpec* aTarget)
|
||||
{
|
||||
aSrc->Copy(*aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpFileDelete(nsFileSpec* aTarget, PRInt32 aFlags)
|
||||
{
|
||||
aTarget->Delete(aFlags);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpFileExecute(nsFileSpec* aTarget, nsString* aParams)
|
||||
{
|
||||
aTarget->Execute(*aParams);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpFileMove(nsFileSpec* aSrc, nsFileSpec* aTarget)
|
||||
{
|
||||
aSrc->Move(*aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpFileRename(nsFileSpec* aSrc, nsFileSpec* aTarget)
|
||||
{
|
||||
aSrc->Rename(*aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpWinShortcutCreate()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpMacAliasCreate()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsInstallFileOpItem::NativeFileOpUnixLinkCreate()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
111
mozilla/xpinstall/src/nsInstallFileOpItem.h
Normal file
111
mozilla/xpinstall/src/nsInstallFileOpItem.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifndef nsInstallFileOpItem_h__
|
||||
#define nsInstallFileOpItem_h__
|
||||
|
||||
#include "prtypes.h"
|
||||
|
||||
#include "nsFileSpec.h"
|
||||
#include "nsSoftwareUpdate.h"
|
||||
#include "nsInstallObject.h"
|
||||
#include "nsInstall.h"
|
||||
|
||||
class nsInstallFileOpItem : public nsInstallObject
|
||||
{
|
||||
public:
|
||||
/* Public Fields */
|
||||
|
||||
/* Public Methods */
|
||||
// used by:
|
||||
// FileOpFileDelete()
|
||||
nsInstallFileOpItem(nsInstall* installObj,
|
||||
PRInt32 aCommand,
|
||||
nsFileSpec& aTarget,
|
||||
PRInt32 aFlags,
|
||||
PRInt32* aReturn);
|
||||
|
||||
// used by:
|
||||
// FileOpDirRemove()
|
||||
// FileOpDirRename()
|
||||
// FileOpFileCopy()
|
||||
// FileOpFileMove()
|
||||
// FileOpFileRename()
|
||||
nsInstallFileOpItem(nsInstall* installObj,
|
||||
PRInt32 aCommand,
|
||||
nsFileSpec& aSrc,
|
||||
nsFileSpec& aTarget,
|
||||
PRInt32* aReturn);
|
||||
|
||||
// used by:
|
||||
// FileOpDirCreate()
|
||||
nsInstallFileOpItem(nsInstall* aInstallObj,
|
||||
PRInt32 aCommand,
|
||||
nsFileSpec& aTarget,
|
||||
PRInt32* aReturn);
|
||||
|
||||
// used by:
|
||||
// FileOpFileExecute()
|
||||
nsInstallFileOpItem(nsInstall* aInstallObj,
|
||||
PRInt32 aCommand,
|
||||
nsFileSpec& aTarget,
|
||||
nsString& aParams,
|
||||
PRInt32* aReturn);
|
||||
|
||||
~nsInstallFileOpItem();
|
||||
|
||||
PRInt32 Prepare(void);
|
||||
PRInt32 Complete();
|
||||
char* toString();
|
||||
void Abort();
|
||||
float GetInstallOrder();
|
||||
|
||||
/* should these be protected? */
|
||||
PRBool CanUninstall();
|
||||
PRBool RegisterPackageNode();
|
||||
|
||||
private:
|
||||
|
||||
/* Private Fields */
|
||||
|
||||
nsInstall* mIObj; // initiating Install object
|
||||
nsFileSpec* mSrc;
|
||||
nsFileSpec* mTarget;
|
||||
nsString* mParams;
|
||||
long mFStat;
|
||||
PRInt32 mFlags;
|
||||
PRInt32 mCommand;
|
||||
|
||||
/* Private Methods */
|
||||
|
||||
PRInt32 NativeFileOpDirCreate(nsFileSpec* aTarget);
|
||||
PRInt32 NativeFileOpDirRemove(nsFileSpec* aTarget, PRInt32 aFlags);
|
||||
PRInt32 NativeFileOpDirRename(nsFileSpec* aSrc, nsFileSpec* aTarget);
|
||||
PRInt32 NativeFileOpFileCopy(nsFileSpec* aSrc, nsFileSpec* aTarget);
|
||||
PRInt32 NativeFileOpFileDelete(nsFileSpec* aTarget, PRInt32 aFlags);
|
||||
PRInt32 NativeFileOpFileExecute(nsFileSpec* aTarget, nsString* aParams);
|
||||
PRInt32 NativeFileOpFileMove(nsFileSpec* aSrc, nsFileSpec* aTarget);
|
||||
PRInt32 NativeFileOpFileRename(nsFileSpec* aSrc, nsFileSpec* aTarget);
|
||||
PRInt32 NativeFileOpWinShortcutCreate();
|
||||
PRInt32 NativeFileOpMacAliasCreate();
|
||||
PRInt32 NativeFileOpUnixLinkCreate();
|
||||
|
||||
};
|
||||
|
||||
#endif /* nsInstallFileOpItem_h__ */
|
||||
|
||||
336
mozilla/xpinstall/src/nsInstallFolder.cpp
Normal file
336
mozilla/xpinstall/src/nsInstallFolder.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
#include "nsInstall.h"
|
||||
#include "nsInstallFolder.h"
|
||||
|
||||
#include "nscore.h"
|
||||
#include "prtypes.h"
|
||||
#include "nsRepository.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsFileSpec.h"
|
||||
#include "nsSpecialSystemDirectory.h"
|
||||
|
||||
#include "nsFileLocations.h"
|
||||
#include "nsIFileLocator.h"
|
||||
|
||||
struct DirectoryTable
|
||||
{
|
||||
char * directoryName; /* The formal directory name */
|
||||
PRInt32 folderEnum; /* Directory ID */
|
||||
};
|
||||
|
||||
struct DirectoryTable DirectoryTable[] =
|
||||
{
|
||||
{"Plugins", 100 },
|
||||
{"Program", 101 },
|
||||
{"Communicator", 102 },
|
||||
{"User Pick", 103 },
|
||||
{"Temporary", 104 },
|
||||
{"Installed", 105 },
|
||||
{"Current User", 106 },
|
||||
{"Preferences", 107 },
|
||||
{"OS Drive", 108 },
|
||||
{"file:///", 109 },
|
||||
|
||||
{"Components", 110 },
|
||||
{"Chrome", 111 },
|
||||
|
||||
{"Win System", 200 },
|
||||
{"Windows", 201 },
|
||||
|
||||
{"Mac System", 300 },
|
||||
{"Mac Desktop", 301 },
|
||||
{"Mac Trash", 302 },
|
||||
{"Mac Startup", 303 },
|
||||
{"Mac Shutdown", 304 },
|
||||
{"Mac Apple Menu", 305 },
|
||||
{"Mac Control Panel", 306 },
|
||||
{"Mac Extension", 307 },
|
||||
{"Mac Fonts", 308 },
|
||||
{"Mac Preferences", 309 },
|
||||
{"Mac Documents", 310 },
|
||||
|
||||
{"Unix Local", 400 },
|
||||
{"Unix Lib", 401 },
|
||||
|
||||
{"", -1 }
|
||||
};
|
||||
|
||||
|
||||
|
||||
nsInstallFolder::nsInstallFolder(const nsString& aFolderID)
|
||||
{
|
||||
mFileSpec = nsnull;
|
||||
SetDirectoryPath( aFolderID, "");
|
||||
}
|
||||
|
||||
nsInstallFolder::nsInstallFolder(const nsString& aFolderID, const nsString& aRelativePath)
|
||||
{
|
||||
mFileSpec = nsnull;
|
||||
|
||||
/*
|
||||
aFolderID can be either a Folder enum in which case we merely pass it to SetDirectoryPath, or
|
||||
it can be a Directory. If it is the later, it must already exist and of course be a directory
|
||||
not a file.
|
||||
*/
|
||||
|
||||
nsFileSpec dirCheck(aFolderID);
|
||||
if ( (dirCheck.Error() == NS_OK) && (dirCheck.IsDirectory()) && (dirCheck.Exists()))
|
||||
{
|
||||
nsString tempString = aFolderID;
|
||||
tempString += aRelativePath;
|
||||
mFileSpec = new nsFileSpec(tempString);
|
||||
|
||||
// make sure that the directory is created.
|
||||
nsFileSpec(mFileSpec->GetCString(), PR_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDirectoryPath( aFolderID, aRelativePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsInstallFolder::~nsInstallFolder()
|
||||
{
|
||||
if (mFileSpec != nsnull)
|
||||
delete mFileSpec;
|
||||
}
|
||||
|
||||
void
|
||||
nsInstallFolder::GetDirectoryPath(nsString& aDirectoryPath)
|
||||
{
|
||||
aDirectoryPath = "";
|
||||
|
||||
if (mFileSpec != nsnull)
|
||||
{
|
||||
// We want the a NATIVE path.
|
||||
aDirectoryPath.SetString(mFileSpec->GetCString());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsInstallFolder::SetDirectoryPath(const nsString& aFolderID, const nsString& aRelativePath)
|
||||
{
|
||||
if ( aFolderID.EqualsIgnoreCase("User Pick") )
|
||||
{
|
||||
PickDefaultDirectory();
|
||||
return;
|
||||
}
|
||||
else if ( aFolderID.EqualsIgnoreCase("Installed") )
|
||||
{
|
||||
mFileSpec = new nsFileSpec(aRelativePath, PR_TRUE); // creates the directories to the relative path.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
PRInt32 folderDirSpecID = MapNameToEnum(aFolderID);
|
||||
|
||||
switch (folderDirSpecID)
|
||||
{
|
||||
case 100: /////////////////////////////////////////////////////////// Plugins
|
||||
SetAppShellDirectory(nsSpecialFileSpec::App_PluginsDirectory );
|
||||
break;
|
||||
|
||||
case 101: /////////////////////////////////////////////////////////// Program
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::OS_CurrentProcessDirectory ));
|
||||
break;
|
||||
|
||||
case 102: /////////////////////////////////////////////////////////// Communicator
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::OS_CurrentProcessDirectory ));
|
||||
break;
|
||||
|
||||
case 103: /////////////////////////////////////////////////////////// User Pick
|
||||
// we should never be here.
|
||||
mFileSpec = nsnull;
|
||||
break;
|
||||
|
||||
case 104: /////////////////////////////////////////////////////////// Temporary
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::OS_TemporaryDirectory ));
|
||||
break;
|
||||
|
||||
case 105: /////////////////////////////////////////////////////////// Installed
|
||||
// we should never be here.
|
||||
mFileSpec = nsnull;
|
||||
break;
|
||||
|
||||
case 106: /////////////////////////////////////////////////////////// Current User
|
||||
SetAppShellDirectory(nsSpecialFileSpec::App_UserProfileDirectory50 );
|
||||
break;
|
||||
|
||||
case 107: /////////////////////////////////////////////////////////// Preferences
|
||||
SetAppShellDirectory(nsSpecialFileSpec::App_PrefsDirectory50 );
|
||||
break;
|
||||
|
||||
case 108: /////////////////////////////////////////////////////////// OS Drive
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::OS_DriveDirectory ));
|
||||
break;
|
||||
|
||||
case 109: /////////////////////////////////////////////////////////// File URL
|
||||
{
|
||||
nsString tempFileURLString = aFolderID;
|
||||
tempFileURLString += aRelativePath;
|
||||
mFileSpec = new nsFileSpec( nsFileURL(tempFileURLString) );
|
||||
}
|
||||
break;
|
||||
|
||||
case 110: /////////////////////////////////////////////////////////// Components
|
||||
SetAppShellDirectory(nsSpecialFileSpec::App_ComponentsDirectory );
|
||||
break;
|
||||
|
||||
case 111: /////////////////////////////////////////////////////////// Chrome
|
||||
SetAppShellDirectory(nsSpecialFileSpec::App_ChromeDirectory );
|
||||
break;
|
||||
|
||||
case 200: /////////////////////////////////////////////////////////// Win System
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Win_SystemDirectory ));
|
||||
break;
|
||||
|
||||
case 201: /////////////////////////////////////////////////////////// Windows
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Win_WindowsDirectory ));
|
||||
break;
|
||||
|
||||
case 300: /////////////////////////////////////////////////////////// Mac System
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_SystemDirectory ));
|
||||
break;
|
||||
|
||||
case 301: /////////////////////////////////////////////////////////// Mac Desktop
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_DesktopDirectory ));
|
||||
break;
|
||||
|
||||
case 302: /////////////////////////////////////////////////////////// Mac Trash
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_TrashDirectory ));
|
||||
break;
|
||||
|
||||
case 303: /////////////////////////////////////////////////////////// Mac Startup
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_StartupDirectory ));
|
||||
break;
|
||||
|
||||
case 304: /////////////////////////////////////////////////////////// Mac Shutdown
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_StartupDirectory ));
|
||||
break;
|
||||
|
||||
case 305: /////////////////////////////////////////////////////////// Mac Apple Menu
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_AppleMenuDirectory ));
|
||||
break;
|
||||
|
||||
case 306: /////////////////////////////////////////////////////////// Mac Control Panel
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_ControlPanelDirectory ));
|
||||
break;
|
||||
|
||||
case 307: /////////////////////////////////////////////////////////// Mac Extension
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_ExtensionDirectory ));
|
||||
break;
|
||||
|
||||
case 308: /////////////////////////////////////////////////////////// Mac Fonts
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_FontsDirectory ));
|
||||
break;
|
||||
|
||||
case 309: /////////////////////////////////////////////////////////// Mac Preferences
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_PreferencesDirectory ));
|
||||
break;
|
||||
|
||||
case 310: /////////////////////////////////////////////////////////// Mac Documents
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Mac_DocumentsDirectory ));
|
||||
break;
|
||||
|
||||
case 400: /////////////////////////////////////////////////////////// Unix Local
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Unix_LocalDirectory ));
|
||||
break;
|
||||
|
||||
case 401: /////////////////////////////////////////////////////////// Unix Lib
|
||||
mFileSpec = new nsFileSpec( nsSpecialSystemDirectory( nsSpecialSystemDirectory::Unix_LibDirectory ));
|
||||
break;
|
||||
|
||||
|
||||
case -1:
|
||||
default:
|
||||
mFileSpec = nsnull;
|
||||
return;
|
||||
}
|
||||
#ifndef XP_MAC
|
||||
if (aRelativePath.Length() > 0)
|
||||
{
|
||||
nsString tempPath(aRelativePath);
|
||||
|
||||
if (aRelativePath.Last() != '/' || aRelativePath.Last() != '\\')
|
||||
tempPath += '/';
|
||||
|
||||
*mFileSpec += tempPath;
|
||||
}
|
||||
#endif
|
||||
// make sure that the directory is created.
|
||||
nsFileSpec(mFileSpec->GetCString(), PR_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void nsInstallFolder::PickDefaultDirectory()
|
||||
{
|
||||
//FIX: Need to put up a dialog here and set mFileSpec
|
||||
return;
|
||||
}
|
||||
|
||||
/* MapNameToEnum
|
||||
* maps name from the directory table to its enum */
|
||||
PRInt32
|
||||
nsInstallFolder::MapNameToEnum(const nsString& name)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if ( name == "null")
|
||||
return -1;
|
||||
|
||||
while ( DirectoryTable[i].directoryName[0] != 0 )
|
||||
{
|
||||
if ( name.EqualsIgnoreCase(DirectoryTable[i].directoryName) )
|
||||
return DirectoryTable[i].folderEnum;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static NS_DEFINE_IID(kFileLocatorIID, NS_IFILELOCATOR_IID);
|
||||
static NS_DEFINE_IID(kFileLocatorCID, NS_FILELOCATOR_CID);
|
||||
|
||||
void
|
||||
nsInstallFolder::SetAppShellDirectory(PRUint32 value)
|
||||
{
|
||||
nsIFileLocator * appShellLocator;
|
||||
|
||||
nsresult rv = nsComponentManager::CreateInstance(kFileLocatorCID,
|
||||
nsnull,
|
||||
kFileLocatorIID,
|
||||
(void**) &appShellLocator);
|
||||
|
||||
if ( NS_SUCCEEDED(rv) )
|
||||
{
|
||||
mFileSpec = new nsFileSpec();
|
||||
appShellLocator->GetFileLocation(value, mFileSpec);
|
||||
NS_RELEASE(appShellLocator);
|
||||
}
|
||||
}
|
||||
58
mozilla/xpinstall/src/nsInstallFolder.h
Normal file
58
mozilla/xpinstall/src/nsInstallFolder.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Daniel Veditz <dveditz@netscape.com>
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __NS_INSTALLFOLDER_H__
|
||||
#define __NS_INSTALLFOLDER_H__
|
||||
|
||||
#include "nscore.h"
|
||||
#include "prtypes.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsFileSpec.h"
|
||||
#include "nsSpecialSystemDirectory.h"
|
||||
|
||||
class nsInstallFolder
|
||||
{
|
||||
public:
|
||||
|
||||
nsInstallFolder(const nsString& aFolderID);
|
||||
nsInstallFolder(const nsString& aFolderID, const nsString& aRelativePath);
|
||||
virtual ~nsInstallFolder();
|
||||
|
||||
void GetDirectoryPath(nsString& aDirectoryPath);
|
||||
|
||||
private:
|
||||
|
||||
nsFileSpec* mFileSpec;
|
||||
|
||||
void SetDirectoryPath(const nsString& aFolderID, const nsString& aRelativePath);
|
||||
void PickDefaultDirectory();
|
||||
PRInt32 MapNameToEnum(const nsString& name);
|
||||
void SetAppShellDirectory(PRUint32 value);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
52
mozilla/xpinstall/src/nsInstallObject.h
Normal file
52
mozilla/xpinstall/src/nsInstallObject.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifndef nsInstallObject_h__
|
||||
#define nsInstallObject_h__
|
||||
|
||||
#include "prtypes.h"
|
||||
|
||||
class nsInstall;
|
||||
|
||||
class nsInstallObject
|
||||
{
|
||||
public:
|
||||
/* Public Methods */
|
||||
nsInstallObject(nsInstall* inInstall) {mInstall = inInstall; }
|
||||
|
||||
/* Override with your set-up action */
|
||||
virtual PRInt32 Prepare() = 0;
|
||||
|
||||
/* Override with your Completion action */
|
||||
virtual PRInt32 Complete() = 0;
|
||||
|
||||
/* Override with an explanatory string for the progress dialog */
|
||||
virtual char* toString() = 0;
|
||||
|
||||
/* Override with your clean-up function */
|
||||
virtual void Abort() = 0;
|
||||
|
||||
/* should these be protected? */
|
||||
virtual PRBool CanUninstall() = 0;
|
||||
virtual PRBool RegisterPackageNode() = 0;
|
||||
|
||||
protected:
|
||||
nsInstall* mInstall;
|
||||
};
|
||||
|
||||
#endif /* nsInstallObject_h__ */
|
||||
986
mozilla/xpinstall/src/nsInstallPatch.cpp
Normal file
986
mozilla/xpinstall/src/nsInstallPatch.cpp
Normal file
@@ -0,0 +1,986 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#include "nsFileSpec.h"
|
||||
#include "prmem.h"
|
||||
#include "nsInstall.h"
|
||||
#include "nsInstallPatch.h"
|
||||
#include "nsInstallResources.h"
|
||||
#include "nsIDOMInstallVersion.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "gdiff.h"
|
||||
|
||||
#include "VerReg.h"
|
||||
#include "ScheduledTasks.h"
|
||||
#include "plstr.h"
|
||||
#include "xp_file.h" /* for XP_PlatformFileToURL */
|
||||
|
||||
|
||||
#ifdef XP_MAC
|
||||
#include "PatchableAppleSingle.h"
|
||||
#endif
|
||||
|
||||
#define BUFSIZE 32768
|
||||
#define OPSIZE 1
|
||||
#define MAXCMDSIZE 12
|
||||
#define SRCFILE 0
|
||||
#define OUTFILE 1
|
||||
|
||||
#define getshort(s) (uint16)( ((uchar)*(s) << 8) + ((uchar)*((s)+1)) )
|
||||
|
||||
#define getlong(s) \
|
||||
(uint32)( ((uchar)*(s) << 24) + ((uchar)*((s)+1) << 16 ) + \
|
||||
((uchar)*((s)+2) << 8) + ((uchar)*((s)+3)) )
|
||||
|
||||
|
||||
|
||||
static int32 gdiff_parseHeader( pDIFFDATA dd );
|
||||
static int32 gdiff_validateFile( pDIFFDATA dd, int file );
|
||||
static int32 gdiff_valCRC32( pDIFFDATA dd, PRFileDesc* fh, uint32 chksum );
|
||||
static int32 gdiff_ApplyPatch( pDIFFDATA dd );
|
||||
static int32 gdiff_getdiff( pDIFFDATA dd, uchar *buffer, uint32 length );
|
||||
static int32 gdiff_add( pDIFFDATA dd, uint32 count );
|
||||
static int32 gdiff_copy( pDIFFDATA dd, uint32 position, uint32 count );
|
||||
static int32 gdiff_validateFile( pDIFFDATA dd, int file );
|
||||
|
||||
|
||||
nsInstallPatch::nsInstallPatch( nsInstall* inInstall,
|
||||
const nsString& inVRName,
|
||||
const nsString& inVInfo,
|
||||
const nsString& inJarLocation,
|
||||
PRInt32 *error)
|
||||
|
||||
: nsInstallObject(inInstall)
|
||||
{
|
||||
char tempTargetFile[MAXREGPATHLEN];
|
||||
char* tempVersionString = inVRName.ToNewCString();
|
||||
|
||||
PRInt32 err = VR_GetPath(tempVersionString, MAXREGPATHLEN, tempTargetFile );
|
||||
|
||||
delete [] tempVersionString;
|
||||
|
||||
if (err != REGERR_OK)
|
||||
{
|
||||
*error = nsInstall::NO_SUCH_COMPONENT;
|
||||
return;
|
||||
}
|
||||
nsString folderSpec(tempTargetFile);
|
||||
|
||||
mPatchFile = nsnull;
|
||||
mTargetFile = nsnull;
|
||||
mPatchedFile = nsnull;
|
||||
mRegistryName = new nsString(inVRName);
|
||||
mJarLocation = new nsString(inJarLocation);
|
||||
mTargetFile = new nsFileSpec(folderSpec);
|
||||
|
||||
mVersionInfo = new nsInstallVersion();
|
||||
|
||||
mVersionInfo->Init(inVInfo);
|
||||
|
||||
}
|
||||
|
||||
|
||||
nsInstallPatch::nsInstallPatch( nsInstall* inInstall,
|
||||
const nsString& inVRName,
|
||||
const nsString& inVInfo,
|
||||
const nsString& inJarLocation,
|
||||
const nsString& folderSpec,
|
||||
const nsString& inPartialPath,
|
||||
PRInt32 *error)
|
||||
|
||||
: nsInstallObject(inInstall)
|
||||
{
|
||||
if ((inInstall == nsnull) || (inVRName == "null") || (inJarLocation == "null"))
|
||||
{
|
||||
*error = nsInstall::INVALID_ARGUMENTS;
|
||||
return;
|
||||
}
|
||||
|
||||
mPatchFile = nsnull;
|
||||
mTargetFile = nsnull;
|
||||
mPatchedFile = nsnull;
|
||||
mRegistryName = new nsString(inVRName);
|
||||
mJarLocation = new nsString(inJarLocation);
|
||||
mVersionInfo = new nsInstallVersion();
|
||||
|
||||
mVersionInfo->Init(inVInfo);
|
||||
|
||||
mTargetFile = new nsFileSpec(folderSpec);
|
||||
if(inPartialPath != "null")
|
||||
*mTargetFile += inPartialPath;
|
||||
}
|
||||
|
||||
nsInstallPatch::~nsInstallPatch()
|
||||
{
|
||||
if (mVersionInfo)
|
||||
delete mVersionInfo;
|
||||
|
||||
if (mTargetFile)
|
||||
delete mTargetFile;
|
||||
|
||||
if (mJarLocation)
|
||||
delete mJarLocation;
|
||||
|
||||
if (mRegistryName)
|
||||
delete mRegistryName;
|
||||
|
||||
if (mPatchedFile)
|
||||
delete mPatchedFile;
|
||||
|
||||
if (mPatchFile)
|
||||
delete mPatchFile;
|
||||
|
||||
}
|
||||
|
||||
|
||||
PRInt32 nsInstallPatch::Prepare()
|
||||
{
|
||||
PRInt32 err;
|
||||
PRBool deleteOldSrc;
|
||||
|
||||
if (mTargetFile == nsnull)
|
||||
return nsInstall::INVALID_ARGUMENTS;
|
||||
|
||||
if (mTargetFile->Exists())
|
||||
{
|
||||
if (mTargetFile->IsFile())
|
||||
{
|
||||
err = nsInstall::SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = nsInstall::FILE_IS_DIRECTORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err = nsInstall::FILE_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
if (err != nsInstall::SUCCESS)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mInstall->ExtractFileFromJar(*mJarLocation, mTargetFile, &mPatchFile);
|
||||
|
||||
|
||||
nsFileSpec *fileName = nsnull;
|
||||
nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );
|
||||
|
||||
mInstall->GetPatch(&ikey, fileName);
|
||||
|
||||
if (fileName != nsnull)
|
||||
{
|
||||
deleteOldSrc = PR_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName = mTargetFile;
|
||||
deleteOldSrc = PR_FALSE;
|
||||
}
|
||||
|
||||
err = NativePatch( *fileName, // the file to patch
|
||||
*mPatchFile, // the patch that was extracted from the jarfile
|
||||
&mPatchedFile); // the new patched file
|
||||
|
||||
if (err != nsInstall::SUCCESS)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
PR_ASSERT(mPatchedFile != nsnull);
|
||||
mInstall->AddPatch(&ikey, mPatchedFile );
|
||||
|
||||
if ( deleteOldSrc )
|
||||
{
|
||||
DeleteFileNowOrSchedule(*fileName );
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
PRInt32 nsInstallPatch::Complete()
|
||||
{
|
||||
if ((mInstall == nsnull) || (mVersionInfo == nsnull) || (mPatchedFile == nsnull) || (mTargetFile == nsnull))
|
||||
{
|
||||
return nsInstall::INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
PRInt32 err = nsInstall::SUCCESS;
|
||||
|
||||
nsFileSpec *fileName = nsnull;
|
||||
nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );
|
||||
|
||||
mInstall->GetPatch(&ikey, fileName);
|
||||
|
||||
if (fileName != nsnull && (*fileName == *mPatchedFile) )
|
||||
{
|
||||
// the patch has not been superceded--do final replacement
|
||||
err = ReplaceFileNowOrSchedule( *mTargetFile, *mPatchedFile);
|
||||
if ( 0 == err || nsInstall::REBOOT_NEEDED == err )
|
||||
{
|
||||
nsString tempVersionString;
|
||||
mVersionInfo->ToString(tempVersionString);
|
||||
|
||||
char* tempRegName = mRegistryName->ToNewCString();
|
||||
char* tempVersion = tempVersionString.ToNewCString();
|
||||
|
||||
err = VR_Install( tempRegName,
|
||||
(char*) (const char *) nsNSPRPath(*mTargetFile),
|
||||
tempVersion,
|
||||
PR_FALSE );
|
||||
|
||||
delete [] tempRegName;
|
||||
delete [] tempVersion;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
err = nsInstall::UNEXPECTED_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing -- old intermediate patched file was
|
||||
// deleted by a superceding patch
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void nsInstallPatch::Abort()
|
||||
{
|
||||
nsFileSpec *fileName = nsnull;
|
||||
nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );
|
||||
|
||||
mInstall->GetPatch(&ikey, fileName);
|
||||
|
||||
if (fileName != nsnull && (*fileName == *mPatchedFile) )
|
||||
{
|
||||
DeleteFileNowOrSchedule( *mPatchedFile );
|
||||
}
|
||||
}
|
||||
|
||||
char* nsInstallPatch::toString()
|
||||
{
|
||||
char* buffer = new char[1024];
|
||||
|
||||
// FIX! sprintf( buffer, nsInstallResources::GetPatchFileString(), mPatchedFile->GetCString());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
PRBool
|
||||
nsInstallPatch::CanUninstall()
|
||||
{
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsInstallPatch::RegisterPackageNode()
|
||||
{
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
|
||||
PRInt32
|
||||
nsInstallPatch::NativePatch(const nsFileSpec &sourceFile, const nsFileSpec &patchFile, nsFileSpec **newFile)
|
||||
{
|
||||
|
||||
DIFFDATA *dd;
|
||||
PRInt32 status = GDIFF_ERR_MEM;
|
||||
char *tmpurl = NULL;
|
||||
char *realfile = PL_strdup(nsNSPRPath(sourceFile)); // needs to be sourceFile!!!
|
||||
nsFileSpec outFileSpec = sourceFile;
|
||||
|
||||
dd = (DIFFDATA *)PR_Calloc( 1, sizeof(DIFFDATA));
|
||||
if (dd != NULL)
|
||||
{
|
||||
dd->databuf = (uchar*)PR_Malloc(BUFSIZE);
|
||||
if (dd->databuf == NULL)
|
||||
{
|
||||
status = GDIFF_ERR_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
dd->bufsize = BUFSIZE;
|
||||
|
||||
// validate patch header & check for special instructions
|
||||
dd->fDiff = PR_Open (nsNSPRPath(patchFile), PR_RDONLY, 0666);
|
||||
|
||||
|
||||
if (dd->fDiff != NULL)
|
||||
{
|
||||
status = gdiff_parseHeader(dd);
|
||||
} else {
|
||||
status = GDIFF_ERR_ACCESS;
|
||||
}
|
||||
|
||||
#ifdef dono
|
||||
#ifdef WIN32
|
||||
|
||||
/* unbind Win32 images */
|
||||
if ( dd->bWin32BoundImage && status == GDIFF_OK ) {
|
||||
tmpurl = WH_TempName( xpURL, NULL );
|
||||
if ( tmpurl != NULL ) {
|
||||
if (su_unbind( srcfile, srctype, tmpurl, xpURL ))
|
||||
{
|
||||
PL_strfree(realfile);
|
||||
realfile = tmpurl;
|
||||
realtype = xpURL;
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
status = GDIFF_ERR_MEM;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef XP_MAC
|
||||
|
||||
if ( dd->bMacAppleSingle && status == GDIFF_OK )
|
||||
{
|
||||
// create a tmp file, so that we can AppleSingle the src file
|
||||
nsSpecialSystemDirectory tempMacFile(nsSpecialSystemDirectory::OS_TemporaryDirectory);
|
||||
nsString srcName = sourceFile.GetLeafName();
|
||||
tempMacFile.SetLeafName(srcName);
|
||||
tempMacFile.MakeUnique();
|
||||
|
||||
// Encode!
|
||||
// Encode src file, and put into temp file
|
||||
FSSpec sourceSpec = sourceFile.GetFSSpec();
|
||||
FSSpec tempSpec = tempMacFile.GetFSSpec();
|
||||
|
||||
status = PAS_EncodeFile(&sourceSpec, &tempSpec);
|
||||
|
||||
if (status == noErr)
|
||||
{
|
||||
// set
|
||||
PL_strfree(realfile);
|
||||
realfile = PL_strdup(nsNSPRPath(tempMacFile));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
if (status != NS_OK)
|
||||
goto cleanup;
|
||||
|
||||
// make a unique file at the same location of our source file (FILENAME-ptch.EXT)
|
||||
nsString patchFileName = "-ptch";
|
||||
nsString newFileName = sourceFile.GetLeafName();
|
||||
|
||||
PRInt32 index;
|
||||
if ((index = newFileName.RFind(".")) > 0)
|
||||
{
|
||||
nsString extention;
|
||||
nsString fileName;
|
||||
newFileName.Right(extention, (newFileName.Length() - index) );
|
||||
newFileName.Left(fileName, (newFileName.Length() - (newFileName.Length() - index)));
|
||||
newFileName = fileName + patchFileName + extention;
|
||||
|
||||
} else {
|
||||
newFileName += patchFileName;
|
||||
}
|
||||
|
||||
|
||||
outFileSpec.SetLeafName(newFileName); //????
|
||||
outFileSpec.MakeUnique();
|
||||
|
||||
char *outFile = PL_strdup(nsNSPRPath(outFileSpec));
|
||||
|
||||
// apply patch to the source file
|
||||
dd->fSrc = PR_Open ( realfile, PR_RDONLY, 0666);
|
||||
dd->fOut = PR_Open ( outFile, PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0666);
|
||||
|
||||
if (dd->fSrc != NULL && dd->fOut != NULL)
|
||||
{
|
||||
status = gdiff_validateFile (dd, SRCFILE);
|
||||
|
||||
// specify why diff failed
|
||||
if (status == GDIFF_ERR_CHECKSUM)
|
||||
status = GDIFF_ERR_CHECKSUM_TARGET;
|
||||
|
||||
if (status == GDIFF_OK)
|
||||
status = gdiff_ApplyPatch(dd);
|
||||
|
||||
if (status == GDIFF_OK)
|
||||
status = gdiff_validateFile (dd, OUTFILE);
|
||||
|
||||
if (status == GDIFF_ERR_CHECKSUM)
|
||||
status = GDIFF_ERR_CHECKSUM_RESULT;
|
||||
|
||||
if (status == GDIFF_OK)
|
||||
{
|
||||
*newFile = &outFileSpec;
|
||||
if ( outFile != nsnull)
|
||||
PL_strfree( outFile );
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
status = GDIFF_ERR_ACCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef XP_MAC
|
||||
if ( dd->bMacAppleSingle && status == GDIFF_OK )
|
||||
{
|
||||
// create another file, so that we can decode somewhere
|
||||
nsFileSpec anotherName = outFileSpec;
|
||||
anotherName.MakeUnique();
|
||||
|
||||
// Close the out file so that we can read it
|
||||
PR_Close( dd->fOut );
|
||||
dd->fOut = NULL;
|
||||
|
||||
|
||||
FSSpec outSpec = outFileSpec.GetFSSpec();
|
||||
FSSpec anotherSpec = anotherName.GetFSSpec();
|
||||
|
||||
|
||||
status = PAS_DecodeFile(&outSpec, &anotherSpec);
|
||||
if (status != noErr)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
nsFileSpec parent;
|
||||
|
||||
outFileSpec.GetParent(parent);
|
||||
|
||||
outFileSpec.Delete(PR_FALSE);
|
||||
anotherName.Copy(parent);
|
||||
|
||||
*newFile = &anotherName;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
cleanup:
|
||||
if ( dd != NULL )
|
||||
{
|
||||
if ( dd->fSrc != nsnull )
|
||||
PR_Close( dd->fSrc );
|
||||
|
||||
|
||||
if ( dd->fDiff != nsnull )
|
||||
PR_Close( dd->fDiff );
|
||||
|
||||
if ( dd->fOut != nsnull )
|
||||
{
|
||||
PR_Close( dd->fOut );
|
||||
}
|
||||
|
||||
if ( status != GDIFF_OK )
|
||||
//XP_FileRemove( outfile, outtype );
|
||||
newFile = NULL;
|
||||
|
||||
PR_FREEIF( dd->databuf );
|
||||
PR_FREEIF( dd->oldChecksum );
|
||||
PR_FREEIF( dd->newChecksum );
|
||||
PR_DELETE(dd);
|
||||
}
|
||||
|
||||
if ( tmpurl != NULL ) {
|
||||
//XP_FileRemove( tmpurl, xpURL );
|
||||
tmpurl = NULL;
|
||||
PR_DELETE( tmpurl );
|
||||
}
|
||||
|
||||
/* lets map any GDIFF error to nice SU errors */
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case GDIFF_OK:
|
||||
break;
|
||||
case GDIFF_ERR_HEADER:
|
||||
case GDIFF_ERR_BADDIFF:
|
||||
case GDIFF_ERR_OPCODE:
|
||||
case GDIFF_ERR_CHKSUMTYPE:
|
||||
status = nsInstall::PATCH_BAD_DIFF;
|
||||
break;
|
||||
case GDIFF_ERR_CHECKSUM_TARGET:
|
||||
status = nsInstall::PATCH_BAD_CHECKSUM_TARGET;
|
||||
break;
|
||||
case GDIFF_ERR_CHECKSUM_RESULT:
|
||||
status = nsInstall::PATCH_BAD_CHECKSUM_RESULT;
|
||||
break;
|
||||
case GDIFF_ERR_OLDFILE:
|
||||
case GDIFF_ERR_ACCESS:
|
||||
case GDIFF_ERR_MEM:
|
||||
case GDIFF_ERR_UNKNOWN:
|
||||
default:
|
||||
status = nsInstall::UNEXPECTED_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
// return -1; //old return value
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
nsInstallPatch::HashFilePath(const nsFilePath& aPath)
|
||||
{
|
||||
PRUint32 rv = 0;
|
||||
|
||||
char* cPath = PL_strdup(nsNSPRPath(aPath));
|
||||
|
||||
if(cPath != nsnull)
|
||||
{
|
||||
char ch;
|
||||
char* filePath = PL_strdup(cPath);
|
||||
PRUint32 cnt=0;
|
||||
|
||||
while ((ch = *filePath++) != 0)
|
||||
{
|
||||
// FYI: rv = rv*37 + ch
|
||||
rv = ((rv << 5) + (rv << 2) + rv) + ch;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
for (PRUint32 i=0; i<=cnt; i++)
|
||||
*filePath--;
|
||||
PL_strfree(filePath);
|
||||
}
|
||||
|
||||
PL_strfree(cPath);
|
||||
|
||||
return (void*)rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* gdiff_parseHeader()
|
||||
*
|
||||
* reads and validates the GDIFF header info
|
||||
*---------------------------------------------------------
|
||||
*/
|
||||
static
|
||||
int32 gdiff_parseHeader( pDIFFDATA dd )
|
||||
{
|
||||
int32 err = GDIFF_OK;
|
||||
uint8 cslen;
|
||||
uint8 oldcslen;
|
||||
uint8 newcslen;
|
||||
uint32 nRead;
|
||||
uchar header[GDIFF_HEADERSIZE];
|
||||
|
||||
/* Read the fixed-size part of the header */
|
||||
|
||||
nRead = PR_Read (dd->fDiff, header, GDIFF_HEADERSIZE);
|
||||
if ( nRead != GDIFF_HEADERSIZE ||
|
||||
memcmp( header, GDIFF_MAGIC, GDIFF_MAGIC_LEN ) != 0 ||
|
||||
header[GDIFF_VER_POS] != GDIFF_VER )
|
||||
{
|
||||
err = GDIFF_ERR_HEADER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* get the checksum information */
|
||||
|
||||
dd->checksumType = header[GDIFF_CS_POS];
|
||||
cslen = header[GDIFF_CSLEN_POS];
|
||||
|
||||
if ( cslen > 0 )
|
||||
{
|
||||
oldcslen = cslen / 2;
|
||||
newcslen = cslen - oldcslen;
|
||||
PR_ASSERT( newcslen == oldcslen );
|
||||
|
||||
dd->checksumLength = oldcslen;
|
||||
dd->oldChecksum = (uchar*)PR_MALLOC(oldcslen);
|
||||
dd->newChecksum = (uchar*)PR_MALLOC(newcslen);
|
||||
|
||||
if ( dd->oldChecksum != NULL && dd->newChecksum != NULL )
|
||||
{
|
||||
nRead = PR_Read (dd->fDiff, dd->oldChecksum, oldcslen);
|
||||
if ( nRead == oldcslen )
|
||||
{
|
||||
nRead = PR_Read (dd->fDiff, dd->newChecksum, newcslen);
|
||||
if ( nRead != newcslen ) {
|
||||
err = GDIFF_ERR_HEADER;
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = GDIFF_ERR_HEADER;
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = GDIFF_ERR_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* get application data, if any */
|
||||
|
||||
if ( err == GDIFF_OK )
|
||||
{
|
||||
uint32 appdataSize;
|
||||
uchar *buf;
|
||||
uchar lenbuf[GDIFF_APPDATALEN];
|
||||
|
||||
nRead = PR_Read(dd->fDiff, lenbuf, GDIFF_APPDATALEN);
|
||||
if ( nRead == GDIFF_APPDATALEN )
|
||||
{
|
||||
appdataSize = getlong(lenbuf);
|
||||
|
||||
if ( appdataSize > 0 )
|
||||
{
|
||||
buf = (uchar *)PR_MALLOC( appdataSize );
|
||||
|
||||
if ( buf != NULL )
|
||||
{
|
||||
nRead = PR_Read (dd->fDiff, buf, appdataSize);
|
||||
if ( nRead == appdataSize )
|
||||
{
|
||||
if ( 0 == memcmp( buf, APPFLAG_W32BOUND, appdataSize ) )
|
||||
dd->bWin32BoundImage = TRUE;
|
||||
|
||||
if ( 0 == memcmp( buf, APPFLAG_APPLESINGLE, appdataSize ) )
|
||||
dd->bMacAppleSingle = TRUE;
|
||||
}
|
||||
else {
|
||||
err = GDIFF_ERR_HEADER;
|
||||
}
|
||||
|
||||
PR_DELETE( buf );
|
||||
}
|
||||
else {
|
||||
err = GDIFF_ERR_MEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = GDIFF_ERR_HEADER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* gdiff_validateFile()
|
||||
*
|
||||
* computes the checksum of the file and compares it to
|
||||
* the value stored in the GDIFF header
|
||||
*---------------------------------------------------------
|
||||
*/
|
||||
static
|
||||
int32 gdiff_validateFile( pDIFFDATA dd, int file )
|
||||
{
|
||||
int32 result;
|
||||
PRFileDesc* fh;
|
||||
uchar* chksum;
|
||||
|
||||
/* which file are we dealing with? */
|
||||
if ( file == SRCFILE ) {
|
||||
fh = dd->fSrc;
|
||||
chksum = dd->oldChecksum;
|
||||
}
|
||||
else { /* OUTFILE */
|
||||
fh = dd->fOut;
|
||||
chksum = dd->newChecksum;
|
||||
}
|
||||
|
||||
/* make sure file's at beginning */
|
||||
PR_Seek( fh, 0, PR_SEEK_SET );
|
||||
|
||||
/* calculate appropriate checksum */
|
||||
switch (dd->checksumType)
|
||||
{
|
||||
case GDIFF_CS_NONE:
|
||||
result = GDIFF_OK;
|
||||
break;
|
||||
|
||||
|
||||
case GDIFF_CS_CRC32:
|
||||
if ( dd->checksumLength == CRC32_LEN )
|
||||
result = gdiff_valCRC32( dd, fh, getlong(chksum) );
|
||||
else
|
||||
result = GDIFF_ERR_HEADER;
|
||||
break;
|
||||
|
||||
|
||||
case GDIFF_CS_MD5:
|
||||
|
||||
|
||||
case GDIFF_CS_SHA:
|
||||
|
||||
|
||||
default:
|
||||
/* unsupported checksum type */
|
||||
result = GDIFF_ERR_CHKSUMTYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* reset file position to beginning and return status */
|
||||
PR_Seek( fh, 0, PR_SEEK_SET );
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* gdiff_valCRC32()
|
||||
*
|
||||
* computes the checksum of the file and compares it to
|
||||
* the passed in checksum. Assumes file is positioned at
|
||||
* beginning.
|
||||
*---------------------------------------------------------
|
||||
*/
|
||||
static
|
||||
int32 gdiff_valCRC32( pDIFFDATA dd, PRFileDesc* fh, uint32 chksum )
|
||||
{
|
||||
uint32 crc;
|
||||
uint32 nRead;
|
||||
|
||||
crc = crc32(0L, Z_NULL, 0);
|
||||
|
||||
nRead = PR_Read (fh, dd->databuf, dd->bufsize);
|
||||
while ( nRead > 0 )
|
||||
{
|
||||
crc = crc32( crc, dd->databuf, nRead );
|
||||
nRead = PR_Read (fh, dd->databuf, dd->bufsize);
|
||||
}
|
||||
|
||||
if ( crc == chksum )
|
||||
return GDIFF_OK;
|
||||
else
|
||||
return GDIFF_ERR_CHECKSUM;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* gdiff_ApplyPatch()
|
||||
*
|
||||
* Combines patch data with source file to produce the
|
||||
* new target file. Assumes all three files have been
|
||||
* opened, GDIFF header read, and all other setup complete
|
||||
*
|
||||
* The GDIFF patch is processed sequentially which random
|
||||
* access is neccessary for the source file.
|
||||
*---------------------------------------------------------
|
||||
*/
|
||||
static
|
||||
int32 gdiff_ApplyPatch( pDIFFDATA dd )
|
||||
{
|
||||
int32 err;
|
||||
XP_Bool done;
|
||||
uint32 position;
|
||||
uint32 count;
|
||||
uchar opcode;
|
||||
uchar cmdbuf[MAXCMDSIZE];
|
||||
|
||||
done = FALSE;
|
||||
while ( !done ) {
|
||||
err = gdiff_getdiff( dd, &opcode, OPSIZE );
|
||||
if ( err != GDIFF_OK )
|
||||
break;
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case ENDDIFF:
|
||||
done = TRUE;
|
||||
break;
|
||||
|
||||
case ADD16:
|
||||
err = gdiff_getdiff( dd, cmdbuf, ADD16SIZE );
|
||||
if ( err == GDIFF_OK ) {
|
||||
err = gdiff_add( dd, getshort( cmdbuf ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case ADD32:
|
||||
err = gdiff_getdiff( dd, cmdbuf, ADD32SIZE );
|
||||
if ( err == GDIFF_OK ) {
|
||||
err = gdiff_add( dd, getlong( cmdbuf ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case COPY16BYTE:
|
||||
err = gdiff_getdiff( dd, cmdbuf, COPY16BYTESIZE );
|
||||
if ( err == GDIFF_OK ) {
|
||||
position = getshort( cmdbuf );
|
||||
count = *(cmdbuf + sizeof(short));
|
||||
err = gdiff_copy( dd, position, count );
|
||||
}
|
||||
break;
|
||||
|
||||
case COPY16SHORT:
|
||||
err = gdiff_getdiff( dd, cmdbuf, COPY16SHORTSIZE );
|
||||
if ( err == GDIFF_OK ) {
|
||||
position = getshort( cmdbuf );
|
||||
count = getshort(cmdbuf + sizeof(short));
|
||||
err = gdiff_copy( dd, position, count );
|
||||
}
|
||||
break;
|
||||
|
||||
case COPY16LONG:
|
||||
err = gdiff_getdiff( dd, cmdbuf, COPY16LONGSIZE );
|
||||
if ( err == GDIFF_OK ) {
|
||||
position = getshort( cmdbuf );
|
||||
count = getlong(cmdbuf + sizeof(short));
|
||||
err = gdiff_copy( dd, position, count );
|
||||
}
|
||||
break;
|
||||
|
||||
case COPY32BYTE:
|
||||
err = gdiff_getdiff( dd, cmdbuf, COPY32BYTESIZE );
|
||||
if ( err == GDIFF_OK ) {
|
||||
position = getlong( cmdbuf );
|
||||
count = *(cmdbuf + sizeof(long));
|
||||
err = gdiff_copy( dd, position, count );
|
||||
}
|
||||
break;
|
||||
|
||||
case COPY32SHORT:
|
||||
err = gdiff_getdiff( dd, cmdbuf, COPY32SHORTSIZE );
|
||||
if ( err == GDIFF_OK ) {
|
||||
position = getlong( cmdbuf );
|
||||
count = getshort(cmdbuf + sizeof(long));
|
||||
err = gdiff_copy( dd, position, count );
|
||||
}
|
||||
break;
|
||||
|
||||
case COPY32LONG:
|
||||
err = gdiff_getdiff( dd, cmdbuf, COPY32LONGSIZE );
|
||||
if ( err == GDIFF_OK ) {
|
||||
position = getlong( cmdbuf );
|
||||
count = getlong(cmdbuf + sizeof(long));
|
||||
err = gdiff_copy( dd, position, count );
|
||||
}
|
||||
break;
|
||||
|
||||
case COPY64:
|
||||
/* we don't support 64-bit file positioning yet */
|
||||
err = GDIFF_ERR_OPCODE;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = gdiff_add( dd, opcode );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( err != GDIFF_OK )
|
||||
done = TRUE;
|
||||
}
|
||||
|
||||
/* return status */
|
||||
return (err);
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* gdiff_getdiff()
|
||||
*
|
||||
* reads the next "length" bytes of the diff into "buffer"
|
||||
*
|
||||
* XXX: need a diff buffer to optimize reads!
|
||||
*---------------------------------------------------------
|
||||
*/
|
||||
static
|
||||
int32 gdiff_getdiff( pDIFFDATA dd, uchar *buffer, uint32 length )
|
||||
{
|
||||
uint32 bytesRead;
|
||||
|
||||
bytesRead = PR_Read (dd->fDiff, buffer, length);
|
||||
if ( bytesRead != length )
|
||||
return GDIFF_ERR_BADDIFF;
|
||||
|
||||
return GDIFF_OK;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* gdiff_add()
|
||||
*
|
||||
* append "count" bytes from diff file to new file
|
||||
*---------------------------------------------------------
|
||||
*/
|
||||
static
|
||||
int32 gdiff_add( pDIFFDATA dd, uint32 count )
|
||||
{
|
||||
int32 err = GDIFF_OK;
|
||||
uint32 nRead;
|
||||
uint32 chunksize;
|
||||
|
||||
while ( count > 0 ) {
|
||||
chunksize = ( count > dd->bufsize) ? dd->bufsize : count;
|
||||
nRead = PR_Read (dd->fDiff, dd->databuf, chunksize);
|
||||
if ( nRead != chunksize ) {
|
||||
err = GDIFF_ERR_BADDIFF;
|
||||
break;
|
||||
}
|
||||
|
||||
PR_Write (dd->fOut, dd->databuf, chunksize);
|
||||
|
||||
count -= chunksize;
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* gdiff_copy()
|
||||
*
|
||||
* copy "count" bytes from "position" in source file
|
||||
*---------------------------------------------------------
|
||||
*/
|
||||
static
|
||||
int32 gdiff_copy( pDIFFDATA dd, uint32 position, uint32 count )
|
||||
{
|
||||
int32 err = GDIFF_OK;
|
||||
uint32 nRead;
|
||||
uint32 chunksize;
|
||||
|
||||
PR_Seek (dd->fSrc, position, PR_SEEK_SET);
|
||||
|
||||
while ( count > 0 ) {
|
||||
chunksize = (count > dd->bufsize) ? dd->bufsize : count;
|
||||
|
||||
nRead = PR_Read (dd->fSrc, dd->databuf, chunksize);
|
||||
if ( nRead != chunksize ) {
|
||||
err = GDIFF_ERR_OLDFILE;
|
||||
break;
|
||||
}
|
||||
|
||||
PR_Write (dd->fOut, dd->databuf, chunksize);
|
||||
|
||||
count -= chunksize;
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
79
mozilla/xpinstall/src/nsInstallPatch.h
Normal file
79
mozilla/xpinstall/src/nsInstallPatch.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifndef nsInstallPatch_h__
|
||||
#define nsInstallPatch_h__
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "nsInstallObject.h"
|
||||
|
||||
#include "nsInstall.h"
|
||||
#include "nsInstallFolder.h"
|
||||
#include "nsInstallVersion.h"
|
||||
|
||||
|
||||
class nsInstallPatch : public nsInstallObject
|
||||
{
|
||||
public:
|
||||
|
||||
nsInstallPatch( nsInstall* inInstall,
|
||||
const nsString& inVRName,
|
||||
const nsString& inVInfo,
|
||||
const nsString& inJarLocation,
|
||||
const nsString& folderSpec,
|
||||
const nsString& inPartialPath,
|
||||
PRInt32 *error);
|
||||
|
||||
nsInstallPatch( nsInstall* inInstall,
|
||||
const nsString& inVRName,
|
||||
const nsString& inVInfo,
|
||||
const nsString& inJarLocation,
|
||||
PRInt32 *error);
|
||||
|
||||
virtual ~nsInstallPatch();
|
||||
|
||||
PRInt32 Prepare();
|
||||
PRInt32 Complete();
|
||||
void Abort();
|
||||
char* toString();
|
||||
|
||||
PRBool CanUninstall();
|
||||
PRBool RegisterPackageNode();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
nsInstallVersion *mVersionInfo;
|
||||
|
||||
nsFileSpec *mTargetFile;
|
||||
nsFileSpec *mPatchFile;
|
||||
nsFileSpec *mPatchedFile;
|
||||
|
||||
nsString *mJarLocation;
|
||||
nsString *mRegistryName;
|
||||
|
||||
|
||||
|
||||
PRInt32 NativePatch(const nsFileSpec &sourceFile, const nsFileSpec &patchfile, nsFileSpec **newFile);
|
||||
void* HashFilePath(const nsFilePath& aPath);
|
||||
};
|
||||
|
||||
#endif /* nsInstallPatch_h__ */
|
||||
343
mozilla/xpinstall/src/nsInstallProgressDialog.cpp
Normal file
343
mozilla/xpinstall/src/nsInstallProgressDialog.cpp
Normal file
@@ -0,0 +1,343 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "nsIXPInstallProgress.h"
|
||||
#include "nsInstallProgressDialog.h"
|
||||
|
||||
#include "nsIAppShellComponentImpl.h"
|
||||
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIDocumentViewer.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsINameSpaceManager.h"
|
||||
#include "nsIContentViewer.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsINetService.h"
|
||||
|
||||
#include "nsIWebShell.h"
|
||||
#include "nsIWebShellWindow.h"
|
||||
|
||||
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
|
||||
static NS_DEFINE_IID( kAppShellServiceCID, NS_APPSHELL_SERVICE_CID );
|
||||
static NS_DEFINE_IID( kNetServiceCID, NS_NETSERVICE_CID );
|
||||
|
||||
// Utility to set element attribute.
|
||||
static nsresult setAttribute( nsIDOMXULDocument *doc,
|
||||
const char *id,
|
||||
const char *name,
|
||||
const nsString &value ) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if ( doc ) {
|
||||
// Find specified element.
|
||||
nsCOMPtr<nsIDOMElement> elem;
|
||||
rv = doc->GetElementById( id, getter_AddRefs( elem ) );
|
||||
if ( elem ) {
|
||||
// Set the text attribute.
|
||||
rv = elem->SetAttribute( name, value );
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
} else {
|
||||
DEBUG_PRINTF( PR_STDOUT, "%s %d: SetAttribute failed, rv=0x%X\n",
|
||||
__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
} else {
|
||||
DEBUG_PRINTF( PR_STDOUT, "%s %d: GetElementById failed, rv=0x%X\n",
|
||||
__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
} else {
|
||||
rv = NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
// Utility to get element attribute.
|
||||
static nsresult getAttribute( nsIDOMXULDocument *doc,
|
||||
const char *id,
|
||||
const char *name,
|
||||
nsString &value ) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if ( doc ) {
|
||||
// Find specified element.
|
||||
nsCOMPtr<nsIDOMElement> elem;
|
||||
rv = doc->GetElementById( id, getter_AddRefs( elem ) );
|
||||
if ( elem ) {
|
||||
// Set the text attribute.
|
||||
rv = elem->GetAttribute( name, value );
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
} else {
|
||||
DEBUG_PRINTF( PR_STDOUT, "%s %d: SetAttribute failed, rv=0x%X\n",
|
||||
__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
} else {
|
||||
DEBUG_PRINTF( PR_STDOUT, "%s %d: GetElementById failed, rv=0x%X\n",
|
||||
__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
} else {
|
||||
rv = NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsInstallProgressDialog::nsInstallProgressDialog()
|
||||
{
|
||||
NS_INIT_REFCNT();
|
||||
mWindow = nsnull;
|
||||
mDocument = nsnull;
|
||||
|
||||
}
|
||||
|
||||
nsInstallProgressDialog::~nsInstallProgressDialog()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_ADDREF( nsInstallProgressDialog );
|
||||
NS_IMPL_RELEASE( nsInstallProgressDialog );
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInstallProgressDialog::QueryInterface(REFNSIID aIID,void** aInstancePtr)
|
||||
{
|
||||
if (aInstancePtr == NULL) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
// Always NULL result, in case of failure
|
||||
*aInstancePtr = NULL;
|
||||
|
||||
if (aIID.Equals(nsIXPInstallProgress::GetIID())) {
|
||||
*aInstancePtr = (void*) ((nsInstallProgressDialog*)this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
if (aIID.Equals(nsIXULWindowCallbacks::GetIID())) {
|
||||
*aInstancePtr = (void*) ((nsIXULWindowCallbacks*)this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
if (aIID.Equals(kISupportsIID)) {
|
||||
*aInstancePtr = (void*) (nsISupports*)((nsIXPInstallProgress*)this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInstallProgressDialog::BeforeJavascriptEvaluation()
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Get app shell service.
|
||||
nsIAppShellService *appShell;
|
||||
rv = nsServiceManager::GetService( kAppShellServiceCID,
|
||||
nsIAppShellService::GetIID(),
|
||||
(nsISupports**)&appShell );
|
||||
|
||||
if ( NS_SUCCEEDED( rv ) )
|
||||
{
|
||||
// Open "progress" dialog.
|
||||
nsIURL *url;
|
||||
rv = NS_NewURL( &url, "resource:/res/xpinstall/progress.xul" );
|
||||
|
||||
if ( NS_SUCCEEDED(rv) )
|
||||
{
|
||||
|
||||
nsIWebShellWindow *newWindow;
|
||||
|
||||
rv = appShell->CreateTopLevelWindow( nsnull,
|
||||
url,
|
||||
PR_TRUE,
|
||||
newWindow,
|
||||
nsnull,
|
||||
this, // callbacks??
|
||||
0,
|
||||
0 );
|
||||
|
||||
if ( NS_SUCCEEDED( rv ) )
|
||||
{
|
||||
mWindow = newWindow;
|
||||
NS_RELEASE( newWindow );
|
||||
|
||||
if (mWindow != nsnull)
|
||||
mWindow->Show(PR_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTF( PR_STDOUT, "Error creating progress dialog, rv=0x%X\n", (int)rv );
|
||||
}
|
||||
NS_RELEASE( url );
|
||||
}
|
||||
|
||||
nsServiceManager::ReleaseService( kAppShellServiceCID, appShell );
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTF( PR_STDOUT, "Unable to get app shell service, rv=0x%X\n", (int)rv );
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInstallProgressDialog::AfterJavascriptEvaluation()
|
||||
{
|
||||
if (mWindow)
|
||||
{
|
||||
mWindow->Close();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInstallProgressDialog::InstallStarted(const char *UIPackageName)
|
||||
{
|
||||
setAttribute( mDocument, "dialog.uiPackageName", "value", nsString(UIPackageName) );
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInstallProgressDialog::ItemScheduled(const char *message)
|
||||
{
|
||||
PRInt32 maxChars = 40;
|
||||
|
||||
nsString theMessage(message);
|
||||
PRInt32 len = theMessage.Length();
|
||||
if (len > maxChars)
|
||||
{
|
||||
PRInt32 offset = (len/2) - ((len - maxChars)/2);
|
||||
PRInt32 count = (len - maxChars);
|
||||
theMessage.Cut(offset, count);
|
||||
theMessage.Insert(nsString("..."), offset);
|
||||
}
|
||||
setAttribute( mDocument, "dialog.currentAction", "value", theMessage );
|
||||
|
||||
nsString aValue;
|
||||
getAttribute( mDocument, "data.canceled", "value", aValue );
|
||||
|
||||
if (aValue.EqualsIgnoreCase("true"))
|
||||
return -1;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInstallProgressDialog::InstallFinalization(const char *message, PRInt32 itemNum, PRInt32 totNum)
|
||||
{
|
||||
|
||||
PRInt32 maxChars = 40;
|
||||
|
||||
nsString theMessage(message);
|
||||
PRInt32 len = theMessage.Length();
|
||||
if (len > maxChars)
|
||||
{
|
||||
PRInt32 offset = (len/2) - ((len - maxChars)/2);
|
||||
PRInt32 count = (len - maxChars);
|
||||
theMessage.Cut(offset, count);
|
||||
theMessage.Insert(nsString("..."), offset);
|
||||
}
|
||||
|
||||
setAttribute( mDocument, "dialog.currentAction", "value", theMessage );
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
char buf[16];
|
||||
|
||||
PR_snprintf( buf, sizeof buf, "%lu", totNum );
|
||||
setAttribute( mDocument, "dialog.progress", "max", buf );
|
||||
|
||||
if (totNum != 0)
|
||||
{
|
||||
PR_snprintf( buf, sizeof buf, "%lu", ((totNum-itemNum)/totNum) );
|
||||
}
|
||||
else
|
||||
{
|
||||
PR_snprintf( buf, sizeof buf, "%lu", 0 );
|
||||
}
|
||||
setAttribute( mDocument, "dialog.progress", "value", buf );
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInstallProgressDialog::InstallAborted()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Do startup stuff from C++ side.
|
||||
NS_IMETHODIMP
|
||||
nsInstallProgressDialog::ConstructBeforeJavaScript(nsIWebShell *aWebShell)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Get content viewer from the web shell.
|
||||
nsCOMPtr<nsIContentViewer> contentViewer;
|
||||
rv = aWebShell ? aWebShell->GetContentViewer(getter_AddRefs(contentViewer))
|
||||
: NS_ERROR_NULL_POINTER;
|
||||
|
||||
if ( contentViewer ) {
|
||||
// Up-cast to a document viewer.
|
||||
nsCOMPtr<nsIDocumentViewer> docViewer( do_QueryInterface( contentViewer, &rv ) );
|
||||
if ( docViewer ) {
|
||||
// Get the document from the doc viewer.
|
||||
nsCOMPtr<nsIDocument> document;
|
||||
rv = docViewer->GetDocument(*getter_AddRefs(document));
|
||||
if ( document ) {
|
||||
// Upcast to XUL document.
|
||||
mDocument = do_QueryInterface( document, &rv );
|
||||
if ( ! mDocument )
|
||||
{
|
||||
DEBUG_PRINTF( PR_STDOUT, "%s %d: Upcast to nsIDOMXULDocument failed, rv=0x%X\n",
|
||||
__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTF( PR_STDOUT, "%s %d: GetDocument failed, rv=0x%X\n",
|
||||
__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTF( PR_STDOUT, "%s %d: Upcast to nsIDocumentViewer failed, rv=0x%X\n",
|
||||
__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTF( PR_STDOUT, "%s %d: GetContentViewer failed, rv=0x%X\n",
|
||||
__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
67
mozilla/xpinstall/src/nsInstallProgressDialog.h
Normal file
67
mozilla/xpinstall/src/nsInstallProgressDialog.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code,
|
||||
* released March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributors:
|
||||
* Douglas Turner <dougt@netscape.com>
|
||||
*/
|
||||
#ifndef __nsInstallProgressDialog_h__
|
||||
#define __nsInstallProgressDialog_h__
|
||||
|
||||
#include "nsIXPInstallProgress.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsISupportsUtils.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include "nsIWebShell.h"
|
||||
#include "nsIWebShellWindow.h"
|
||||
#include "nsIXULWindowCallbacks.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMXULDocument.h"
|
||||
|
||||
|
||||
class nsInstallProgressDialog : public nsIXPInstallProgress, public nsIXULWindowCallbacks
|
||||
{
|
||||
public:
|
||||
|
||||
nsInstallProgressDialog();
|
||||
virtual ~nsInstallProgressDialog();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
|
||||
NS_IMETHOD BeforeJavascriptEvaluation();
|
||||
NS_IMETHOD AfterJavascriptEvaluation();
|
||||
NS_IMETHOD InstallStarted(const char *UIPackageName);
|
||||
NS_IMETHOD ItemScheduled(const char *message);
|
||||
NS_IMETHOD InstallFinalization(const char *message, PRInt32 itemNum, PRInt32 totNum);
|
||||
NS_IMETHOD InstallAborted();
|
||||
|
||||
// Declare implementations of nsIXULWindowCallbacks interface functions.
|
||||
NS_IMETHOD ConstructBeforeJavaScript(nsIWebShell *aWebShell);
|
||||
NS_IMETHOD ConstructAfterJavaScript(nsIWebShell *aWebShell) { return NS_OK; }
|
||||
|
||||
private:
|
||||
|
||||
nsCOMPtr<nsIDOMXULDocument> mDocument;
|
||||
nsCOMPtr<nsIWebShellWindow> mWindow;
|
||||
};
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user