Perl CGI image output using ffmpeg

I've been staring at this for hours now, and I need your help. It seems I've found a bug in Perl CGI, but it's most often my own fault...

I try to extract a movie frame from a specified file in the specified size at the specified offset (in time), and output the result as a jpeg image (using a regular <img src="..."> tag). Now, if I run my program without paramters (no ?stuff), it uses my preset defaults and everything is fine. If, however, I specify any one of the three paramters - for example: http://myhost/cgi-bin/get_frame.pl?offset=5 the image breaks. Probably no data at all comes through. I have checked the command strings, and they are identical! My only assumtion is that the CGI lib does something when it discovers the GET parameters, and then fails to pipe, output binary, or whatever.

Please help, I need to solve this.
#!/usr/bin/perl -Tw
 
use CGI qw(:standard);
use strict;
 
my $query = new CGI;
 
my $file = $query->param("filename");
my $size = $query->param("size");
my $offset = $query->param("offset");
my $pid;
my $buffer;
my $cmd;
 
local $ENV{"PATH"} = "/usr/bin";
 
if (!defined($file)) {
    $file = "/opt/www/html/test.wmv";
}
 
if (!defined($size)) {
    $size = "320x240";
}
 
if (!defined($offset)) {
    $offset = 4;
}
 
$cmd = "/usr/bin/ffmpeg -itsoffset -$offset -i $file " .
       "-vcodec mjpeg -vframes 1 -an -f rawvideo " .
       "-s $size -y /dev/stdout 2> /dev/null |";
 
if (1) {
    print $query->header("image/jpeg");
} else {
    print $query->header;
    print "file=$file<br>\n";
    print "size=$size<br>\n";
    print "offset=$offset<br>\n";
    print "$cmd\n";
    print "</body></html>\n";
    exit;
}
 
$pid = open(DATA, $cmd);
unless ($pid) {
#    print "Error: can't ffmpeg!\n</body></html>\n";
    exit;
}
while (read DATA, $buffer, 65536) {
    print $buffer;
}
close DATA;

Open in new window

LVL 2
obgAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Adam314Commented:
You don't print any message if there is an error with the open command, you just exit silently.  You could sent something to your error log, or you could display a default image.

You should also binmode STDOUT, but that should be the same with your default or not.

Try one of these:

##### Option 1: exit with error.  User will see broken image.  Message will go to error log.
...
binmode(STDOUT);
open(DATA, $cmd) or die "Can't start '$cmd': $!\n";
while (read DATA, $buffer, 65536) {
    print $buffer;
}
close DATA;
 
##### Option 2: display default image, log to error log.
binmode(STDOUT);
unless(open(DATA, $cmd)) {
    print STDERR "Could not start '$cmd': $!\n";
    open(DEF, "</path/to/default.jpg") or die "Could not display default image: $!\n";
    print while(<DEF>);
    close(DEF);
    exit(1);
}
while (read DATA, $buffer, 65536) {
    print $buffer;
}
close DATA;

Open in new window

0
obgAuthor Commented:
Ok, I've binmode'd STDOUT and now I've added an error picture, and there is no error picture and the message does not pop up in the error.log. However, another message pops up:

Insecure dependency in piped open while running with -T switch at...

And guess what! After removing the -T, it actually works! Why is this, and can I solve this keeping the "taint checking", or do I really need it in the first place? (I've always just used it, to be honest.)
0
Adam314Commented:
If you use "perl -t" (lowercase t) then the taint will generate warnings.  If you use "perl -T" (capital T), then taint will generate errors.

What taint does is alert you if you are using tainted data in a way that may be unsafe.  Data is considered tainted if it comes from outside your program, or is directly derived from tainted data.  You can get an untainted version of tainted data by running the tainted data through a RE, and capturing what you want to keep (which can be the entire string).

Whether you need it or not depends on what you do.  In your code above, you have:
...
my $offset = $query->param("offset");
...
$cmd = "/usr/bin/ffmpeg -itsoffset -$offset ..."
...
If some were were to pass in offset with a value of ";rm *" (notice the semicolon at the beginning), then you tried to open the $cmd, this would get processed as a command trying to delete your files.  
This is what the taint error is alerting you about:
Insecure dependency = using tainted data (it came from a user - which is outside your program)
in piped open = how you were using it - an open command with a pipe, which is a command piping data to us
If you wanted to use that data in a secure way, you could check filename, size, and offset against a RE, and capture the result.


my $file;
if($query->param("filename") =~ /^([a-zA-Z0-9_\-\.\/]+)$/) {
    $file = $1;
}
else {
    #display some error to the user about an invalid filename
    die "Invalid filename\n";
}
 
my $offset;
if($query->param("offset") =~ /^(\d+)$/) {
    $offset = $1;
}
else {
    #display some error to the user about an invalid offset
    die "Invalid offset\n";
}
 
#similar for size

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
obgAuthor Commented:
Thanks!

You didn't pinpoint my primary problem, but you certainly led me in the right direction. I'm thereby slightly hesitant to giving you an A. Your excellent explanation of tainted messages made up my mind though. This is only a private intranet site, but these advice are certainly valid anyway. Thanks again!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Scripting Languages

From novice to tech pro — start learning today.