#!/usr/bin/perl
##########################################################################
$prognfo = <<EOM;
sendSMS.pl v0.2.4 03/08/2006
copyright (c) 2001-2006 by Paul Kreiner
EOM
#
# Written by: Paul Kreiner <deacon@thedeacon.org>
# with portions by: Brandon Zehm <caspian@dotconf.net>
#
# History:
# The original sendSMS.pl was an personal-use hack written
# by Brandon Zehm. It has been completely rewritten to
# be modular, robust, and extendable, and to easily allow the
# addition of other service providers.
#
# Changelog:
# v0.2.4 03/08/2006
# Incorporated patch to add Alltell Wireless support (provided by Anthony
# Valley). Marked Cricket support as deprecated, since Cricket is
# specifically blocking scripts such as this one through technical measures.
# CRICKET support will be removed in a future release.
# Paul Kreiner
#
# v0.2.3 12/15/2005
# Incorporated patch to add Cingular support (provided by Barret Kendrick).
# Paul Kreiner
#
# v0.2.2 10/04/2004
# Incorporated change to ATTWS profile to reflect changes on their website.
# (reported and patch provided by Joel Chen)
# Paul Kreiner
#
# v0.2.1 09/25/2004
# Added support for HTTPCookie parameter for each provider. Currently, only
# SPRINT requires a cookie to be set, but others may need it later.
# Incorporated changes and features to support Sprint PCS now that they've
# changed their website again (thanks to Joel Chen for his help).
# Paul Kreiner
#
# v0.2.0 05/13/2004
# Incorporated patch to add Bell Canada / Bell Mobility support (provided
# by David Caplan).
# Removed support for deprecated 'VSTREAM' service provider. NOTE: If you
# are still using 'VSTREAM', you must switch to 'TMOBILE'.
# Paul Kreiner
#
# v0.1.9 12/12/2003
# Incorporated change to Sprint PCS profile to reflect changes on their
# website - they only send 'Message sent' now (reported by Ken Cochran).
# Added global version/date/copyright variables to code so help screen
# gives program info without having to modify it in multiple places.
# Simplified the help screen by removing "written by:" and extra CR's.
# Paul Kreiner
#
# v0.1.8 09/15/2003
# Incorporated patch to add Sprint PCS support (provided by John Carbone).
# Paul Kreiner
#
# v0.1.7 09/04/2003
# Incorporated patch to add T-Mobile support (provided by Manuel Martin).
# Incorporated patch to add Cricket support (provided by Tod Morrison).
# Marked VoiceStream support as deprecated in help and code comments.
# Small fixes for typos and minor cosmetic code cleanups.
# Paul Kreiner
#
# v0.1.6 08/12/2002
# Updated ATTWS profile to reflect changes on their website.
# Jared Cheney
#
# v0.1.5 05/29/2002
# Updated to support web proxies.
# Brandon Zehm
#
# v0.1.4 04/04/2002
# Updated VoiceStream Support and added support for SkyTel pagers.
# Brandon Zehm
#
# v0.1.3 01/22/2001
# Some changes to get this running on Win32 (ActiveState 5.6.0/build 623).
# Couldn't get POSIX::termios or IO::Select to correctly make STDIN non-
# blocking, so I gave up after ~4 hrs of trying, and simply removed the
# STDIN message-sending functionality for Win32. If someone out there
# can send me a patch (and NOT simply include an external module, such as
# Term::ReadKey or something) then maybe it'll be supported.
# Paul Kreiner
#
# v0.1.2 01/20/2001
# Added support for passing a message via STDIN. Got the list of providers
# in the help sub to be dynamically generated, making it that much easier to
# add service providers (only two places to edit, now.) Implemented signal
# trapping for cleaner exits. Added support for Cellular One (unverified).
# Minor code cleanups.
# Paul Kreiner
#
# v0.1.1 01/17/2001
# Update to improve modularity and make adding new providers much easier
# than before. Finished removing any global vars, just because I can.
# Replaced original encodeAsURL code with much more efficient algorithm. I
# think that was the last of Brandon's original code.
# Paul Kreiner
#
# v0.1.0 01/15/2001
# Initial 'beta' release. Basic functionality in place, although modularity
# needs to be improved. This is a full rewrite of the original sendSMS.pl
# script, with commentary, support for multiple providers, and a more robust
# user interface. Includes support for AT&T WS and VoiceStream (verified).
# Paul Kreiner
#
# Notes:
# It's a big script, for what it does. Note that one of the primary
# design goals was to have a single self-contained script that
# would do everything -- no CPAN modules, .conf files, etc.
# The layout of the script is set up so the templates for various
# service providers appear right near the top, so if someone
# (yeah, this means YOU) wants to add/modify support for
# a service provider, it should be really easy to see all the fields
# to modify.
#
# License:
# sendSMS.pl (hereafter referred to as "program") is free software;
# you can redistribute it and/or modify it under the terms of the GNU General
# Public License as published by the Free Software Foundation; either version
# 2 of the License, or (at your option) any later version.
# Note that when redistributing modified versions of this source code, you
# must ensure that this disclaimer and the above coder's names are included
# VERBATIM in the modified code.
#
# Disclaimer:
# This program is provided with no warranty of any kind, either expressed or
# implied. It is the responsibility of the user (you) to fully research and
# comprehend the usage of this program. As with any tool, it can be misused,
# either intentionally (you're a vandal) or unintentionally (you're a moron).
# THE AUTHOR(S) IS(ARE) NOT RESPONSIBLE FOR ANYTHING YOU DO WITH THIS PROGRAM
# or anything that happens because of your use (or misuse) of this program,
# including but not limited to anything you, your lawyers, or anyone else
# can dream up. And now, a relevant quote directly from the GPL:
#
# NO WARRANTY
#
# 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
# FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
# OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
# PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
# OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
# TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
# PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
# REPAIR OR CORRECTION.
#
# ---
#
# Whee, that was fun, wasn't it? Now let's all get together and think happy
# thoughts. And remember, the same class of people who made all the above
# legal spaghetti necessary are the same ones who are stripping your rights
# away with DVD CCA, DMCA, stupid software patents, and retarded legal
# challenges to everything we've come to hold dear about our internet.
# So enjoy your dwindling "freedom" while you can, because 1984 is coming
# sooner than you think. :[
#######################################
# Some quick function definitions (in order of appearance):
#
# sub getProvider: names & parameters of service providers. USER MODIFYABLE.
# sub initialize: sets up initial SIG handlers & alarms
# sub run: the main loop of the program
# sub processCmdLine: parse STDIN and command line for user input
# sub checkInput: sanity check of user-input values
# sub encodeAsURL: encodes any string(s) as legit URL(s)
# sub openSocket: opens a socket to remote webserver
# sub sendData: build & send HTTP request to remote server
# sub waitforReply: waits for success/fail response from remote server
# sub printHelp: print user help message & list of providers
# sub exit: exit, print optional error message and close filehandles
######################################
# Here we go...
use vars qw($prognfo);
use strict;
initialize(); ### set up some initial SIG handlers, etc.
run(); ### executes the main body of the program (done for readability)
exit(1); ### execution should never get here, but just in case...
############
# Function: getProvider( \$HTTPRequestMethod,
# \$HTTPRequestString, \$HTTPReferer, \$HTTPCookie, \$remoteHostName,
# \$formDataString, \$successString, \$provider, \$receiver,
# \$sender, \$message )
# Pass this function several references to scalars, as follows:
# - $HTTPRequestMethod is either "GET" or "POST", depending
# upon what is specified in the selected provider's template.
# - $HTTPRequestString is the rest of the GET or POST line that
# is to be sent to the remote server. Again, part of the template.
# - $HTTPReferer is the referer string defined in the provider's template.
# - $HTTPCookie is the cookie string defined in the provider's template.
# Note that the cookie string can (and should) be left empty, unless you
# *know* that your provider requires a cookie to work.
# - $remoteHostName is the name of the remote webserver. It can also have
# a "magic" value when the function is called, which will alter the behavior
# of this function. See below for more info.
# - $formDataString is the actual data, formatted differently per provider,
# that will be submitted to the provider's website. This string is defined
# as part of each provider's template.
# - $successString is the string which we will wait to receive back
# from the remote server, to signal that we were successful. This is
# defined differently for each provider, as part of their template.
# - $provider is the name of our selected service provider.
# - $receiver is the cellular number (10-digit) of our recipient.
# - $sender is the sender's name / number.
# - $messageBody is the text of the message body.
#
# This function will find the user-selected service provider's template
# and fill in all other referenced variables with the correct info for that
# provider. This includes remote servername, HTTP request method,
# referring webpage, success string, and a properly-formatted form
# submission data string.
# There is a second use to this function -- if it is called with a magic value
# of 'get providerNames' in $remoteHostName, it will return an array with
# the names/descriptions of all available service providers.
###
sub getProvider
{
(
my $HTTPRequestMethod,
my $HTTPRequestString,
my $HTTPReferer,
my $HTTPCookie,
my $remoteHostName,
my $formDataString,
my $successString,
my $provider,
my $receiver,
my $sender,
my $messageBody,
) = @_;
##############################################################################
#
# BEGIN USER-MODIFYABLE SECTION:
#
##############################################################################
# List of providers by name. First entry is the name of the service parameters
# array. Second entry is the "common" name of the service provider. This is
# the name that the provider will be referenced as on the command line.
# Finally, the third entry is the full name of the service provider. This is
# used when the list of providers is printed (usually by the help function).
# NOTE: you will need to add your provider's info here, and below (in the
# "parameters" section) to add support for them to sendSMS. A sample entry
# has been created for you.
my @providerNames = (
# \my %TEMPLATE, 'TEMPLATE', 'An example service provider',
\my %ALLTEL, 'ALLTEL', 'Alltel Wireless',
\my %ATTWS, 'ATTWS', 'AT&T Wireless',
\my %BELLCA, 'BELLCA', 'Bell Canada',
\my %CINGULAR, 'CINGULAR', 'Cingular',
\my %CELLONE, 'CELLONE', 'Cellular One',
\my %CRICKET, 'CRICKET', 'Cricket [deprecated]',
\my %SKYTEL, 'SKYTEL', 'SkyTel',
\my %SPRINT, 'SPRINT', 'Sprint PCS',
\my %TMOBILE, 'TMOBILE', 'T-Mobile',
);
if ($remoteHostName == 'get providerNames')
{ #return a list of providers immediately if being called with a magic
# value for remoteHostName
return(@providerNames);
}
# Service provider's PARAMETERS section. Note that an example TEMPLATE
# provider entry is already here. I suggest using a sniffer tool such as
# ngrep or tcpdump to sniff the conversation between your browser and the
# target webpage, then examining it to extract the relevant information for
# use in this section. For example, if a session to www.example.com runs as
# follows:
# POST /cgi-bin/sms.cgi HTTP/1.1
# Referer: http://www.example.com/index.html
# Host: www.example.com
# Cookie: JSESSIONID=boguscookie
# Content-type: application/x-www-form-urlencoded
# Content-length: 53
# sender=123&receiver=456&text=This is a test message
#
# And a successful message returns a page with the text "Mesage submitted
# successfully", then the data you'd insert below would be:
### An example service provider's parameters:
#%TEMPLATE = (
# 'remoteHostName' => 'www.example.com',
# 'HTTPRequestMethod' => 'POST',
# 'HTTPRequestString' => '/cgi-bin/sms.cgi',
# 'HTTPReferer' => 'http://www.example.com/index.html',
# 'HTTPCookie' => 'JSESSIONID=boguscookie',
# 'successString' => 'successfully',
# 'formDataString' => 'sender=$$sender&receiver=$$receiver&text=$$messageBody',
#);
### Alltel Wireless service parameters
%ALLTEL = (
'remoteHostName' => 'message.alltel.com',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/customer_site/jsp/messaging.jsp',
'HTTPReferer' => 'http://message.alltel.com/customer_site/jsp/messaging.jsp',
'HTTPCookie' => 'JSESSIONID=boguscookie',
'successString' => 'Message Status',
'formDataString' => 'devicetype=1&min=$$receiver&trackResponses=No&callback=$$sender&type=1&text=$$messageBody&count=0',
);
### AT&T Wireless service parameters:
%ATTWS = (
'remoteHostName' => 'www.mymmode.com',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/messagecenter/sendMessage.do',
'HTTPReferer' => 'http://www.mymmode.com/messagecenter/init',
'HTTPCookie' => '',
'successString' => 'submitted',
'formDataString' => 'recipient=$$receiver&sender=$$sender&subject=&message=$$messageBody',
);
### Bell Canada (Bell Mobility) service parameters:
%BELLCA = (
'remoteHostName' => 'www.txt.bellmobility.ca',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/sms/sendMessage',
'HTTPReferer' => 'http://www.txt.bellmobility.ca/sms/sendMessage',
'HTTPCookie' => '',
'successString' => 'essage was sent to',
'formDataString' => 'TEMPLATE=/img/sms/general/outbox/messageConfirmation.html&EXCEPTION_TEMPLATE=/img/sms/general/imgHtmlException.html&TRANSPORT_TYPE=sms&MIN_NUMBER=$$receiver&SMTP_RETURNPATH=&MIN_RETURNPATH=&FROM=&CALLBACK_NUMBER=&STATUS_REQUEST=&MESSAGE_SUBJECT=$$sender&MESSAGE_BODY=$$messageBody&CHARCOUNT=150&x=100&y=15'
);
### Cellular One service parameters:
%CELLONE = (
'remoteHostName' => ' www.celloneusa.com',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/cgi-bin/sendMsg.pl',
'HTTPReferer' => 'http://www.celloneusa.com/message.cfm',
'HTTPCookie' => '',
'successString' => 'will be sent',
'formDataString' => 'toPhone=$$receiver&fromPhone=$$sender&SendTime=0&FormName2=Short\%20Messaging\%20System&FileName2=Message.pl&Required2=toPhone\%2BfromPhone\%2BtxtMessage&txtMessage=$$messageBody'
);
### Cingular Wireless service parameters:
%CINGULAR = (
'remoteHostName' => 'www.cingularme.com',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/do/public/send',
'HTTPReferer' => 'http://www.cingularme.com/do/public/send',
'HTTPCookie' => 'JSESSIONID=aB5fMc38zGGc',
'successString' => 'clear the fields below',
'formDataString' => 'min=$$receiver&from=$$sender&subject=&msg=$$messageBody'
);
### Cricket service parameters:
### As of 3/8/06, CRICKET support is DEPRECATED and will be removed in the
### future. Cricket has specifically been blocking scripts such as this from
### accessing their site.
%CRICKET = (
'remoteHostName' => 'www.mycricket.com',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/text/sendto_sms_system.asp?Step=2',
'HTTPReferer' => 'http://www.mycricket.com/text/sendto_sms_system.asp',
'HTTPCookie' => '',
'successString' => 'Message Sent',
'formDataString' => 'cricket_phone_number=$$receiver&msg=$$messageBody&from=$$sender'
);
### SkyTel service parameters:
%SKYTEL = (
'remoteHostName' => 'www.skytel.com',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/servlet/SendMessage',
'HTTPReferer' => 'http://www.skytel.com:80/servlet/SendMessage',
'HTTPCookie' => '',
'successString' => 'Received Your Message',
'formDataString' => 'cmd=post&recipients=$$receiver&message=$$messageBody'
);
### Sprint PCS service parameters:
%SPRINT = (
'remoteHostName' => 'messaging.sprintpcs.com',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/textmessaging/composeconfirm',
'HTTPReferer' => 'http://messaging.sprintpcs.com/textmessaging/compose',
'HTTPCookie' => 'JSESSIONID=boguscookie',
'successString' => 'Message Sent',
'formDataString' => 'phoneNumber=$$receiver&message=$$messageBody&characters=160&callBackNumber=$$sender'
);
### T-Mobile (was: VoiceStream) service parameters:
%TMOBILE = (
'remoteHostName' => 'www.t-mobile.com',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/messaging/default.asp',
'HTTPReferer' => 'http://www.t-mobile.com/messageing/default.asp',
'HTTPCookie' => '',
'successString' => 'message was sent to',
'formDataString' => 'txtNum=$$receiver&txtFrom=$$sender&txtMessage=$$messageBody'
);
### Verizon Dominicana service parameters:
%VERIZONDOMINICANA = (
'remoteHostName' => '64.32.115.70',
'HTTPRequestMethod' => 'POST',
'HTTPRequestString' => '/Portal/Gen3Sync/WISSMS.asp',
'HTTPReferer' => 'http://www1.datamovil.net.do/',
'HTTPCookie' => '',
'successString' => 'Su mensaje fue enviado',
'formDataString' => 'min=$$receiver&msg=$$messageBody&totalCounter=160',
);
# Check to be sure we know how to send to the selected service provider...
# We run through all the service providers, looking for a match by name:
for (my $count=0; $count < @providerNames; $count+=3)
{
if ($$provider =~ /^$providerNames[$count + 1]$/i)
{ #We found a match - let's copy the data out of the template.
$$remoteHostName= % { $providerNames[$count] } -> { 'remoteHostName' };
$$HTTPRequestMethod= % { $providerNames[$count] } -> { 'HTTPRequestMethod' };
$$HTTPRequestString= % { $providerNames[$count] } -> { 'HTTPRequestString' };
$$HTTPReferer= % { $providerNames[$count] } -> { 'HTTPReferer' };
$$HTTPCookie= % { $providerNames[$count] } -> { 'HTTPCookie' };
$$successString= % { $providerNames[$count] } -> { 'successString' };
# A little tricky here. Because the contents of formDataString include
# dynamic content (sender, recipient, message), we need to substitute the
# real data in place of the variable names, before assigning the entire
# string to $$formDataString to be used elsewhere. Eval() allows this.
eval "\$\$formDataString=\"" . %{$providerNames[$count]} -> { 'formDataString' } . "\"";
}
}
# Unknown service provider. Exit with an error description.
if (! $$formDataString ) # We couldn't find a service provider match!
{ &printHelp;
&exit("ERROR: the service provider ($$provider) you specified is not recognized.");
}
}
##############################################################################
# END USER-MODIFYABLE SECTION.
# You shouldn't need to touch anything past this point, if you simply wish
# to add support for another service provider.
##############################################################################
# This space intentionally left blank.
############
# Function: initialize()
# Sets up anything/everyting before the program's execution really starts.
# This is basically to keep things tidy up at the top of the source.
# Everything that would normally be up there is included here, instead.
###
sub initialize
{
### Intercept signals and exit cleanly ###
$SIG{'QUIT'} = sub { &exit("ERROR: Exiting prematurely...received SIG$_[0]!"); };
$SIG{'INT'} = sub { &exit("ERROR: Exiting prematurely...received SIG$_[0]!"); };
$SIG{'KILL'} = sub { &exit("ERROR: Exiting prematurely...received SIG$_[0]!"); };
$SIG{'TERM'} = sub { &exit("ERROR: Exiting prematurely...received SIG$_[0]!"); };
if ($^O !~ /win/i) { ### SIGHUP and SIGALRM are not supported on Win32
$SIG{'HUP'} = sub { &exit("ERROR: Exiting prematurely...received SIG$_[0]!"); };
$SIG{'ALRM'} = sub { &exit("FAILURE: Execution ran over 30-second limit. Exiting..."); };
alarm(30); # Abort execution if process runs over 30 seconds:
}
}
############
# Function: run()
# This is the core of the program's execution. It simply runs through
# making calls out to other subroutines as the program executes. This
# was done primarily to keep the top of the code clean (for provider
# templates). Note that regardless of success or failure, the program
# should exit from somewhere within run() or one of it's called subroutines.
# In other words, run() should never return to main().
###
sub run
{
# Process the command line arguments to get the variables we're going to use,
# and to catch any really glaring user errors.
processCmdLine(
\my $receiver,
\my $provider,
\my $sender,
\my $data,
\my $proxy,
\my $proxyport );
# Sanity check of the value of our user-input variables.
checkInput(
\$provider,
\$receiver);
# Convert relevant strings to URL-encoding before putting them into the request.
encodeAsURL(
\$sender,
\$receiver,
\$data);
# Parse the user's input to get back all the provider-specific info we'll need
# later on.
getProvider (
\my $web_method,
\my $web_methodstring,
\my $web_referer,
\my $web_cookie,
\my $web_server,
\my $SMSstring,
\my $success_string,
\$provider,
\$receiver,
\$sender,
\$data);
# Open a filehandle for communications with the remote server.
my $server = $web_server;
my $port = 80;
$server = $proxy if ($proxy);
$port = $proxyport if ($proxyport);
openSocket(
\my $filehandle,
\$server,
\$port );
# Send our data to the remote server.
sendData(
\$filehandle,
\$web_method,
\$web_methodstring,
\$web_referer,
\$web_cookie,
\$web_server,
\$SMSstring);
# Check to see if the remote server accepted our data.
if (!waitforReply(
\$filehandle,
\$success_string) )
{
printf("Success! Your message should be on it's way.\n");
# Cleanup: if our data socket is open, close it. Not necessary in most
# cases, but it's good form.
if (<$filehandle>) { close $filehandle; }
exit(0); ### Return to calling program with a success code.
}
else
{
# Close filehandle (if still open) and return to calling program with a failure code.
&exit("FAILURE! $provider replied with an error.",$filehandle);
}
}
############
# Function: processCmdLine(\$receiver, \$provider, \$sender, \$message, \$proxy)
# Pass by reference the following scalars:
# - $receiver is the variable which will store the receiver's cell number.
# - $provider is the variable which will store the service provider's name.
# - $sender is the variable which will store the sender's name / number.
# - $message is the variable which will store the text of the message body.
# Initially these are null scalars, but after parsing the command line,
# these variables will have the above specified values. Note that $sender
# and $message are considered optional (although not specifying $message
# is silly).
# If invalid command-line parameters or garbage are found, the subroutine will
# act accordingly, perhaps with just a warning message, or by halting
# execution with an error message.
# If STDIN contains any info, then it will override data passed via "-m" as the
# text of the message body. This can be useful for some applications, or
# just for script debugging and quick messages.
# One other note: sendSMS.pl will allow the user to send an 'empty' message
# (that is, one with no sender info or message body). This is by design.
# Certain providers will actually allow more data to be passed in the sender
# field than in the message field, so omitting the message body will actually
# allow more text to be sent to the recipient. Also, some providers
# require both sender & message, while others require only one (and still
# others require neither). It has been deemed best to remain flexible, and
# let the user decide which fields to use, depending on provider. As a side
# effect, it is possible to send a 'blank' message with neither sender nor
# message body, although it would be generally silly to do so.
###
sub processCmdLine
{
my $numargs = @ARGV; ### Count # of args passed via cmdline
# Get incoming variables (those passed to this function)
(
my $receiver,
my $provider,
my $sender,
my $message,
my $proxy,
my $proxyport
) = @_;
if ($^O !~ /win/i) { ### STDIN not supported for Win32
# Here we try to parse anything in the STDIN queue (for instance, an
# echo "blah" | sendSMS.pl -r 111222333 -p BLAH command)
use Fcntl; ### Need to use Perl's file control semantics.
# Set STDIN to non-blocking mode (so if there's no input, it won't wait for it)
fcntl(STDIN, F_SETFL, O_NONBLOCK)
or die "ERROR: can't set non blocking: $!";
chomp($$message = <STDIN>); # Check for STDIN message body.
}
# For loop to process each command-line argument in turn...
for (my $count = 0; $count < $numargs; $count++)
{
if ($ARGV[$count] =~ /^-r/ )
{ ### Check for recipient (-r) flag and make sure
$count++; ### next param isn't another argument flag.
if ($ARGV[$count] && $ARGV[$count] !~ /^-/ ) {
$$receiver=$ARGV[$count];
}
}
elsif ($ARGV[$count] =~ /^-p/ )
{ ### Check for provider (-p) flag and make sure
$count++; ### next param isn't another argument flag.
if ($ARGV[$count] && $ARGV[$count] !~ /^-/ ) {
$$provider=$ARGV[$count];
}
}
elsif ($ARGV[$count] =~ /^-s/ )
{ ### Check for sender (-s) flag. The next param
$count++; ### is any text, so a "-" is OK.
if ($ARGV[$count] ) {
$$sender=$ARGV[$count];
}
}
elsif ($ARGV[$count] =~ /^-m/ && !$$message)
{ ### Check for message (-m) flag. The next
$count++; ### param is any text, so a "-" is OK.
if ($ARGV[$count] ) { ### NOTE that if STDIN contains message data,
$$message=$ARGV[$count];###then the -m flag will be ignored.
}
}
elsif ($ARGV[$count] =~ /^-x/ )
{ ### Check for proxy (-x) flag. The next param
$count++; ### is any text, so a "-" is OK.
if ($ARGV[$count] ) {
$$proxy=$ARGV[$count];
if ($$proxy =~ /:/) {
($$proxy, $$proxyport) = split(/:/, $$proxy);
}
}
}
else { ### There's a bad argument somewhere, so
if($ARGV[$count]=~/^-/) { ### print the summary help screen, and exit
&printHelp; ### with a (hopefully) useful error message.
&exit("Invalid command-line parameter: $ARGV[$count]");
}
else { ### If it's unattached command-line trash, then
### ignore it, warn the user, and keep running.
print("sendSMS: Warning: Ignored command-line garbage: $ARGV[$count]\n");
}
}
}
}
############
# Function: checkInput(\$provider, \$receiver)
# Pass by reference two scalars:
# - $provider is the name of our selected service provider.
# - $receiver is the cellular number (10-digit) of our recipient.
#
# This function will simply perform a sanity check on these two values
# to prevent any stupid errors (either on the programmer's or the user's
# part) from causing havoc and possibly sending garbage out on the 'net.
###
sub checkInput
{
(
my $provider,
my $receiver
) = @_;
### Check input:
# Make sure destination number and service provider are not null (we will
# allow null sender and data, however.) Also be sure destination number
# is in the correct format.
if (! $$provider) # was there no provider selected?
{
&printHelp;
# _some_ cmdline params were passed, so we'll give the user a starting hint:
if (@ARGV) {
&exit("HINT: Please specify a service provider code from the above list.");
}
# _no_ cmdline params were passed, so maybe they just want to see the standard "help":
exit(1);
}
## # Basic sanity check on the cellular number (must be exactly 10 digits, positive integer).
## if ($$receiver eq '' || !(length( int($$receiver) ) eq 10) ||
## ($$receiver < 0 && $$receiver > 9999999999) )
## {
## &printHelp; &exit("HINT: Please specify a 10-digit number (ie: 2223334444) for the\n recipient's phone.");
## }
}
############
# Function: encodeAsURL(\$string1[,\$string2 .. $stringN] )
# Pass the reference to one or more strings to this function, and it will
# convert the control characters to properly-formatted URL-encoding.
# Upon completion, the original string(s) in the caller's context will have
# been modified.
###
sub encodeAsURL
{
# A for loop to execute the encode routine for each passed variable:
for (my $count = 0; $count < @_; $count++)
{
my $data = $_[$count];
# Here's the conversion...
$$data =~ s/([^a-zA-Z0-9])/'%'.unpack("H*",$1)/eg;
$$data =~ tr/ /+/;
}
}
############
# Function: openSocket(\$filehandle, \$servername)
# Pass a reference to two scalars in the caller's context:
# - $filehandle is used is a 'pseudo-filehandle' -- that is, it will work
# anywhere a "real" filehandle would, even though it appears to be a regular
# scalar variable. This has some unique advantages, such as being able to
# pass references to a socket back and forth between functions. For
# more info, see http://perl.plover.com/local.html.
# - $servername is the name of the remote server which we are going
# to attempt to connect our socket to.
# NOTE: Failures at this stage will automatically end the program, exiting
# with an error message that will hopefully provide some useful info.
###
sub openSocket
{
# First, we collect the passed references:
(
my $socket,
my $server,
my $port,
) = @_;
# Create the socket. There is some trickery involved with the second line,
# in that we create a null glob and then assign it to scalar $$socket.
# The "do { local *SERVER }" part creates the glob, then destroys it's
# entry in the naming tables. However, the data gets tagged with the name of
# scalar $$socket, which basically means we have a null glob that is
# referenced as a scalar and can be treated as such -- passed around, etc.
# For our purposes this is advantageous.
# See the function comments (above) for more info and a web page which
# explains the process in greater detail.
use Socket;
$$socket = do { local *SERVER };
# Open TCP socket in streaming mode:
socket($$socket, PF_INET, SOCK_STREAM, getprotobyname('tcp')) || &exit("ERROR: couldn't create an outbound streaming TCP socket");
# Create data struct we will be using to make the connection: sockaddr_in(port,IPv4 destination)
my $stream = sockaddr_in ($$port, inet_aton($$server)) || &exit("DEBUG: couldn't get sockaddr_in for hostname\"$$server\"",$$socket);
# Connect our socket ($$socket) to web server defined as ($stream):
connect($$socket, $stream) || &exit("ERROR: couldn't connect to host\"$$server\".\n Perhaps it's down, or doesn't exist?",$$socket);
# Force our output to flush immediately after a print (no buffers).
select($$socket);
$| = 1;
select(STDOUT);
}
###########
# Function: sendData( \$socketFileHandle, \$HTTPRequestMethod,
# \$HTTPRequestString, \$HTTPReferer, \$HTTPCookie, \$remoteHostName,
# \$formDataString )
# Pass this function several references to scalars, as follows:
# - $socketFileHandle is the scalar name of the filehandle we are
# printing to.
# - $HTTPRequestMethod is either "GET" or "POST", depending
# upon what is specified in the selected provider's template.
# - $HTTPRequestString is the rest of the GET or POST line that
# is to be sent to the remote server. Again, part of the template.
# - $HTTPReferer is the referer string defined in the provider's template.
# - $HTTPCookie is the cookie string defined in the provider's template.
# Note that the cookie string can be left blank. In fact, it probably should,
# unless you *know* your provider requires a cookie.
# - $remoteHostName is the name of the remote webserver.
# - $formDataString is the actual data, formatted differently per provider,
# that will be submitted to the provider's website. This string is defined
# as part of each provider's template.
#
# This function will correctly format the GET/POST request, tack on a
# bunch of HTTP headers (some static, some based on the passed data),
# and send the entire string to the server. Note that not all headers are
# *required* -- but we generate them anyway, because we want this
# utility to appear to be a "legit" web client when it hits the server's logs.
###
sub sendData
{
## Get incoming variables
(
my $remoteHostSocket,
my $method,
my $methodstring,
my $referer,
my $cookie,
my $server,
my $data
) = @_;
# If GET is used, then $$data *is* the method string, so overwrite $$methodstring:
if ($$method eq "GET") { $$methodstring = $$data; }
# Build the HTTP request...
my $string = "$$method $$methodstring HTTP\/1.1\015\012";
$string .= "Referer: $$referer\015\012";
$string .= "Host: $$server\015\012";
$string .= "Accept-Language: en-us\015\012";
$string .= "User-Agent: Mozilla\/5.0 \(compatible; Konqueror\/2.0.1; X11\)\015\012";
$string .= "Connection: Keep-Alive\015\012";
# If a cookie is defined (currently, only SPRINT needs one), then add it:
if ($$cookie) { $string .= "Cookie: $$cookie\015\012"; }
# If POST is used, then there are some extra headers to be generated:
if ($$method eq "POST") {
my $length = (length $$data);
$string .= "Content-type: application/x-www-form-urlencoded\015\012";
$string .= "Content-length: $length\015\012";
$string .= "\015\012$$data";
}
# Send the entire string to the remote server as one request:
print $remoteHostSocket ("$string\015\012");
}
############
# Function: waitforReply(\$socketFileHandle, \$successString)
# Pass two scalars by reference, as follows:
# - $socketFileHandle is the scalar name of the filehandle we are
# reading from.
# - $successString is the string we are waiting to receive back
# from the remote server, to signal that we were successful. This is
# defined differently for each provider, as part of their template.
#
# This function will read in all data from the remote end, until either
# (a) the success string is found, or (b) the remote end closes the
# connection. Returns 0 if the string was found, 1 if not.
###
sub waitforReply
{
## Get incoming data...
(
my $returnData,
my $flag
) = @_;
# Here we listen to the remote web server's reply, and wait until we see our
# 'success' string in the reply [meaning the message was accepted], or until
# the remote server closes the socket [meaning the message probably wasn't
# accepted].
while (<$returnData>) {
my $string = $_; # Read data from our open filehandle, until it closes.
if ($string =~ m/$$flag/i) { # Waiting to spot the 'success' string...
return(0); # Success!
}
}
return(1); # Error!
}
############
# Function: printHelp()
# Prints help message to the currently selected output stream, then returns.
###
sub printHelp
{
print $prognfo;
print <<EOM;
Usage: sendSMS.pl -r receiver -p provider [-s sender] [-m message] [-x proxy:port]
EOM
if ($^O !~ /win/i) { ### Win32 support for STDIN is broken
print" or: echo <message> | sendSMS.pl -r receiver -p provider [-s sender] [-x proxy:port]\n";
}
print <<EOM;
<receiver> is the cellular number of phone which will receive
the SMS message. Note: this number MUST be in 10-digit format.
(i.e. '2223334444')
<provider> denotes which provider the receiving cellphone is serviced by.
Currently, the following provider codes are recognized:
EOM
# Generate and print the list of providers:
my $a; my $tmp;
# Call to getProvider w/magic value, to get array list of providers returned:
my @list = getProvider('','','','','get providerNames','','','','','');
# Here we actually format and print the list...
for ($a=1; $a < @list; $a+=3) # Read each provider record
{
$tmp = " $list[$a] - $list[$a + 1]"; # Print the short and long name
printf ("%-41s",$tmp); # right-justified 41-char field
if (@list-3 > $a && length ( $tmp) < 42 ) # If it's not too crowded,
{ # Print a second record too
$a+=3; # Point to next record
print " $list[$a] - $list[$a+1]";
}
print "\n"; # Newline, loop to read more.
}
print <<EOM;
<sender> may be the sender's name, email, phone, etc (any text string)
Note that some services may impose additional limits on the contents of
this field. Although sendSMS doesn't require a sender, some providers may.
<message> is the text body of your message. Since SMS only provides for
160 characters per message (including headers, etc.), this will probably
be 127-145 characters, depending upon your provider.
EOM
}
############
# Function: exit($errorMessageString, $filehandle)
# Exit, printing an error message (if applicable), and close open filehandles.
# Parameters:
# - $errorMessageString: the text of an extended error message. It is
# printed out when the program bails, to give a more detailed explaination
# of the failure.
# - $filehandle: the scalar representation of an open filehandle (glob)
# which needs to be closed before program execution ends.
# NOTE that this function takes 'normally-passed' variables, not references.
###
sub exit
{
if (length($_[0])) { printf "sendSMS: $_[0]\n"; } # Print error message, if passed.
# Cleanup: if our data socket is open, close it. Not necessary in most
# cases, but it's good form.
if (<$_[1]>) { close $_[1]; } # Close open filehandle, if it's still open.
exit(1); ### Return to calling program w/an error code
}
syntax highlighted by Code2HTML, v. 0.9.1