#!/usr/bin/perl
# license: GPL V2
# (c)2014 rudi, forum.chdk-treff.de
use URI::Escape;
my $unzip = "/usr/bin/unzip";
my $srcfolder = "/tmp/";
my $mainhtml = "/chdk.html";
my $form;
my $qsChdkZip; #querystring filename
my $qsChdkZipSize; #querystring filesize
my $qsChdkFileCount = 0; #querystring file count
my $qsTime = 0; #time for set server time
my $chdkZip; #full path filename
my $chdkFileCount = 0; #checkZipContent file count
my $chdkUPsize = 0; #unpacked size of chdk files
my $fullChdk = 0; #zip type
my $updTime = 0; #time need update
my $langDE = ($ENV{'HTTP_ACCEPT_LANGUAGE'} =~ /^de/)?1:0; #user language german from HTTP_ACCEPT_LANGUAGE
my $htmlTitle = (!$langDE)?"install CHDK":"CHDK einrichten";
my $htmlCheckErr;
sub isSvrTime {
my (undef, $min, $hour, $day, $mon, $year) = localtime();
$year = $year+1900;
$mon = $mon+1;
$mon = ($mon > 9)?$mon:"0".$mon;
$day = ($day > 9)?$day:"0".$day;
$hour = ($hour > 9)?$hour:"0".$hour;
$min = ($min > 9)?$min:"0".$min;
my $svrTime = "$year$mon$day$hour$min";
$updTime = ($qsTime > $svrTime)?1:0;
}
sub setSvrTime {
if ($updTime) { `date -s $qsTime`; }
}
sub checkZipContent {
my $fn;
my $us = 0;
my @res = `unzip -l $chdkZip`;
my $res = 0;
my $fDiskboot = 0;
my $dModules = 0;
foreach $line (@res) {
if ($line =~ /(\d+) \d\d-\d\d-\d\d \d\d:\d\d ([\w+\/]*[\w\-]+\.\w\w\w)/) {
$fn = $2;
$us +=$1;
if ($fn =~ /^CHDK\//) {
#dir exist
if ($fn =~ /^CHDK\/MODULES\//) { $dModules += 1; } else { $fullChdk = 1; }
} else {
#not chdk dir => root dir
if ($fn =~ /\//) { $res = 0; last; } # '/' in line
if ($fn =~ /^DISKBOOT.BIN$/) { $fDiskboot += 1; }
}
$res += 1;
}
}
if ($res == 0 || $fDiskboot != 1 || $dModules == 0) { return 1; }
$chdkFileCount = $res;
$chdkUPsize = $us;
return 0;
}
sub deleteFile {
my $err = system("rm $chdkZip");
if ($err == 0) {
my @text = ("uploaded file deleted", "hochgeladene Datei gelöscht");
print "
$text[$langDE] \n";
} else {
my @text = ("Can't delete uploaded file!", "Hochgeledene Datei konnte nicht gelöscht werden!");
print "$text[$langDE] \n";
}
return ($err)?1:0;
}
sub refreshSD {
my $duration = shift @_;
my @text = (
["synchronize files, need approximate %d seconds time ...", "synchronisiere Dateien, dauert etwa %d Sekunden ..."],
["... completed", "... abgeschlossen"],
["synchronize failed!", "Synchronisation fehlgeschlagen!"]
);
if ($duration > 1) { printf "$text[0][$langDE] \n", $duration+1; } #add time for sync
my $err = system("sync") ||
system("sleep $duration") ||
system("sync") ||
system("mount -o remount /mnt/sd");
if ($duration > 1) { print "$text[1][$langDE] \n"; }
if ($err) { print "$text[2][$langDE] \n"; }
return ($err)?1:0;
}
sub checkChdkZip {
if ($abort != 0) {
$htmlCheckErr = (!$langDE)?"Installation canceled!":"Einrichtung abgebrochen!";
return 1;
}
if (length($chdkZip) == 0) {
$htmlCheckErr = (!$langDE)?"Missing file name!":"Dateiname fehlt!";
return 1;
}
if ($chdkZip !~ /.zip$/) {
$htmlCheckErr = (!$langDE)?"ZIP file required!":"ZIP-Datei erwartet!";
return 1;
}
if (checkZipContent != 0) {
$htmlCheckErr = (!$langDE)?"CHDK ZIP file required!":"CHDK-ZIP-Datei erwartet!";
return 1;
}
return 0;
}
sub processInstall {
my @text = (
["Extract and copy CHDK files, please wait ...", "CHDK-Dateien werden entpackt und kopiert, bitte warten ..."],
["... completed", "... abgeschlossen"],
["Error on copy files! Check filesystem with fsck/chkdsk and try installation again.", "Fehler beim Kopieren! Überprüfe das Dateisystem mit fsck/chkdsk und führe die Einrichtung erneut aus."],
["Copied %d from %d files", "%d von %d Dateien kopiert"],
["Press Button 'OK' and restart the camera!", "Drücke die 'OK'-Schaltfläche und starte die Kamera neu!"]
);
$| = 1; #flush for print
print "$text[0][$langDE] \n";
my $err = refreshSD(1);
if (!$err) {
my @res = `unzip -o $chdkZip -d /mnt/sd/`;
my $res = 0;
foreach $line (@res) {
if ($line =~ /inflating:/) { $res += 1; }
}
if ($qsChdkFileCount == $res) {
print "$text[1][$langDE] \n";
} else {
print "$text[2][$langDE] \n";
}
printf "$text[3][$langDE] \n", $res, $qsChdkFileCount;
#wait duration for sync
#highest value from data volume, ref=250'000 byte/sec
my $wd = $chdkUPsize/250000;
#or file count, ref=25 files/sec
if ($res/25 > $wd) { $wd = $res/25; }
$err = refreshSD(int($wd)+1); #round up
}
deleteFile;
if (!$err) { print "$text[4][$langDE] \n"; }
return ($err)?1:0;
}
sub qsSplit {
my @nameValuePairs = split (/&/, $queryString);
foreach $nameValue (@nameValuePairs) {
my ($name, $value) = split (/=/, $nameValue);
$value =~ tr/+/ /;
$value =~ s/%([\dA-Fa-f][\dA-Fa-f])/ pack ("C",hex ($1))/eg;
$form{$name} = $value;
}
}
sub writeChdkFileinfo {
my @text = (
["Update", "Aktualisierung"],
["Complete", "Komplett"],
["CHDK (absent value)", "CHDK (Angabe fehlt)"],
["Uploaded file", "hochgeladene Datei"],
["Name", "Name"],
["Size", "Größe"],
["File content", "Inhalt der Datei"],
["Package", "Paket"],
["Files", "Dateien"],
["Informations from filename", "Informationen aus dem Dateinamen"],
["Type", "Typ"],
["Camera", "Kamera"],
["Version", "Version"],
["Revision", "Revision"]
);
my $package = ($fullChdk == 0)?$text[0][$langDE]:$text[1][$langDE];
my $typ = (!$langDE)?"unknown":"unbekannt";
my $cam = $typ;
my $ver = $typ;
my $rev = $typ;
my $err = 1;
if ($qsChdkZip =~ /^(CHDK\-DE|CHDK_DE|CHDK)?[-_]?([a-z]+\d+[a-z]*_?\w*\-\d\d\d[a-z])\-(\d+\.\d+\.\d+)\-[-_A-Za-z]*(\d+).*\.zip/) {
if ($1) { $typ = $1 }
if ($2) { $cam = $2 }
if ($3) { $ver = $3 }
if ($4) { $rev = $4 }
if (!$1 && $2 && $3 && $4) { $typ = $text[2][$langDE] }
$err = 0;
}
print "\n";
if($qsChdkZipSize > 0) {
print "
\n";
print "$text[3][$langDE] \n";
print "$text[4][$langDE]: $qsChdkZip \n";
print "$text[5][$langDE]: $qsChdkZipSize Byte \n";
print "
\n";
}
print "
\n";
print "$text[6][$langDE] \n";
print "$text[7][$langDE]: $package \n";
print "$text[8][$langDE]: $chdkFileCount \n";
print "$text[5][$langDE]: $chdkUPsize Byte \n";
print "
\n";
print "
\n";
print "$text[9][$langDE] \n";
print "$text[10][$langDE]: $typ \n";
print "$text[11][$langDE]: $cam \n";
print "$text[12][$langDE]: $ver \n";
print "$text[13][$langDE]: $rev \n";
print "
\n";
print "
\n";
return $err;
}
#POST required
if($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN, $queryString, $ENV{'CONTENT_LENGTH'});
qsSplit;
}
if (length($form{fn}) > 0) {
$qsChdkZip = $form{fn};
$chdkZip = $srcfolder.$qsChdkZip;
}
if (length($form{fc}) > 0) { $qsChdkFileCount = $form{fc}; }
if (length($form{fs}) > 0) { $qsChdkZipSize = $form{fs}; }
if (length($form{time}) > 0) { $qsTime = $form{time}; }
if (length($form{utime}) > 0) { $updTime = $form{utime}; }
if (length($form{abort}) > 0) { $abort = $form{abort}; }
if (checkChdkZip == 0) {
$htmlTitle = ($qsChdkFileCount == $chdkFileCount)?"$htmlTitle : 2":"$htmlTitle : 1";
}
# HTML HEADER BEGIN
print "Content-type: text/html\n\n";
print "\n";
print "\n";
print "\n";
print "$htmlTitle \n";
print "\n";
print "\n";
print "\n";
print " $htmlTitle \n";
print " \n";
print "
\n";
print " \n";
#HTML HEADER END
if (checkChdkZip == 0) {
if ($qsChdkFileCount == $chdkFileCount) {
#step 2: install
my @text = (
["Step 2 - Copy files", "Schritt 2 - CHDK-Dateien kopieren"],
["Current server time", "Aktuelle Serverzeit"],
["Unsafe filename!", "Unsicherer Dateiname!"],
["Step 2 completed", "Schritt 2 abgeschlossen"],
["Error on step 2", "Fehler im Schritt 2"]
);
print "$text[0][$langDE] \n";
my $pi = writeChdkFileinfo;
print " \n";
print "\n";
if ($updTime == 1) {
my $lt = localtime;
print "$text[1][$langDE]: $lt \n";
}
if ($pi) { print "$text[2][$langDE] \n"; }
$pi = processInstall;
print " \n";
print " \n";
print "\n";
} else {
#step 1: check
my @text = (
["Step 1 completed", "Schritt 1 abgeschlossen"],
["Unsafe filename!", "Unsicherer Dateiname!"],
["Step 2 - Copy files", "Schritt 2 - CHDK-Dateien kopieren"],
["Cancel", "Abbrechen"],
["Next", "Weiter"]
);
$| = 1; #flush for print
isSvrTime;
print "$text[0][$langDE] ";
my $fi = writeChdkFileinfo;
print " \n";
print "\n";
}
} else {
my @text = (
["Error", "Fehler"],
["", ""]
);
#check error
print "$text[0][$langDE] \n";
print "$htmlCheckErr
\n";
print "\n";
print " \n";
print "\n";
}
# HTML FOOTER BEGIN
print " \n";
print " \n";
print "
\n";
print " \n";
print " chdk_install.cgi: v1.6
\n";
print "\n";
print "\n";
# HTML FOOTER END
#! push to end of script
# webserver output fails with set date on execute cgi
# at first time after boot
setSvrTime;