Perl to exec a program with arguments containing "#" - perl

Am a newbie in Perl and need help with a small problem
Situation:
I have to execute a command line program through perl.
The arguments to this command line are email addresses
These email addresses are passed to me through another module.
Problem:
I have written the code to create the argument list from these email addresses but am having problem in running exec().
NOTE: If I pass hardcoded strings with escaped "#" character to the exec() as command args,it works perfectly.
Sub creating cmd args map
sub create_cmd_args {
my($self, $msginfo) = #_;
my #gd_args_msg = ('--op1');
my $mf = $msginfo->sender_smtp;
$mf =~ s/#/\\#/ig; ## Tried escaping #, incorrect results.
push #gd_args_msg, '-f="'.$mf.'"';
for my $r (#{$msginfo->per_recip_data}) {
my $recip = $r->recip_addr_smtp;
$recip =~ s/#/\\#/ig; ## Tried escaping #, incorrect results.
push #gd_args_msg, '-r="'.($recip).'"';
}
return #gd_args_msg;
}
Sub that uses this args map to exec the program
sub check {
my($self, $msginfo) = #_;
my $cmd = $g_command;
my #cmd_args = create_cmd_args($self, $msginfo);
exec($cmd, #cmd_args); ### ******* fails here
}
Sample run:
INPUT:
sender_smtp: <ashish#isthisreal.com>
receiver_smtp: <areyouarealperson#somedomain.com>
Could someone please guide me what is wrong here?

As an argument to a command in the shell,
-f="<ashish#isthisreal.com>"
causes the the string
-f=<ashish#isthisreal.com>
to be passed to the program. Your program passes
-f="<ashish\#isthisreal.com>"
to the program. The problem isn't the #; the problem is the " and \ you are adding.
my $mf = $msginfo->sender_smtp;
push #gd_args_msg, "-f=$mf"; # Assuming $mf is <ashish#isthisreal.com>

If you look at the post at Trying to convert Perl to PHP and the code within the md5sum implementation that calls the command line you will see an approach that will save you from needing to worry about escaping characters.

Related

"sh: 1: file: not found" thrown in Perl

So this is an issue I see thrown around on several coding help-sites that always have a slight variation. I'm not entirely familiar with what it means, and what's even more curious is that this error is thrown midway through a larger Upload.pm script, and does not cause any sort of fatal error. It gets tossed into my error log somewhere during this unless conditional snippet
# If this is the first slice, validate the file extension and mime-type. Mime-type of following slices should be "application/octet-stream".
unless ( defined $response{'error'} ) {
if ( $slice->{'index'} == 1 ) {
my ($filename, $directory, $extension) = fileparse($path.$parent_file, qr/\.[^.]*/);
unless ( is_valid_filetype($slice->{'tmp_file'}, $extension) ) {
$response{'error'} = "Invalid file type.";
$response{'retry'} = 0;
}
}
}
Now, let me be perfectly honest. I don't really understand the error message, and I could really use some help understanding it, as well as solving it.
Our Perl based web app has refused to let us upload files correctly since upgrading to Debian Bullseye, and I've been stuck debugging this code I didn't write for a few days now. I'm wondering if the upgrade depreciated some Perl modules, or if the directories to said modules are no longer working?
I'm testing this in a Ubuntu based Docker environment running Debian Bullseye on an Apache 2 server.
If you need any more context, clarification, etc, please let me know.
is_valid_filetype() looks like this:
sub is_valid_filetype
{
my ($tmp_file, $extension) = #_;
if ( $tmp_file && $extension ) {
# Get temp file's actual mime-type.
my $mime = qx/file --mime-type -b '${tmp_file}'/;
$mime =~ s/^\s+|\s+$//g;
# Get valid mime-types matching this extension.
my $dbh = JobTracker::Common::dbh or die("DBH not available.");
my $mime_types = $dbh->selectrow_array('SELECT `mime_types` FROM `valid_files` WHERE `extension` = ?', undef, substr($extension, 1));
if ( $mime && $mime_types ) {
if ( $mime_types !~ /,/ ) {
# Single valid mime-type for this extension.
if ( $mime eq $mime_types ) {
return 1;
}
} else {
# Multiple valid mime-types for this extension.
my %valid_mimes = map { $_ => 1 } split(/,/, $mime_types);
if ( defined $valid_mimes{$mime} ) {
return 1;
}
}
}
}
return 0;
}
It's a message from sh (not Perl). It concerns an error on line 1 of the script, which was apparently an attempt to run the file utility. But sh couldn't find it.
The code in question executes this command using
qx/file --mime-type -b '${tmp_file}'/
Install file or adjust the PATH so it can be found.
Note that this code suffers from a code injection bug. It will fail if the string in $tmp_path contains a single quote ('), possibly resulting in the unintentional execution of code.
Fixed:
use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote( "file", "--mime-type", "-b", $tmp_file" );
qx/$cmd/
Debian Bullseye was reading our CSV files as the wrong mime-type. It was interpreting the file command as application/csv, despite obviously not being an application.
This may be an actual bug in Bullseye, because both my boss and I have scoured the internet with no lucky finding anyone else with this issue. I may even report to Bullseye's devs for further awareness.
The fix was manually adding in our own mime-types that interpreted this file correctly.
It took us dumping the tmp directory to confirm the files existed, and triple checking I had my modules installed.
This was such a weird and crazy upstream issue that either of us could not have imaged it would be the file type interpretation at an OS level in Bullseye.
I really hope this helps someone, saves them the time it took us to find this.

How do I input paramters into a Jenkins perl script from command line?

Background
I am running a Jenkns Job, called Job A, that feeds its build parameters into a perl script, called ScriptA.pl, in the following format below:
#Check the input params
my $PARAM1 = $ENV{ "PARAM1" };
my $PARAM2 = $ENV{ "PARAM2" };
.....more params fed in the same way
if ( $PARAM1 eq "" ) {
print "PARAM1 is a required parameter.\n";
exit 1;
}
if ( $PARAM2 eq "" ) {
print "PARAM2 is a required parameter.\n";
exit 1;
}
.....more param checks done in the same way
###Script then runs a bunch of execution statements####
Problem
I am trying to run this script from Linux command line in cshell in the following way to test that the execution component works:
/% ScriptA.pl jetFuel steelBeams
And it thinks that no parameters have been entered, since this error is returned
PARAM1 is a required parameter.
Question
How do I properly input Jenkins parameters into a Jenkins script from command line?
If you can't modify the Perl script so it reads command-line parameters (which would improve its general usability to boot), maybe create a simple wrapper script:
#/bin/sh
env PARAM1="$1" PARAM2="$2" perl ScriptA.pl
(Yes, sh. Nobody should use csh for anything in 2019.)

if condition using telnet in perl not working

I'm trying to use if condition to check if command has passed, but its not working. Even though the mount has been successful it goes to failed message. When i enter this command, it retruns to the prompt without any message, hence i'm comparing with a "". And when i do a "ls" of destination folder, it shows all contents of source folder. Any help? Is my if condition correct?
my $port = new Net::Telnet->new(Host=>$ip,Port=>$ip_port,Timeout => "$timeout", Dump_Log => "dumplog.log", Errmode=> "return" );
if($port->cmd("mount -t nfs -o nolock <path-of-source-folder> <destination-folder>") eq "")
{
print "Successful\n";
}
else{
print "Failed.\n ";
}
In scalar context, the Net::Telnet cmd method returns 1 on success (not a string). Your check should be something like:
if ($port->cmd("mount -t nfs -o nolock <path-of-source-folder> <destination-folder>") == 1)
{
print "Successful\n";
} else {
print "Failed.\n";
}
If you actually want to collect the output from the mount command and inspect it, you will have to either call it in list context or pass a stringref argument, like so:
my #outlines = $port->cmd("mount ...");
Or:
my $out;
my $ret = $port->cmd("mount ...", [Output => \$out]);
if ($ret == 1)
{
# inspect $out
}
See the Net::Telnet documentation for more.
Your check for the result seems to be wrong. The documenation of Net::Telnet says that
This method sends the command $string, and reads the characters sent back by the command up until and including the matching prompt. It's assumed that the program to which you're sending is some kind of command prompting interpreter such as a shell.
The command $string is automatically appended with the output_record_separator, by default it is "\n". This is similar to someone typing a command and hitting the return key. Set the output_record_separator to change this behavior.
In a scalar context, the characters read from the remote side are discarded and 1 is returned on success.
So you need to check in a scalar context
if ($port->cmd("..") ) {
...
}

Perl Irssi scripting: rename invalid DCC file

I'm on Windows, using Irssi client irssi-win32-0.8.12.exe.
I'm having problems receiving a file with invalid name:
..nameo_\u2605_name.. (err: DCC can't create file)
How can I strip this invalid part "\u2605" from filename, using script?
This page doesn't help
I think this part of the Irssi source has something to do with it. Starting at line 195
/* if some plugin wants to change the file name/path here.. */
signal_emit("dcc get receive", 1, dcc);
I sure hope Irssi on Windows accepts scripts written in Perl. If this is the case, here's the solution:
use strict;
use warnings;
our $VERSION = "1.0";
our %IRSSI = ();
# interception made by registering signal as first + Irssi::signal_continue()
sub event_ctcp_dccsend {
my ($server, $args, $nick, $addr, $target) = #_;
# split incomming send request args into filename (either before first space or
# quoted), and the rest (IP, port, +optionally filesize)
my ($filename, $rest) = $args =~ /((?:".*")|\S*)\s+(.*)/;
# remember file name for informing sake
my $oldname = $filename;
# replace backslashes with "BSL" (change to anything you wish)
if ($filename =~ s/\\/BSL/g) {
# some info for user
Irssi::print('DCC SEND request from '.$nick.': renamed bad filename '.$oldname.' to '.$filename);
$args = $filename." ".$rest;
# propagate signal; Irssi will proceed the request with altered arguments ($args)
Irssi::signal_continue($server, $args, $nick, $addr, $target);
}
}
# register signal of incoming ctcp 'DCC SEND', before anything else
Irssi::signal_add_first('ctcp msg dcc send', 'event_ctcp_dccsend');
The script intercepts "DCC SEND" ctcp messages and replaces all backslashes in the filename into "BSL" string, then forwards altered arguments of message to any other scripts and Irssi.
If you want to remove all "\uXXXX" instead, use s/\\u\w{4}//g in place of s/\\/BSL/g
I hope it helps!

Send request parameters when calling a PHP script via command line

When you run a PHP script through a browser it looks something like
http://somewebsite.com/yourscript?param1=val1&param2=val2.
I am trying to achieve the same thing via command line without having to rewrite the script to accept argv instead of $_REQUEST. Is there a way to do something like this:
php yourscript.php?param1=val1&param2=val2
such that the parameters you send show up in the $_REQUEST variable?
In case you don't want to modify running script, you can specify parameters using in -B parameter to specify code to run before the input file. But in this case you must also add -F tag to specify your input file:
php -B "\$_REQUEST = array('param1' => 'val1', 'param2' => 'val2');" -F yourscript.php
I can't take credit for this but I adopted this in my bootstrap file:
// Concatenate and parse string into $_REQUEST
if (php_sapi_name() === 'cli') {
parse_str(implode('&', array_slice($argv, 1)), $_REQUEST);
}
Upon executing a PHP file from the command line:
php yourscript.php param1=val1 param2=val2
The above will insert the keys and values into $_REQUEST for later retrieval.
No, there is no easy way to achieve that. The web server will split up the request string and pass it into the PHP interpreter, who will then store it in the $_REQUEST array.
If you run from the command line and you want to accept similar parameters, you'll have to parse them yourself. The command line has completely different syntax for passing parameters than HTTP has. You might want to look into getopt.
For a brute force approach that doesn't take user error into account, you can try this snippet:
<?php
foreach( $argv as $argument ) {
if( $argument == $argv[ 0 ] ) continue;
$pair = explode( "=", $argument );
$variableName = substr( $pair[ 0 ], 2 );
$variableValue = $pair[ 1 ];
echo $variableName . " = " . $variableValue . "\n";
// Optionally store the variable in $_REQUEST
$_REQUEST[ $variableName ] = $variableValue;
}
Use it like this:
$ php test.php --param1=val1 --param2=val2
param1 = val1
param2 = val2
I wrote a short function to handle this situation -- if command line arguments are present and the $_REQUEST array is empty (ie, when you're running a script from the command line instead of though a web interface), it looks for command line arguments in key=value pairs,
Argv2Request($argv);
print_r($_REQUEST);
function Argv2Request($argv) {
/*
When $_REQUEST is empty and $argv is defined,
interpret $argv[1]...$argv[n] as key => value pairs
and load them into the $_REQUEST array
This allows the php command line to subsitute for GET/POST values, e.g.
php script.php animal=fish color=red number=1 has_car=true has_star=false
*/
if ($argv !== NULL && sizeof($_REQUEST) == 0) {
$argv0 = array_shift($argv); // first arg is different and is not needed
foreach ($argv as $pair) {
list ($k, $v) = split("=", $pair);
$_REQUEST[$k] = $v;
}
}
}
The sample input suggested in the function's comment is:
php script.php animal=fish color=red number=1 has_car=true has_star=false
which yields the output:
Array
(
[animal] => fish
[color] => red
[number] => 1
[has_car] => true
[has_star] => false
)