################################################################################
# Copyright (c) 2001 Simplewire. All rights reserved. 
#
# Net::SMS.pm, version 2.51 (EMS Enabled)
# 
#
# Simplewire, Inc. grants to Licensee, a non-exclusive, non-transferable,
# royalty-free and limited license to use Licensed Software internally for
# the purposes of evaluation only. No license is granted to Licensee
# for any other purpose. Licensee may not sell, rent, loan or otherwise
# encumber or transfer Licensed Software in whole or in part,
# to any third party.
#
# For more information on this license, please view the License.txt file
# included with your download or visit www.simplewire.com
#
################################################################################

#---------------------------------------------------------------------
# User documentation within and more is in POD format is at end of
# this file.  Search for =head
#---------------------------------------------------------------------

package Net::SMS;

#---------------------------------------------------------------------
# Version Info
#---------------------------------------------------------------------
$Net::SMS::VERSION = '2.51';
require 5.002;

#---------------------------------------------------------------------
# Other module use
#---------------------------------------------------------------------
use strict;
use Unicode::String qw(utf8 latin1 utf16);
use XML::DOM;
use LWP::UserAgent;
use HTTP::Request;
use HTTP::Response;

######################################################################
# Net::SMS->new();
#
######################################################################

sub new
{

    my $that  = shift;
    my $class = ref($that) || $that;
    local $_;
    my %args;

	#-----------------------------------------------------------------
	# Define default package vars
	#-----------------------------------------------------------------

	# Placeholder
	my $self = {	NOTHING		=> 'nothing'	};

    bless($self, $class);

	$self->reset();

    return $self;
}


sub reset
{

	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    #-----------------------------------------------------------------
	# Define default package vars
    #-----------------------------------------------------------------
	$self->{	DEBUG				}	= 0;

	# constants
	$self->{	RT_SENDPAGE			}	= 'sendpage';
	$self->{	RT_CHECKSTATUS		}	= 'checkstatus';
	$self->{	RT_CARRIERLIST		}	= 'servicelist';
	$self->{	OF_SELECTBOX		}	= 'selectbox';
	$self->{	OF_ALL				}	= 'all';
	$self->{	OT_PRODUCTION		}	= 'production';
	$self->{	OT_DEVELOPMENT		}	= 'development';
	$self->{	PROXY_TYPE_HTTP		}	= '1';
	$self->{	m_CarrierList		}	= [];
	$self->{	m_ClientStatusCode	}	= -1;
	$self->{	m_ClientStatusDesc	}	= '';
	$self->{	m_ConnectionTimeout	}	= 30;
	$self->{	m_ErrorCode			}	= '';
	$self->{	m_ErrorDesc			}	= 'No transaction with the Simplewire network has occured.';
    $self->{    m_ErrorResolution   }   = '';
	$self->{	m_MsgCallback		}	= undef;
	$self->{	m_MsgCarrierID		}	= undef;
	$self->{	m_MsgFrom			}	= undef;
	$self->{	m_MsgImage			}	= undef;
	$self->{	m_MsgImageFilename	}	= undef;
	$self->{	m_MsgPin			}	= undef;
	$self->{	m_MsgRingtone		}	= undef;
	$self->{	m_MsgStatusCode		}	= undef;
	$self->{	m_MsgStatusDesc		}	= undef;
	$self->{	m_MsgText			}	= undef;
	$self->{	m_MsgTicketID		}	= undef;
	$self->{	m_MsgUnicodeText	}	= undef;
	$self->{	m_OptCountryCode	}	= undef;
	$self->{	m_OptDataCoding		}	= undef;
	$self->{	m_OptDelimiter		}	= undef;
    $self->{	m_OptFields			}	= 'all';
	$self->{	m_OptFlash			}	= undef;
	$self->{	m_OptNetworkCode	}	= undef;
	$self->{	m_OptPhone			}	= undef;
	$self->{	m_OptTimeout		}	= 30;
	$self->{	m_OptType			}	= 'production';
    $self->{    m_ProxyType         }   = 'http';
	$self->{	m_ProxyPassword		}	= '';
	$self->{	m_ProxyPort			}	= 80;
	$self->{	m_ProxyServer		}	= undef;
	$self->{	m_ProxyUsername		}	= '';
	$self->{	m_RequestProtocol	}	= 'paging';
	$self->{	m_RequestType		}	= '';
	$self->{	m_RequestVersion	}	= '2.0';
	$self->{	m_RequestXML		}	= '';
	$self->{	m_ResponseProtocol	}	= 'paging';
	$self->{	m_ResponseType		}	= '';
	$self->{	m_ResponseVersion	}	= '2.0';
	$self->{	m_ResponseXML		}	= undef;
	$self->{	m_ServerBeginResponse}	= '<?xml version="1.0" ?>';
	$self->{	m_ServerDomain		}	= 'simplewire.com';
	$self->{	m_ServerEndResponse	}	= '</response>';
	$self->{	m_ServerName		}	= 'wmp-test';
	$self->{	m_ServerFile		}	= '/paging/rpc.xml';
	$self->{	m_ServerPort		}	= 80;
	$self->{	m_ServerProtocol	}	= 'http://';
	$self->{	m_SubscriberID		}	= '';
	$self->{	m_SubscriberPassword}	= '';
	$self->{	m_UserAgent			}	= 'Perl/SMS/2.5.0';
	$self->{	m_UserIP			}	= '';
	$self->{	m_XMLVersion		}	= '1.0';
	# Added for EMS
	$self->{    m_OptContentType    }   = '';
    $self->{    m_EmsElements       }   = [];
	
}


sub carrierList
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return @{ $self->{m_CarrierList} };
}


sub carrierListSend
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return $self->send('servicelist');
}


sub connectionTimeout
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ConnectionTimeout} = shift(); }

    return $self->{m_ConnectionTimeout} if defined($self->{m_ConnectionTimeout}) || return undef;

}


sub debugMode
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{DEBUG} = shift(); }

    return $self->{DEBUG} if defined($self->{DEBUG}) || return undef;

}


sub errorCode
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ErrorCode} = shift(); }

    return $self->{m_ErrorCode} if defined($self->{m_ErrorCode}) || return undef;

}


sub errorDesc
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ErrorDesc} = shift(); }

    return $self->{m_ErrorDesc} if defined($self->{m_ErrorDesc}) || return undef;

}


sub errorResolution
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ErrorResolution} = shift(); }

    return $self->{m_ErrorResolution} if defined($self->{m_ErrorResolution}) || return undef;

}


sub isCarrierlist
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return 1 if ($self->{m_RequestType} eq "servicelist");
    return 0;

}


sub isMsg
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return 1 if ($self->{m_RequestType} eq "sendpage");
    return 0;

}


sub isMsgStatus
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return 1 if ($self->{m_RequestType} eq "checkstatus");
    return 0;

}


sub msgCallback
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

	# if parameter list has length == 1, then pop value and set call back.
    if (@_ == 1) { $self->{m_MsgCallback} = shift(); }

    return $self->{m_MsgCallback} if defined($self->{m_MsgCallback}) || return undef;

}


sub msgCarrierID
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_MsgCarrierID} = shift(); }

    return $self->{m_MsgCarrierID} if defined($self->{m_MsgCarrierID}) || return undef;

}


sub msgCLIIconFilename
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		my $file_path = shift();
		my $hexResult = '';
		my $buf;

		open(fh, "< $file_path") || die "Can't open file \"$file_path\"";
		binmode fh;

		while(read fh, $buf, 1)
		{
			$hexResult .= sprintf( "%2.2lX",  ord($buf) );
		}

		close(fh);

		$self->{m_MsgImageFilename} = $file_path;
		$self->{m_MsgImage}	= $hexResult;
		$self->{m_OptType}	=	'icon';
	}

    return $self->{m_MsgImageFilename} if defined($self->{m_MsgImageFilename}) || return undef;

}


sub msgCLIIconHex
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		my $hexResult = shift();
		$self->{m_MsgImage}	= $hexResult;
		$self->{m_OptType}	=	'icon';
	}

    return $self->{m_MsgImage} if defined($self->{m_MsgImage}) || return undef;
}


sub msgFrom
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_MsgFrom} = shift(); }

    return $self->{m_MsgFrom} if defined($self->{m_MsgFrom}) || return undef;

}


sub msgOperatorLogoFilename
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));
	
    if (@_ == 1)
	{
		my $file_path = shift();
		my $hexResult = '';
		my $buf;

		open(fh, "< $file_path") || die "Can't open file \"$file_path\"";
		binmode fh;

		while(read fh, $buf, 1)
		{
			$hexResult .= sprintf( "%2.2lX",  ord($buf) );
		}

		close(fh);

		$self->{m_MsgImageFilename} = $file_path;
		$self->{m_MsgImage}	= $hexResult;
		$self->{m_OptType}	=	'logo';
	}

    return $self->{m_MsgImageFilename} if defined($self->{m_MsgImageFilename}) || return undef;

}


sub msgOperatorLogoHex
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		my $hexResult = shift();
		$self->{m_MsgImage}	= $hexResult;
		$self->{m_OptType}	=	'logo';
	}

    return $self->{m_MsgImage} if defined($self->{m_MsgImage}) || return undef;
}


sub msgPictureFilename
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		my $file_path = shift();
		my $hexResult = '';
		my $buf;

		open(fh, "< $file_path") || die "Can't open file \"$file_path\"";
		binmode fh;

		while(read fh, $buf, 1)
		{
			$hexResult .= sprintf( "%2.2lX",  ord($buf) );
		}

		close(fh);

		$self->{m_MsgImageFilename} = $file_path;
		$self->{m_MsgImage}	= $hexResult;
		$self->{m_OptType}	=	'picture';
	}

    return $self->{m_MsgImageFilename} if defined($self->{m_MsgImageFilename}) || return undef;

}


sub msgPictureHex
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		my $hexResult = shift();
		$self->{m_MsgImage}	= $hexResult;
		$self->{m_OptType}	=	'picture';
	}

    return $self->{m_MsgImage} if defined($self->{m_MsgImage}) || return undef;
}


sub msgPin
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_MsgPin} = shift(); }

    return $self->{m_MsgPin} if defined($self->{m_MsgPin}) || return undef;

}


sub msgProfileName
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		$self->{m_MsgText} = shift();
		$self->{m_OptType} = 'profile';
	}

	return $self->{m_MsgText} if defined($self->{m_MsgText}) || return undef;
	
}


sub msgProfileRingtone
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		$self->{m_MsgRingtone} = shift();
		$self->{m_OptType}	=	'profile';
	}

    return $self->{m_MsgRingtone} if defined($self->{m_MsgRingtone}) || return undef;

}


sub msgProfileScreenSaverFilename
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		my $file_path = shift();
		my $hexResult = '';
		my $buf;

		open(fh, "< $file_path") || die "Can't open file \"$file_path\"";
		binmode fh;

		while(read fh, $buf, 1)
		{
			$hexResult .= sprintf( "%2.2lX",  ord($buf) );
		}

		close(fh);

		$self->{m_MsgImageFilename} = $file_path;
		$self->{m_MsgImage}	= $hexResult;
		$self->{m_OptType}	=	'profile';
	}

    return $self->{m_MsgImageFilename} if defined($self->{m_MsgImageFilename}) || return undef;

}


sub msgProfileScreenSaverHex
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		my $hexResult = shift();
		$self->{m_MsgImage}	= $hexResult;
		$self->{m_OptType}	=	'profile';
	}

    return $self->{m_MsgImage} if defined($self->{m_MsgImage}) || return undef;
}


sub msgRingtone
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1)
	{
		$self->{m_MsgRingtone} = shift();
		$self->{m_OptType}	=	'ringtone';
	}

    return $self->{m_MsgRingtone} if defined($self->{m_MsgRingtone}) || return undef;

}


sub msgSend
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return $self->send('sendpage');
}


sub msgSendEx
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    $self->msgCarrierId(shift());
    $self->msgPin(shift());
    $self->msgFrom(shift());
    $self->msgCallback(shift());
    $self->msgText(shift());

    return $self->send('sendpage');
}


sub msgStatusCode
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_MsgStatusCode} = shift(); }

    return $self->{m_MsgStatusCode} if defined($self->{m_MsgStatusCode}) || return undef;

}


sub msgStatusDesc
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_MsgStatusDesc} = shift(); }

    return $self->{m_MsgStatusDesc} if defined($self->{m_MsgStatusDesc}) || return undef;

}


sub msgStatusSend
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return $self->send('checkstatus');
}


sub msgText
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_MsgText} = shift(); }

	return $self->{m_MsgText} if defined($self->{m_MsgText}) || return undef;
	
}


sub msgTicketID
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_MsgTicketID} = shift(); }

    return $self->{m_MsgTicketID} if defined($self->{m_MsgTicketID}) || return undef;

}


sub optCountryCode
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptCountryCode} = shift(); }

    return $self->{m_OptCountryCode} if defined($self->{m_OptCountryCode}) || return undef;

}


sub optDataCoding
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptDataCoding} = shift(); }

    return $self->{m_OptDataCoding} if defined($self->{m_OptDataCoding}) || return undef;

}


sub optDelimiter
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptDelimiter} = shift(); }

    return $self->{m_OptDelimiter} if defined($self->{m_OptDelimiter}) || return undef;

}


sub optFields
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptFields} = shift(); }

    return $self->{m_OptFields} if defined($self->{m_OptFields}) || return undef;

}


sub optFlash
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptFlash} = shift(); }

    return $self->{m_OptFlash} if defined($self->{m_OptFlash}) || return undef;

}


sub optNetworkCode
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptNetworkCode} = shift(); }

    return $self->{m_OptNetworkCode} if defined($self->{m_OptNetworkCode}) || return undef;

}


sub optPhone
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptPhone} = shift(); }

    return $self->{m_OptPhone} if defined($self->{m_OptPhone}) || return undef;

}


sub optTimeout
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptTimeout} = shift(); }

    return $self->{m_OptTimeout} if defined($self->{m_OptTimeout}) || return undef;

}


sub optType
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_OptType} = shift(); }

    return $self->{m_OptType} if defined($self->{m_OptType}) || return undef;

}

############################################
# EMS Functionality
# Must set optContentType = "ems" for EMS to work
# emsAddText()
# emsAddPredefinedSound()
# emsAddPredefinedAnimation()
# emsAddUserDefinedSound()
# emsAddSmallPicture()
# emsAddSmallPictureHex()
# emsAddLargePicture()
# emsAddLargePictureHex()
# emsAddUserPromptIndicator()
############################################

sub optContentType
{
	# This function deprecates the optType function and requires
	# a list of constants. So check for 'em
	
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { 
	   
	   # we're being set
	   my $var = shift;
	   
	   my @opts = ("ringtone", "icon", "logo", "picture", "profile", "setting", "ems");
       my $success = 0;
	   foreach my $opt (@opts) {
	       $success = 1 if ($var eq $opt);
	   }
	   
	   if ($success == 1) {
	       # set both vars so we don't break anything
		   # eventually optType should be phased out
		   $self->{m_OptContentType} = $var;
		   $self->{m_OptType} = $var;
	   } else {
	       die "You must set optContentType to one of the following: " . join(", ", @opts) . "\n";
	   }
	   
	}

	# we're being read
    return $self->{m_OptContentType} if defined($self->{m_OptContentType}) || return undef;

}

sub priv_emsAddElement
{
    # Private function that appends to the
	# $self->{m_EmsElements} array
    # 
	# INPUT: name, type, value
	# OUTPUT: sizeof array after push()
	
	# pop value
    my $self = shift();
	
	# build hash
	my $ems = {};
	$ems->{"name"} = shift;
	$ems->{"type"} = shift;
	$ems->{"value"} = shift;
    
	#print "name:" . $ems->{"name"} . "\n";
	#print "type:" . $ems->{"type"} . "\n";
	#print "val: " . $ems->{"value"} . "\n";
	
	#print "size of elements before push:" . $#{$self->{m_EmsElements}} . "\n";
	
	push @{ $self->{m_EmsElements} }, $ems;
	
	#print "size of elements after push:" . $#{$self->{m_EmsElements}} . "\n";

	#my $arr = pop @{$self->{m_EmsElements}};
	#print $arr->{"name"} . "\n";
	#print $arr->{"type"} . "\n";
	#print $arr->{"value"} . "\n";
	
	#print "size of elements after pop:" . $#{$self->{m_EmsElements}} . "\n";
	return $#{$self->{m_EmsElements}} + 1;
	
}

sub emsAddText
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

	# append content to m_EmsElements with helper function
	$self->priv_emsAddElement("text", "", shift);
	
}

sub emsAddPredefinedSound
{
    # EMS Predefined Sound
	# 0 Chimes high
	# 1 Chimes low
	# 2 Ding
	# 3 Ta Da
	# 4 Notify
	# 5 Drum
	# 6 Claps
	# 7 Fan Fare
	# 8 Chords high
	# 9 Chords low
	
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

	# check vals
	my $val = shift;
	if ($val >= 0 && $val < 10) {
    	
		# append content to m_EmsElements with helper function
	    $self->priv_emsAddElement("sound", "predefined", $val);
	
	} else {
	  
	    die "You must use a Predefined Sound between 0 and 9. Please see the perldoc.";
	
	}

}

sub emsAddPredefinedAnimation
{

    # EMS Predefined anim
    # 0 I am ironic, flirty
    # 1 I am glad
    # 2 I am sceptic
    # 3 I am sad
    # 4 WOW!
    # 5 I am crying
    # 6 I am winking
    # 7 I am laughing
    # 8 I am indifferent
    # 9 In love/ kissing
    # 10 I am confused
    # 11 Tongue hanging out
    # 12 I am angry
    # 13 Wearing glasses
    # 14 Devil

	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

	# check vals
	my $val = shift;
	if ($val >= 0 && $val < 15) {
    	
		# append content to m_EmsElements with helper function
	    $self->priv_emsAddElement("animation", "predefined", $val);
	
	} else {
	  
	    die "You must use a Predefined Animation between 0 and 14. Please see the perldoc.";
	
	}

}

sub emsAddUserDefinedSound
{
    # EMS User Defined Sound
	# User defined sounds are sent over the air interface. They are monophonic only,
    # use the iMelody format, and have a maximum length of 128 Bytes (without the
    # use of the UPI (use the word "join" to concatenate lengthy messages)
	
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));
	
	# append content to m_EmsElements with helper function
	$self->priv_emsAddElement("sound", "user", shift);

}

sub emsAddSmallPicture
{
	# EMS Small pictures are 16x16 pixels, Black and white
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    # read in image data
    my $file_path = shift();
    my $hexResult = '';
    my $buf;

    open(fh, "< $file_path") || die "Can't open file \"$file_path\"";
    binmode fh;

    while(read fh, $buf, 1)
    {
        $hexResult .= sprintf( "%2.2lX",  ord($buf) );
    }

    close(fh);

	# append content to m_EmsElements with helper function
	$self->priv_emsAddElement("picture", "small", $hexResult);

}

sub emsAddSmallPictureHex
{
	# EMS Small pictures are 16x16 pixels, Black and white
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

	# append content to m_EmsElements with helper function
	$self->priv_emsAddElement("picture", "small", shift);

}

sub emsAddLargePicture
{
	# EMS Large pictures are 32x32 pixels or of variable size
	# maximum 128 bytes, where width is a multiple of 8 pixels, Black and white
	# Larger pictures may be sent, but the word "join" must be placed
	# in the UPI (user prompt indicator)

	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    # read in image data
    my $file_path = shift();
    my $hexResult = '';
    my $buf;

    open(fh, "< $file_path") || die "Can't open file \"$file_path\"";
    binmode fh;

    while(read fh, $buf, 1)
    {
        $hexResult .= sprintf( "%2.2lX",  ord($buf) );
    }

    close(fh);

	# append content to m_EmsElements with helper function
	$self->priv_emsAddElement("picture", "large", $hexResult);

}

sub emsAddLargePictureHex
{
	# EMS Large pictures are 32x32 pixels or of variable size
	# maximum 128 bytes, where width is a multiple of 8 pixels, Black and white
	# Larger pictures may be sent, but the word "join" must be placed
	# in the UPI (user prompt indicator)
	
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

	# append content to m_EmsElements with helper function
	$self->priv_emsAddElement("picture", "large", shift);

}

sub emsAddUserPromptIndicator
{
    # EMS User Prompt Indicator
	# This feature introduced in 3GPP TS 23.040 Release 4 allows handsets to stitch
	# pictures and user-defined sounds. It also allows the user to be prompted upon
	# reception of the message to execute media specific actions (storage, handset
	# personalisation, etc.). UPI is typically used by content providers when they send
	# content to users. Please refer to tables in chapter 4 for more information about
	# which products support this feature.
	
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

	# append content to m_EmsElements with helper function
	$self->priv_emsAddElement("upi", "", shift);

}

############################################
# End EMS Functionality
############################################


sub requestXML
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_RequestXML} = shift(); }

    return $self->{m_RequestXML} if defined($self->{m_RequestXML}) || return undef;

}


sub responseXML
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ResponseXML} = shift(); }

    return $self->{m_ResponseXML} if defined($self->{m_ResponseXML}) || return undef;

}


sub serverDomain
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ServerDomain} = shift(); }

    return $self->{m_ServerDomain} if defined($self->{m_ServerDomain}) || return undef;

}


sub serverName
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ServerName} = shift(); }

    return $self->{m_ServerName} if defined($self->{m_ServerName}) || return undef;

}


sub serverPort
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ServerPort} = shift(); }

    return $self->{m_ServerPort} if defined($self->{m_ServerPort}) || return undef;

}


sub subscriberID
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    my $var = shift();

    if (defined($var)) { $self->{m_SubscriberID} = $var; }

    return $self->{m_SubscriberID};

}


sub subscriberPassword
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    my $var = shift();

    if (defined($var)) { $self->{m_SubscriberPassword} = $var; }

    return $self->{m_SubscriberPassword};

}


sub success
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    # if the error_code is between 0 and 10 then its an okay response.
    if ($self->errorCode >= 0 and $self->errorCode <= 10 and $self->errorCode ne "")
	{
        return 1;
    }

    return 0;

}


sub synchronous
{
	# Deprecated. Does nothing. Here for backward compatibility.

	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));
}


sub userIP
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    my $var = shift();

    if (defined($var)) { $self->{m_UserIP} = $var; }

    return $self->{m_UserIP};

}


sub userAgent
{
    
    # Deprecated userAgent version 2.13

	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return '';

}

sub	proxyType
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ProxyType} = shift(); }

    return $self->{m_ProxyType} if defined($self->{m_ProxyType}) || return undef;
}

sub	proxyServer
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ProxyServer} = shift(); }

    return $self->{m_ProxyServer} if defined($self->{m_ProxyServer}) || return undef;
}


sub	proxyPort
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ProxyPort} = shift(); }

    return $self->{m_ProxyPort} if defined($self->{m_ProxyPort}) || return undef;
}


sub	proxyUserName
{
	# This is a pass-through function to proxyUsername

	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->proxyUsername( shift() ); }

    return $self->proxyUsername if defined($self->proxyUsername) || return undef;
}


sub	proxyUsername
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ProxyUsername} = shift(); }

    return $self->{m_ProxyUsername} if defined($self->{m_ProxyUsername}) || return undef;
}


sub	proxyPassword
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    if (@_ == 1) { $self->{m_ProxyPassword} = shift(); }

    return $self->{m_ProxyPassword} if defined($self->{m_ProxyPassword}) || return undef;
}


sub toXML
{
	# pop value
    my $self = shift();

	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    #-----------------------------------------------------------------
    # Common heading for all requests
    #-----------------------------------------------------------------

    my $xml =<<ENDXML;
<?xml version="$self->{m_XMLVersion}" ?>
<request version="$self->{m_RequestVersion}" protocol="$self->{m_RequestProtocol}" type="$self->{m_RequestType}">
    <user agent="$self->{m_UserAgent}" ip="$self->{m_UserIP}"/>
    <subscriber id="$self->{m_SubscriberID}" password="$self->{m_SubscriberPassword}"/>
ENDXML

    #-----------------------------------------------------------------
    # If sendpage
    #-----------------------------------------------------------------
    if ($self->isMsg)
	{

		# Check to see if any options were set for the sendpage
    	if (defined($self->optCountryCode) or defined($self->optDataCoding) or
			defined($self->optDelimiter) or defined($self->optNetworkCode) or
			defined($self->optPhone) or defined($self->optType) )
		{
    		$xml .= "    <option";

			# Set the country code option
		    if (defined($self->optCountryCode))
			{
				$xml .= ' countrycode="' . html_encode( $self->optCountryCode ) . '"';
            }

    		# Set the data coding option
            if (defined($self->optDataCoding))
			{
                $xml .= ' datacoding="' . html_encode( $self->optDataCoding ) . '"';
            }

    		# Set the delimiter option
            if (defined($self->optDelimiter))
			{
                $xml .= ' delimiter="' . html_encode($self->optDelimiter) . '"';
            }

            # Set the flash option
            if (defined($self->optFlash))
			{
                $xml .= ' flash="' . html_encode($self->optFlash) . '"';
            }

			# Set the network code option
		    if (defined($self->optNetworkCode))
			{
				$xml .= ' networkcode="' . html_encode( $self->optNetworkCode ) . '"';
            }

			# Set the phone type option
		    if (defined($self->optPhone))
			{
				$xml .= ' phone="' . html_encode( $self->optPhone ) . '"';
            }

			# Set the timeout option
            if (defined($self->optTimeout))
			{
                $xml .= ' timeout="' . $self->optTimeout . '"';
            }

            # Set the type option
		    if (defined($self->optType))
			{
				$xml .= ' type="' . html_encode( $self->optType ) . '"';
            }

			$xml .= "/>\n";
        }

        # Check to see if any page items were set for the sendpage

    	if (defined($self->msgCarrierID) or defined($self->msgPin) or
			defined($self->msgFrom) or defined($self->msgCallback) or
			defined($self->msgText) or defined($self->msgRingtone) or
			defined($self->{m_MsgImage}) or defined($self->{m_EmsElements}) )
		{
    		$xml .= "    <page";


			if (defined( $self->msgCarrierID))
			{
                $xml .= ' serviceid="' . html_encode( $self->msgCarrierID ) . '"';
            }

            if (defined($self->msgPin))
			{
                $xml .= ' pin="' . html_encode( $self->msgPin ) . '"';
            }

            if (defined($self->msgFrom))
			{
                $xml .= ' from="' . unicode_encode( $self->msgFrom ) . '"';
            }

            if (defined($self->msgCallback))
			{
                $xml .= ' callback="' . html_encode( $self->msgCallback ) . '"';
            }

			if (defined($self->msgText))
			{
				$xml .= ' text="' . unicode_encode( $self->msgText ) . '"';
			}

			if (defined($self->msgRingtone))
			{
				$xml .= ' ringtone="' . html_encode( $self->msgRingtone ) . '"';
			}

			if (defined($self->{m_MsgImage}))
			{
				$xml .= ' image="' . $self->{m_MsgImage} . '"';
			}

			$xml .= ">\n";
			
			# EMS FUNCTIONALITY
			# Check to see if EMS was added and place it here
			#print "checking to see if we have ems...\n";
			
			if (defined($self->{m_EmsElements}) && $#{$self->{m_EmsElements}} >= 0) {
			
			   #print "We have EMS\n";
			   
			   # start ems element
			   $xml .= "\t<ems>\n";
			   
			   # add all ems elements
			   my @arr = @{ $self->{m_EmsElements} };
			   foreach my $item (@arr) {
			   
		           #print $item->{name} . "\n";
				   $xml .= "<" . $item->{name};
				   
				   # if type exists, then add it
				   if ($item->{type} ne "") {
				      $xml .= " type=\"" . $self->html_encode($item->{type}) . "\"";
				   }
				   
				   # if value exists, then add it
				   if ($item->{value} ne "") {
				      $xml .= " value=\"";
					  
					  # if type is text, unicode escape
					  if ($item->{name} eq "text") {
 				      
					     $xml .= $self->unicode_encode($item->{value});   				  
					  
					  } elsif ($item->{name} eq "sound") {
					     
						 # sounds need to only have newlines escaped
						 my $tmp = $item->{value};
						 $tmp =~ s/\n/&#10;/g;
						 $tmp =~ s/\r\n/&#10;/g;
						 $xml .= $tmp;
					     #$xml .= $self->unicode_encode($item->{value});						 
						 
					  } else {
				      
					     $xml .= $self->html_encode($item->{value});
					  
					  }
					  
					  $xml .= "\"";
					  
				   }
				   
				   # end element
				   $xml .= "/>\n";
			   
			   } # foreach loop
			   
			   # end ems tag
			   $xml .= "\t</ems>\n";
						   
			}
			# End EMS

		    $xml .= "    </page>\n";
			
        } # End page element

    }

    #-----------------------------------------------------------------
    # If checkstatus
    #-----------------------------------------------------------------
	elsif ($self->isMsgStatus)
	{

		# Check to see if any options were set for the sendpage
    	if (defined($self->msgTicketID))
		{
    		$xml .= "    <ticket";

			# Set the method option
			if (defined($self->msgTicketID))
			{
                $xml .= ' id="' . html_encode($self->msgTicketID) . '"';
            }

			$xml .= "/>\n";
        }

    }

    #-----------------------------------------------------------------
    # If servicelist
    #-----------------------------------------------------------------
	elsif ($self->isCarrierlist)
	{
        # Check to see if any options were set for the servicelist
    	if (defined($self->optFields) or defined($self->optType))
		{
    		$xml .= "    <option";

			# Set the fields option
			if (defined($self->optFields))
			{
                $xml .= ' fields="' . html_encode( $self->optFields ) . '"';
            }

            # Set the type option
            if (defined($self->optType))
			{
                $xml .= ' type="' . html_encode( $self->optType ) . '"';
            }

    		$xml .= "/>";
        }
    }


	#-----------------------------------------------------------------
    # End XML all the same
    #-----------------------------------------------------------------
    $xml .= '</request>';

	$self->{m_RequestXML} = $xml;

	if( $self->{DEBUG} )
	{
		print 'REQUEST XML ==' . "\n" . $self->{m_RequestXML} . "\n";
	}

    return $xml;
}


sub xmlParse
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

    return $self->xmlParseEx($self->toXML());
}


sub xmlParseEx
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));


    if (@_ ne "1") { die "You must pass XML for this functiont to parse"; }

    $self->{ m_ResponseXML } = shift();

	if( $self->{DEBUG})
	{
		print 'RESPONSE XML == ' . "\n" . $self->{ m_ResponseXML } . "\n";
	}

    my $parser = new XML::DOM::Parser;

    # Begin parsing XML post so we can process this transaction
	my $doc = $parser->parsestring ($self->{ m_ResponseXML });

    # Check for <response> element
    my $response = $doc->getElementsByTagName ("response");

    if ($response->getLength() != 1)
	{
        $doc->dispose();
        $self->raise_error(101);
        return;
    }

    # At this point, the document should be validated
    $response = $doc->getDocumentElement();


    ##################################################################
    # Parse required <response> attributes
    ##################################################################

	#-----------------------------------------------------------------
	# Parse <response> version attribute
        #-----------------------------------------------------------------
	my $response_version = $response->getAttributeNode("version");

    if (!defined($response_version))
	{
        $doc->dispose();
        $self->raise_error(102);
        return;
    }

    $self->{m_ResponseVersion} = $response_version->getValue();


    #-----------------------------------------------------------------
	# Parse <response> protocol attribute
    #-----------------------------------------------------------------
	my $response_protocol = $response->getAttributeNode("protocol");

    if (!defined($response_protocol))
	{
        $doc->dispose();
        $self->raise_error(103);
        return;
    }

    $self->{m_ResponseProtocol} = $response_protocol->getValue();


    #-----------------------------------------------------------------
	# Parse <response> type attribute
    #-----------------------------------------------------------------
	my $response_type = $response->getAttributeNode("type");

    if (!defined($response_type))
	{
        $doc->dispose();
        $self->raise_error(104);
        return;
    }

    my $type = $response_type->getValue();
	
    if ($type eq "sendpage")
	{
        $self->{m_ResponseType} = "sendpage";
    }
	elsif ($type eq "checkstatus")
	{
		$self->{m_ResponseType} = "checkstatus";
    }
	elsif ($type eq "servicelist")
	{
		$self->{m_ResponseType} = "servicelist";
    }
	else
	{
		# Do nothing here, so error code and desc can be retrieved.
    }

    ##################################################################
    # Parse Errors
    ##################################################################

    my $errors = $doc->getElementsByTagName("error");

    if ($errors->getLength() > 0)
	{
		my $error = $errors->item(0);

        # Now get attributes for the error element

        #-----------------------------------------------------------------
		# Parse <error> code attribute
        #-----------------------------------------------------------------
		my $error_code = $error->getAttributeNode("code");

	    if (defined($error_code))
		{
        	$self->errorCode($error_code->getValue());
	    }

        #-----------------------------------------------------------------
	    # Parse <error> description attribute
	    #-----------------------------------------------------------------
		my $error_desc = $error->getAttributeNode("description");

	    if (defined($error_desc))
		{
        	$self->errorDesc($error_desc->getValue());
	    }

        #-----------------------------------------------------------------
	    # Parse <error> resolution attribute
	    #-----------------------------------------------------------------
		my $error_resolution = $error->getAttributeNode("resolution");

	    if (defined($error_resolution))
		{
        	$self->errorResolution($error_resolution->getValue());
	    }
	}


    ##################################################################
    # Parse Status
    ##################################################################

    my $stats = $doc->getElementsByTagName("status");

    if ($stats->getLength() > 0)
	{

		my $status = $stats->item(0);

        # Now get attributes for the status element

            #----------------------------------------------------------------

	    # Parse <status> code attribute
	    #-----------------------------------------------------------------
		my $status_code = $status->getAttributeNode("code");

	    if (defined($status_code))
		{
        	$self->msgStatusCode($status_code->getValue());
	    }

		#-----------------------------------------------------------------
		# Parse <status> description attribute
		#-----------------------------------------------------------------
		my $status_desc = $status->getAttributeNode("description");

	    if (defined($status_desc))
		{
        	$self->msgStatusDesc($status_desc->getValue());
	    }
	}

    ##################################################################
    # Ticket
    ##################################################################

    my $tickets = $doc->getElementsByTagName("ticket");

    if ($tickets->getLength() > 0)
	{

		my $ticket = $tickets->item(0);

        # Now get attributes for the error element

        #-----------------------------------------------------------------
		# Parse <ticket> id attribute
	    #-----------------------------------------------------------------
		my $ticket_id = $ticket->getAttributeNode("id");

	    if (defined($ticket_id))
		{
        	$self->msgTicketID($ticket_id->getValue());
	    }
	}


    ##################################################################
    # Parse service list return!
    ##################################################################

    my $services = $doc->getElementsByTagName("service");

    # If Services Greater Than 1 Then Reset Service List
    if ($services->getLength() > 0)
	{
		$self->{m_CarrierList} = [];
    }

    for (my $index = 0; $index < $services->getLength(); $index++)
	{
		my $service = $services->item($index);

        # Construct a hash to put all the services into
        my $s = {};

		my $id = $service->getAttributeNode("id");

	    if (defined($id))
		{
        	$s->{ID} = $id->getValue();
	    }

        my $title = $service->getAttributeNode("title");

	    if (defined($title))
		{
        	$s->{Title} = $title->getValue();
	    }

        my $subtitle = $service->getAttributeNode("subtitle");

	    if (defined($subtitle))
		{
        	$s->{SubTitle} = $subtitle->getValue();
	    }

        my $contenttype = $service->getAttributeNode("contenttype");

	    if (defined($contenttype))
		{
        	$s->{ContentType} = $contenttype->getValue();
	    }

        my $pinrequired = $service->getAttributeNode("pinrequired");

	    if (defined($pinrequired))
		{
        	$s->{PinRequired} = $pinrequired->getValue();
	    }

        my $pinminlength = $service->getAttributeNode("pinminlength");

	    if (defined($pinminlength))
		{
        	$s->{PinMinLength} = $pinminlength->getValue();
	    }

        my $pinmaxlength = $service->getAttributeNode("pinmaxlength");

	    if (defined($pinmaxlength))
		{
        	$s->{PinMaxLength} = $pinmaxlength->getValue();
	    }

        my $textrequired = $service->getAttributeNode("textrequired");

	    if (defined($textrequired))
		{
        	$s->{TextRequired} = $textrequired->getValue();
	    }

        my $textminlength = $service->getAttributeNode("textminlength");

	    if (defined($textminlength))
		{
        	$s->{TextMinLength} = $textminlength->getValue();
	    }

        my $textmaxlength = $service->getAttributeNode("textmaxlength");

	    if (defined($textmaxlength))
		{
        	$s->{TextMaxLength} = $textmaxlength->getValue();
	    }

        my $fromrequired = $service->getAttributeNode("fromrequired");

	    if (defined($fromrequired))
		{
        	$s->{FromRequired} = $fromrequired->getValue();
	    }

        my $fromminlength = $service->getAttributeNode("fromminlength");

	    if (defined($fromminlength))
		{
        	$s->{FromMinLength} = $fromminlength->getValue();
	    }

        my $frommaxlength = $service->getAttributeNode("frommaxlength");

	    if (defined($frommaxlength))
		{
        	$s->{FromMaxLength} = $frommaxlength->getValue();
	    }

        my $callbackrequired = $service->getAttributeNode("callbackrequired");

	    if (defined($callbackrequired))
		{
        	$s->{CallbackRequired} = $callbackrequired->getValue();
	    }

        my $callbacksupported = $service->getAttributeNode("callbacksupported");

	    if (defined($callbacksupported))
		{
        	$s->{CallbackSupported} = $callbacksupported->getValue();
	    }

        my $callbackminlength = $service->getAttributeNode("callbackminlength");

	    if (defined($callbackminlength))
		{
        	$s->{CallbackMinLength} = $callbackminlength->getValue();
	    }

        my $callbackmaxlength = $service->getAttributeNode("callbackmaxlength");

	    if (defined($callbackmaxlength))
		{
        	$s->{CallbackMaxLength} = $callbackmaxlength->getValue();
	    }

        my $type = $service->getAttributeNode("type");

	    if (defined($type))
		{
        	$s->{Type} = $type->getValue();
	    }
		
        my $smartmsg = $service->getAttributeNode("smartmsg");

	    if (defined($smartmsg))
		{
        	$s->{SmartMsgID} = $smartmsg->getValue();
	    }

		# New Carrier Recognition Functions
		my $country_code = $service->getAttributeNode("countrycode");

        if (defined($country_code))
		{
        	$s->{CountryCode} = $country_code->getValue();
        }

		my $country_name = $service->getAttributeNode("countryname");

        if (defined($country_name))
		{
        	$s->{CountryName} = $country_name->getValue();
        }

		my $country_reg = $service->getAttributeNode("countryregion");

		if (defined($country_reg))
		{
        	$s->{CountryRegion} = $country_reg->getValue();
    	}

		##############################################################
        # Now push hash onto service_list array
        ##############################################################
		push @{ $self->{m_CarrierList} }, $s;
	}

	if($self->{DEBUG})
	{
		print 'XMLParseEx @exit:' . "\n";
		print "Client Status Code: $self->{m_ClientStatusCode}\n";
		print "Client Status Desc: $self->{m_ClientStatusDesc}\n";
		print "ErrorCode == " . $self->errorCode . "\n";
		print "ErrorDesc == " . $self->errorDesc . "\n\n";
        print "ErrorResolution == " . $self->errorResolution . "\n\n";
	}

}

######################################################################
#
# PRIVATE FUNCTIONS
#
######################################################################

sub escape
{
    shift() if ref($_[0]);
    my $toencode = shift();
    return undef unless defined($toencode);
    $toencode=~s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
    return $toencode;
}


sub html_encode
{
    shift() if ref($_[0]);
    my $toencode = shift();
    return undef unless defined($toencode);

    $toencode =~ s/</&lt;/g;
    $toencode =~ s/>/&gt;/g;
    $toencode =~ s/&/&amp;/g;
    $toencode =~ s/"/&quot;/g;
    $toencode =~ s/'/&apos;/g;

    return $toencode;
}


sub unicode_encode
{
    shift() if ref($_[0]);
    my $toencode = shift();
    return undef unless defined($toencode);

	Unicode::String->stringify_as("utf8");
	my $unicode_str = Unicode::String->new();
	my $text_str = "";
	my $pack_str = "";


	# encode Perl UTF-8 string into latin1 Unicode::String
	#  - currently only Basic Latin and Latin 1 Supplement
	#    are supported here due to issues with Unicode::String .
	$unicode_str->latin1( $toencode );

	# Convert to hex format ("U+XXXX U+XXXX ")
	$text_str = $unicode_str->hex;

	# Now, the interesting part.
	# We must search for the (now hex-encoded)
	#	Simplewire Unicode escape sequence.
	my $pattern = 'U\+005[C|c] U\+0058 U\+00([0-9A-Fa-f])([0-9A-Fa-f]) U\+00([0-9A-Fa-f])([0-9A-Fa-f]) U\+00([0-9A-Fa-f])([0-9A-Fa-f]) U\+00([0-9A-Fa-f])([0-9A-Fa-f])';


	# Replace Simplewire escapes with entities (beginning of string)
	$_ = $text_str;
	if( /^$pattern/ )
	{
		$pack_str = pack "H8", "$1$2$3$4$5$6$7$8";
		$text_str =~ s/^$pattern/\&#x$pack_str/;
	}

	# Replace Simplewire escapes with entities (middle of string)
	$_ = $text_str;
	while( / $pattern/ )
	{
		$pack_str = pack "H8", "$1$2$3$4$5$6$7$8";
		$text_str =~ s/ $pattern/\;\&#x$pack_str/;
		$_ = $text_str;
	}


	# Replace "U+"  with "&#x"	(beginning of string)
	$text_str =~ s/^U\+/&#x/ ;

	# Replace " U+" with ";&#x"	(middle of string)
	$text_str =~ s/ U\+/;&#x/g ;


	# Append ";" to end of string to close last entity.
	# This last ";" at the end of the string isn't necessary in most parsers.
	# However, it is included anyways to ensure full compatibility.
	if( $text_str ne "" )
	{
		$text_str .= ';';
	}

    return $text_str;
}


sub handle_http_error
{
	my $self = shift();
	my $http_error = shift();

	my $errorLookup = 
	{
		#HTTP       Simplewire
		#ERROR      ERROR
		#---------------------
		400		=>	251,
		401		=>	252,
		402		=>	253,
		403		=>	254,
		404		=>	255,
		405		=>	256,
		406		=>	257,
		407		=>	258,
		408		=>	259,
		409		=>	260,
		410		=>	261,
		411		=>	262,
		412		=>	263,
		413		=>	264,
		414		=>	265,
		415		=>	266,

		500		=>	267,
		501		=>	268,
		502		=>	269,
		503		=>	270,
		504		=>	271,
		505		=>	272,
	};

	# check if it was anything but success codes
	if( $http_error >= 200 && $http_error < 300 )
	{
		# return that no error was found
		$self->raise_error( 0 );
		return 0;
	}

	# Check if valid http error number
	if(  defined( $errorLookup->{$http_error} )  )
	{
		# valid http error number, so set Simplewire error
		$self->raise_error( $errorLookup->{$http_error} );
		return 1;
	}
	
	# At this point, we know that the error is not a success code
	# Nor is it an http error on our list of http errors, so return 0
	# 	- no http error.
	return 0;
}

sub raise_error
{

    my $self = shift();
    my $error = shift();

    $self->errorCode($error);
	
	my $errorLookup = 
	{

		# Client/Internet Error Codes
		101		=>	"Error while parsing response.  Request was sent off.",
		102		=>	"The required version attribute of the response element was not found in the response.",
		103		=>	"The required protocol attribute of the response element was not found in the response.",
		104		=>	"The required type attribute of the response element was not found in the response.",
		105		=>	"The client tool does not know how to handle the type of response.",
		106		=>	"A connection could not be established with the Simplewire network.",
		107		=>	"Internet The connection timed out.",
		108		=>	"Internet An internal error occured while connecting.",
		109		=>	"Internet Trying to use an invalid URL.",
		110		=>	"Internet The host name could not be resolved.",
		111		=>	"Internet The specified protocol is not supported.",
		112		=>	"Internet An error occured while authenticating.",
		113		=>	"Internet An error occured while logging on.",
		114		=>	"Internet An invalid operation was attempted.",
		115		=>	"Internet The request is pending.",
		116		=>	"Internet An error occured while processing the proxy request.",
		117		=>	"Internet SOCKS server returned an invalid version.",
		118		=>	"Internet SOCKS error while connecting.",
		119		=>	"Internet SOCKS authentication error.",
		120		=>	"Internet SOCKS general error.",
		121		=>	"Internet Proxy authentication error.",
		122		=>	"Internet The proxy host name could not be resolved.",
		123		=>	"Internet An error occured while transfering data.",

		# HTTP Errors
		250		=>	"HTTP Error.",
		251		=>	"HTTP Bad request.",					# 400
		252		=>	"HTTP Unauthorized.",					# 401
		253		=>	"HTTP Payment required.",				# 402
		254		=>	"HTTP Forbidden.",						# 403
		255		=>	"HTTP Not found.",						# 404
		256		=>	"HTTP Method not allowed.",				# 405
		257		=>	"HTTP Not acceptable.",					# 406
		258		=>	"HTTP Proxy authentication required.",	# 407
		259		=>	"HTTP Request timeout.",				# 408
		260		=>	"HTTP Conflict.",						# 409
		261		=>	"HTTP Gone.",							# 410
		262		=>	"HTTP Length required.",				# 411
		263		=>	"HTTP Precondition failed.",			# 412
		264		=>	"HTTP Request Entity too large.",		# 413
		265		=>	"HTTP Request-URI too long.",			# 414
		266		=>	"HTTP Unsupported media type.",			# 415
		267		=>	"HTTP Internal server error.",			# 500
		268		=>	"HTTP Not implemented.",				# 501
		269		=>	"HTTP Bad gateway.",					# 502
		270		=>	"HTTP Service unavailable.",			# 503
		271		=>	"HTTP Gateway timeout.",				# 504
		272		=>	"HTTP Version not supported.",			# 505
	};


	# Check if valid error number
	if(  defined( $errorLookup->{$error} )  )
	{
		# valid error number, so set error description
		$self->errorDesc( $errorLookup->{$error} );
	}
	else
	{
		# invalid error number, so set general error
		$self->errorCode( 106 );
		$self->errorDesc( $errorLookup->{106} );
	}
	
}


sub prepare_post
{
	my $self = shift();
	my $varref = shift();

	my $body = "";
	# cycle through all key/value pairs and add to content
	while (my ($var,$value) = map { escape($_) } each %$varref)
	{
		if ($body)
		{
			$body .= "&$var=$value";
		}
		else
		{
			$body = "$var=$value";
		}

	}

	# return newly formed content
	return $body;
}



sub send
{
	# pop value
    my $self = shift();
	
	# check to make sure that this function is being called on an object
    die "You must instantiate an object to use this function" if !(ref($self));

	$self->{m_RequestType} = shift();
    my $txt = "";
    my %vars = (
		"xml" => $self->toXML()
	);


    ##################################################################
    # Create LWP::UserAgent Object
    ##################################################################
	my $http = new LWP::UserAgent;
	$http->timeout( $self->connectionTimeout );
	$http->agent( $self->{m_UserAgent} . ' ' . $http->agent );
	if( defined( $self->{m_ProxyServer} ) )
	{
		$http->proxy('http', $self->{m_ServerProtocol} . $self->proxyServer . ':' . $self->proxyPort . '/');
	}

	
    ##################################################################
    # Begin loop for redundancy
    ##################################################################
	my $httpErrorEvent = undef;
	
	# Create a request
	my $request = undef;
	
	my $response = undef;

	my $body = undef;
	
   	##########################################################
	# Create the url to retrieve
	##########################################################
	my $server_name = $self->serverName . "." . $self->serverDomain . ":" . $self->serverPort;
	my $full_file = $self->{m_ServerProtocol} . $server_name . $self->{m_ServerFile};

	##########################################################
	# Request and get response
	##########################################################


	$body = $self->prepare_post(\%vars);

	# Finish setting up request
	$request = new HTTP::Request( POST => $full_file);
	$request->content_type("application/x-www-form-urlencoded");
	$request->content($body);
	$request->header( 'Accept' => 'text/xml' );
	$request->proxy_authorization_basic( $self->proxyUsername,
										 $self->proxyPassword );


	# send off request and get response
	$response = $http->request($request);


	$self->{m_ClientStatusCode} = $response->code;
	$self->{m_ClientStatusDesc} = $response->message;

	if( $self->handle_http_error( $self->{m_ClientStatusCode} ) )
	{
		$httpErrorEvent = 1;
	}

	if ( $self->{DEBUG} && defined( $self->proxyServer ) && $response->is_success )
	{
		print "Successful Proxy\n";
	}
	elsif( $self->{DEBUG} && defined( $self->proxyServer ) )
	{
		print "Failed Proxy\n";
	}


	if ( defined($response) && defined($response->content) )
	{
		$txt = $response->content;
	}
	else
	{
		$txt = "";
	}


	if($self->{DEBUG})
	{
		print "@ SEND\n";
		print "Client Status Code: $self->{m_ClientStatusCode}\n";
		print "Client Status Desc: $self->{m_ClientStatusDesc}\n";
		print "m_ErrorCode == " . $self->errorCode . "\n";
		print "m_ErrorDesc == " . $self->errorDesc . "\n";
	}


	# now, check for errors, special cases. Parse response.
	# Check for HTTP Error
	if ( defined($httpErrorEvent) )
	{
		# do nothing. Http error codes were already set.
		return 0;
	}
	elsif (defined($txt) && $txt eq "")
	{
    	$self->raise_error(106);
        return 0;
	}
	# Now parse the xml
	else
	{
    	# Cleanup text
    	if (defined($txt))
		{
			$txt =~ s/^.*<\?xml/<\?xml/gs;
                        if ($^O eq "MSWin32") {
				if ($txt =~ s/.land Mobiltelefon AB/ land Mobiltelefon AB/) {
	                                #print "Found plus sign\n";
	                        }
				if ($txt =~ s/[ιηυΤΙσ]/_/g) {
	                               #print "Found bad char\n";
	                        }
                        }

        	$self->xmlParseEx($txt);
        	return 1;
        }
		else
		{
        	# Problem, set general error. Return fail.
			$self->raise_error(106);
            return 0;
        }
    }
}

1;
__END__;


######################## User Documentation ##########################


## To format the following user documentation into a more readable
## format, use one of these programs: pod2man; pod2html; pod2text.

=head1 NAME

Net::SMS - Sends wireless messages to any carrier including text messages and
SMS (Short Message Service).

=head1 SYNOPSIS

The Perl SMS SDK provides easy, high-level control of the Simplewire wireless
text-messaging platform. The Perl SMS SDK was designed to be
as developer-friendly as possible by hiding the intricacies of the XML format
required to communicate with the Simplewire WMP (Wireless Message Protocol)
servers. The Perl SMS SDK makes it possible to send an SMS message off with
as little as two lines of code.

This software is commercially supported. Go to www.simplewire.com
for more information.

=head1 INSTALLATION

For very detailed instructions, please refer to the .PDF manual that
has been included in the /docs directory of the Net-SMS-X.XX.tar.gz
download.  Once you unzip and untar this file, inside the /docs
directory will be very detailed installation instructions.

If you are advanced in Perl, then you may just follow the
instructions below. Place the release file in the root directory. 
In the root directory, execute
the following commands, where "X.XX" represents the specific version being used.

[root]# tar -zxvf Net-SMS-X.XX.tar.gz

[root]# cd Net-SMS-X.XX

[Net-SMS-X.XX]# perl Makefile.PL

[Net-SMS-X.XX]# make

[Net-SMS-X.XX]# make install

=head1 EXAMPLES

See the /examples folder that is contained within the Net-SMS-X.XX.tar.gz
download file.

=head1 QUICK START

# Import Module
use Net::SMS;

# Create Object
my $sms = Net::SMS->new();

# Subscriber Settings
$sms->subscriberID("123-456-789-12345");
$sms->subscriberPassword("Password Goes Here");

# Message Settings
$sms->msgPin("+1 100 510 1234");
$sms->msgFrom("Demo");
$sms->msgCallback("+1 100 555 1212");
$sms->msgText("Hello World From Simplewire!");

print "Sending message to Simplewire...\n";

# Send Message
$sms->msgSend();

# Check For Errors
if ($sms->success)
{
    print "Message was sent!\n";
}
else
{
    print "Message was not sent!\n";
    print "Error Code: " . $sms->errorCode() . "\n";
    print "Error Description: " . $sms->errorDesc() . "\n";
    print "Error Resolution: " . $sms->errorResolution() . "\n";
}

=head1 EMS (Enhanced Message Service)

Quick start for EMS:

    $sms->optContentType("ems");
    $sms->emsAddPredefinedSound(1);
    $sms->emsAddPredefinedAnimation(1);
    $sms->emsAddSmallPicture("example.gif");
    $sms->emsAddText("This is an EMS from Simplewire!");

Simplewire supports sending EMS messages via its network. The Enhanced 
Messaging Service (EMS) uses standard SMS and allows the user
to add fun visual and audible content to their message. For example, 
simple animations, pictures, melodies, sounds and even formatting of 
the text itself, everything mixed together seamlessly into one message.

To activate EMS add the following line to your code:

    $sms->optContentType("ems");

This is a summary of the EMS functions built-in to this SDK:

    emsAddText()
    emsAddPredefinedSound()
    emsAddPredefinedAnimation()
    emsAddUserDefinedSound()
    emsAddSmallPicture()
    emsAddSmallPictureHex()
    emsAddLargePicture()
    emsAddLargePictureHex()
    emsAddUserPromptIndicator()

SMS, and therefore EMS, are not actually sent from handset across the 
mobile network to handset as it appears to users, but instead messages 
are sent from handsets, or from Simplewire's network, to a Short 
Message Service Center (SMSC) resident on the Operators network, and 
then on to the receiving handset.

EMS has a Store and Forward model  i.e. messages are forwarded to the
receiving handset as soon as it is reachable, and a user does not have to access
a network-based inbox to receive messages. Indeed EMSs can be received whilst
a handset is making a voice call, browsing the Internet, etc. Further, delivery
reporting is also supported to enable a user to check that a message has been
successfully delivered.

Therefore, EMS has many advantages as a messaging platform for the mobile
world, where convenience and ease of use are key.

=head2 Pictures

    # 16x16 image, black and white
    $sms->emsAddSmallPicture("example.gif");

    # 32x32 image, black and white
    $sms->emsAddLargePicture("large.gif");

	
Pictures are contained within a single SM (Short Message, or segment if
describing an SM that is part of a concatenated message). It is possible to
include either small (16*16 pixels) or large (32*32 pixels). Larger
pictures may be sent by joining small pictures together using the 
emsAddUserPromptIndicator() function. Please see below for UPI description.

EMS Release 4 supports black and white pictures. All pictures are user defined 
i.e. although they are either stored on the handset during manufacture,
downloaded, or stored from other messages, they are called user-defined as the
picture itself is sent over the air (see various predefined media detailed below).

Simplewire's network will convert color GIF images into black and white
automatically using a method that takes any color above 50% brightness and turning it
to white, and anything below 50% brightness to black. So #999999 is converted
to white, while #336699 is converted to black. Of course this example is
representing colors using the standard web pallette, but you get the idea.

For exact image recreation, use Photoshop or another editing program to 
convert your image to black and white.

=head2 Animations

    # I am laughing
    $sms->emsAddPredefinedAnimation(7);

There are a number of predefined animations. These animations are not sent
over the air interface, only the identification of them. Basically the originating
terminal sends an instruction to the receiving terminal to play, say, pre-defined
animation number 9.

As soon as the position of the animation in the SM data is reached, the animation
corresponding to the received number is displayed in a manner which is
manufacturer specific. Animations are played as soon they are focused.
There are 6 predefined animations in Release 4.1.0 (0-5) of EMSI and 
additional 9 ones as of Release 4.3.0 (0-14) of EMSI. Please find an 
overview of all these predefined animations below:

=head3 Animation Description

    0 I am ironic, flirty
    1 I am glad
    2 I am sceptic
    3 I am sad
    4 WOW!
    5 I am crying
    6 I am winking
    7 I am laughing
    8 I am indifferent
    9 In love/ kissing
    10 I am confused
    11 Tongue hanging out
    12 I am angry
    13 Wearing glasses
    14 Devil

=head2 Sounds

These may be inserted into text messages to provide audible indications and
experiences to the recipient. When they are received, they are played by the
receiving handset at an appropriate point in the message.

=head3 Predefined

    # Play the Drums
    $sms->emsAddPredefinedSound(5);

There are a number of predefined sounds. These sounds are not transferred over
the air interface, only the identification of them. There are 10 different sounds
that can be added in the message, and as soon as the sound mark is in focus (on
the display), the sound will be played.

Below please find an overview of all these predefined sounds:

    0 Chimes high
    1 Chimes low
    2 Ding
    3 Ta Da
    4 Notify
    5 Drum
    6 Claps
    7 Fan Fare
    8 Chords high
    9 Chords low

=head3 User Defined

    # Play my sound
    $sms->emsAddUserDefinedSound("MELODY:*5c5*5e4*5c5*5e4*4e5*4g4*4e5");

User defined sounds are sent over the air interface. They are monophonic only,
use the iMelody format, and have a maximum length of 128 Bytes without the
use of the UPI (see the UPI section below). Please note, we have found
that many EMS phones do not support UPI for user defined melodies.

We have found that the following format, although based on the EMSI standard,
bloats the melody data heavily, and is not needed. The MELODY: line item
is typically all you need. 

For example, this will work fine:

    MELODY:*5f3r4*5f4*5c4r4*5f1r3*4#g3*4a2*5c3*4f2r3*4a4*5c4*5f3

Rather than:

    BEGIN:IMELODY
	VERSION:1.2
	FORMAT:CLASS1.0
	NAME:A-Team Theme Song
	MELODY:*5f3r4*5f4*5c4r4*5f1r3*4#g3*4a2*5c3*4f2r3*4a4*5c4*5f3
	END:IMELODY

The official format of the iMelody is constituted of a header, the melody and a footer.

=head4 Header

    Desc:      BEGIN:IMELODY<cr><line-feed>
    Example:   BEGIN:IMELODY<cr><line-feed>
    Status:    Mandatory

    Desc:      VERSION:<version><cr><line-feed>
    Example:   VERSION:1.2<cr><line-feed> 
    Status:    Mandatory (We've found this to be optional)

    Desc:      FORMAT:<format><cr><linefeed>
    Example:   FORMAT:CLASS1.0<cr><line-feed> 
    Status:    Mandatory (We've found this to be optional)

    Desc:      NAME:<characters-notlf><cr><line-feed>
    Example:   NAME:My song<cr><line-feed> 
    Status:    Optional

    Desc:      COMPOSER:<characters-notlf><cr><line-feed>
    Example:   COMPOSER:John Doe<cr><line-feed> 
    Status:    Optional

    Desc:      BEAT:<beat><cr><line-feed> 
    Example:   BEAT:240<cr><line-feed> 
    Status:    Optional

    Desc:      STYLE:<style><cr><line-feed>
    Example:   STYLE:S2<cr><line-feed> 
    Status:    Optional

    Desc:      VOLUME:<volume><cr><linefeed>
    Example:   VOLUME:V8<cr><line-feed> 
    Status:    Optional

    <format> ::= CLASS1.0
    iMelody also defines a "CLASS2.0" format.

    <beat>::="25" | "26" | "27" | ... | "899" | "900"
    <style>::= "S0" | "S1" | "S2"
    <volume-modifier>::=V+|V- (changes volume + or  from current volume)
    <volume>::="V0" | "V1" | ... | "V15" |<volume-modifier>
    <characters-not-lf> ::= Any character in the ASCII character-set except <line-feed>.

=head4 Footer

    Desc:      END:IMELODY<cr><line-feed> 
    Example:   END:IMELODY<cr><line-feed> 
    Status:    Mandatory

=head4 Melody

    Desc:      MELODY:<melody><cr><linefeed>
    Example:   MELODY:c2d2e2f2<cr><line-feed> 
    Status:    Mandatory

The melody is composed as follow:
    <melody> ::= { <silence> | <note> | <led> | <vib> | <backlight> | <repeat> |
    <volume> }+
    <volume-modifier>::=V+|V- (changes volume + or  from current volume)
    <volume>::="V0" | "V1" | ... | "V15" |<volume-modifier>
    <led> ::= ledoff | ledon
    <vibe> ::= vibeon | vibeoff
    <backlight> ::= backon | backoff
    <repeat> ::= ( | ) | @<repeat-count>
    <repeat-count> ::= 0 | 1 | ...
    <silence> ::= r<duration>[<duration-specifier>]
    <note> ::= [<octave-prefix>]<basic-ess-iss-note><duration>[<duration-specifier>]
    <duration> := 0 | 1 | 2 | 3 | 4 | 5
    <duration-specifier> ::= . | : | ;
    <octave-prefix> ::= *0 | *1 | ... | *8 (A=55Hz) | (A=110Hz) | ... |
    (A=14080Hz)
    <basic-ess-iss-note> ::= <basic-note> | <ess-note> | <iss-note>
    <basic-note> ::= c | d | e | f | g | a | b
    <ess-note> ::= &d | &e | &g | &a | &b
    <iss-note> ::= #c | #d | #f | #g | #a

Duration

    Value Duration
    0     Full-note
    1     ½-note
    2     Ό-note
    3     1/8-note
    4     1/16-note
    5     1/32-note

Duration Specifier

    Value Duration
          No special duration
    .     Dotted note
    :     Double dotted note
    ;     2/3 length

The octave prefix only applies to the immediately following note. 
If not specified, the default octave-prefix is *4. i.e. A=880Hz.

The repeat blocks cannot be nested in this simple CLASS1.0 definition.
The default character set is UTF-8.

The maximum length for a melody is 128 bytes (this includes the melody header
and footer).

Example of a «CLASS1» iMelody object:

    Mandatory Header       BEGIN:IMELODY
                           VERSION:1.2
                           FORMAT:CLASS1.0
    Mandatory Melody       MELODY:&b2#c3Vc2*4g3d3V+#d1r3d2e2:d1V+f2f3.
    Mandotory Footer       END:IMELODY

=head2 Concatenation

    # Concatenate three SMS messages to make one
    # Each SMS can contain 140 bytes, and we've
    # added enough content to take up 320 bytes
    # 140 bytes - 1st message
    # 140 bytes - 2nd message
    # 40 bytes  - 3rd message
    $sms->emsAddUserPromptIndicator(3);

The Simplewire network supports concatenated EMS messages  the ability
for the SMS handset to automatically combine several Short Messages. This feature is
extremely useful because of the restrictions on the amount of information that
an SMS can carry - in GSM the amount of information that can be carried within
an SMS is only 140 bytes.

The handset is therefore able to both send and receive longer, richer messages.
The Standard allows up to 255 messages to be concatenated into one, however,
current phones support anywhere between 3 and 10 segments, and each
handset should be investigated for its level of support.

=head2 User Prompt Indicator

This feature introduced in 3GPP TS 23.040 Release 4 allows handsets to stitch
pictures and user-defined sounds. It also allows the user to be prompted upon
reception of the message to execute media specific actions (storage, handset
personalisation, etc.).


=head1 UNICODE

For Unicode characters in the range 0x00 to 0xFF, you can use the
Perl hexadecimal escape sequence.

Format: \x##

Backslash + Lowercase 'x' + Two Hex Digits

Example: $r->msgText( "Uppercase Z: \x5A" );


For Unicode characters in the range 0x0000 to 0xFFFF, Simplewire provides
its own escape sequence. This is only for use with the msgFrom and msgText
methods.

Format: \\X####

Backslash + Backslash + Uppercase 'X' + Four Hex Digits

Example: $r->msgText( "Smiley Face: \\X263A" );


Note: Both sequences can be used in the same string.
	Example: $r->msgText( "Degree Sign: \xB0   \n   Tilde: \\X007E" );


=head1 SEE ALSO

/Net-SMS-X.XX/examples/

/Net-SMS-X.XX/docs/sw-doc-manual-perl-2.4.0.pdf

Visit http://www.simplewire.com/

=head1 AUTHOR

Simplewire E<lt>support@simplewire.comE<gt>
www.simplewire.com

=head1 COPYRIGHT

Please refer to License.txt within the Net-SMS-X.XX.tar.gz file
for licensing information.

