--- /dev/null
+Makefile
+Makefile.old
+blib
+pm_to_blib
SMS/Config.pm
SMS/Log.pm
SMS/NBS.pm
-SMS/NBS/Frame.pm
SMS/NBS/Message.pm
SMS/NBS/Stack.pm
+SMS/NBS/Lib.pm
+SMS/NBS/Alcatel.pm
SMS/PDU.pm
+SMS/Bitmap.pm
SMS/Spool.pm
SMS/Transport.pm
SMS/Transport/Transport.pm
SMS/Transport/NovelSoft.pm
SMS/Transport/Serial.pm
SMS/Transport/XmlRpc.pm
+SMS/Transport/Print.pm
SMS/OTA/OTA.pm
SMS/OTA/Bitmap.pm
SMS/OTA/CLIicon.pm
examples/slashdot/README
examples/slashdot/slashdot
examples/slashdot/transport.conf
+examples/commandline/README
+examples/commandline/gsmcmd
+examples/commandline/transport.conf
codes.txt
rtttlsyntax.txt
Look in the examples directory, this should get you started.
The most complex one is the smartmessagingserver, allowing you to request a
-rttl or groupgraphic by sending an sms message. The slashdot example allows
+rtttl or groupgraphic by sending an sms message. The slashdot example allows
you to receive the latest slashdot headlines in a SMS message. The ideas are
ofcourse endless. I've build an application on top of these modules that
expose the send functions in an XMLRPC way, thus creating a SMS webservice.
--- /dev/null
+package GSM::SMS::Bitmap;
+# Generic package for monochrome bitmaps
+
+my $VERSION='0.1';
+
+use strict;
+use warnings;
+
+require Exporter;
+our @ISA=qw(Exporter);
+our @EXPORT=qw();
+
+use Image::Magick;
+use Carp;
+
+
+sub new {
+ my ($class, $arg) = @_;
+ my $self={};
+
+ bless($self,$class);
+
+ my $img;
+ if ("Image::Magick" ne ref $arg) {
+ my $err;
+ $img=Image::Magick->new() or carp "Image::Magick->new(): $!" and return undef;
+ $err=$img->Read($arg) and carp "Image::Magick->Read(\"$arg\"): $err" and return undef;
+ }
+ else {
+ $img=$arg;
+ }
+
+ $img->Set("magick"=>"mono");
+ $self->{"blob" }=$img->ImageToBlob();
+ $self->{"width" }=$img->Get("columns");
+ $self->{"height"}=$img->Get("height" );
+ return $self;
+}
+
+sub crop {
+ my ($self, $width, $height) = @_;
+
+ if ($self->{"width"} > $width) {
+ carp "Cutting image width to $width pixels (from ".$self->{"width"}.")";
+ $self->{"width"}=$width;
+ }
+ if ($self->{"height"} > $height) {
+ carp "Cutting image height to $height pixels (from ".$self->{"height"}.")";
+ $self->{"height"}=$height;
+ }
+}
+
+sub pixref {
+ my ($self, $x, $y) = @_;
+ my $zero = 0;
+
+ return \$zero if 0 # out of bouds
+ || $x<0 || $x>=$self->{"width" }
+ || $y<0 || $y>=$self->{"height"}
+ ;
+ return \vec($self->{"blob"},int(($self->{"width"}+7)/8)*$y + $x,1);
+}
+
+sub pixget {
+ my ($self, @rest) = @_;
+
+ return(${$self->pixref(@rest)});
+}
+
+# Convert the bit stream to the list of byte values
+# Hmm, it is a back ineffective to unpack() it back but who cares
+# Two versions exist - FIXME: how to merge them?
+
+sub pixlist_horiz {
+ my ($self) = @_;
+
+ my $bits="";
+ for my $y (0..$self->{"height"}) {
+ for my $x (0..int(($self->{"width"}+7)&~0x7)-1) {
+ $bits.=$self->pixget($x,$y);
+ }
+ }
+ return unpack("C*",pack("B*",$bits));
+}
+
+sub pixlist_vert {
+ my ($self) = @_;
+
+ my $bits="";
+ for my $x (0..$self->{"width"}-1) {
+ for my $y (0..int(($self->{"height"}+7)&~0x7)-1) {
+ $bits.=$self->pixget($x,$y);
+ }
+ }
+ return unpack("C*",pack("B*",$bits));
+}
+
+1;
use GSM::SMS::OTA::Config;
use GSM::SMS::Transport;
use MIME::Base64;
+use Carp;
#
}
#
+# Send message (new style)
+#
+sub sendsms {
+ my ($self, $msisdn, $message, %args ) = @_;
+ my $ret = 0;
+
+ my $transport = $self->{'__TRANSPORT__'};
+
+ my $nbs_message = GSM::SMS::NBS::Message->new();
+ my $err = $nbs_message->store($msisdn, $message, %args);
+ carp "Error during SMS conversion: $err" if $err;
+ foreach my $frame ( @{$nbs_message->get_frames()} ) {
+ # transport->send returns -1 on failure.
+ $ret = -1 if $transport->send($msisdn, $frame);
+ }
+ return $ret;
+}
+
+#
# Send message
#
sub sendto {
--- /dev/null
+package GSM::SMS::NBS::Alcatel;
+
+use strict;
+use warnings;
+
+require Exporter;
+our @ISA=qw(Exporter);
+our @EXPORT=qw();
+
+use GSM::SMS::NBS::Lib;
+use Carp;
+
+use vars qw($VERSION);
+$VERSION = '0.1';
+
+use constant IEI_ALCATEL_PICTURE => 0b100;
+use constant IEI_ALCATEL_ANIMATION => 0b101;
+
+
+# returns ($msg, $udh=[...])
+sub alcatel_picture {
+ my ($class, $bitmap) = @_;
+
+ $bitmap->crop(0xFF,0xFF);
+ my @msglist=($bitmap->{"width"}, $bitmap->{"height"}, $bitmap->pixlist_vert());
+ return (pack("C*", @msglist), [
+ {
+ "type"=>"alcatel",
+ "ems_compat"=>1
+ && !($bitmap->{"width"}&0x7) # width%8 == 0
+ && ($bitmap->{"width"}/8 * $bitmap->{"height"} <= 128), # width/8*height <= 128
+ "alcatel_type"=>IEI_ALCATEL_PICTURE,
+ "length"=>scalar(@msglist),
+ }]);
+}
+
+# expects list of GSM::SMS::Bitmap instances mixed with numbers (display times)
+# returns ($msg, $udh=[...])
+sub alcatel_animation {
+ my ($class, @list) = @_;
+
+ my $sized;
+ my @words=();
+ my %imgs=();
+ my $imgmsg=""; # concatenated image bodies (produced by alcatel_picture())
+
+ my $header_len = 2*(1+@list); # "2*"=words, "1+"=sequence_size, "@list"=items
+ for my $item (@list) {
+ if ("GSM::SMS::Bitmap" eq ref $item) {
+ if (!exists $imgs{$item}) {
+ $imgs{$item}=$header_len+length($imgmsg);
+ my ($msg)=$class->alcatel_picture($item);
+ $imgmsg.=$msg;
+ $sized = ((0 # condition tests whether we have _violated_ EMS compatibility
+ || $item->{"width"} != $item->{"height"}
+ || ($sized && $sized != $item->{"width"})
+ || ($item->{"width"} != 8 && $item->{"width"} != 16)
+ ) ? -1 : $item->{"width"});
+ }
+ push(@words,$imgs{$item});
+ } else { # "display time" here:
+ push(@words,min($item & 0x0FFF));
+ }
+ }
+ unshift(@words,scalar(@words));
+ my $r=pack("v*",@words).$imgmsg;
+ return ($r, [
+ {
+ "type"=>"alcatel",
+ "ems_compat"=>1
+ && 4 == keys %imgs # exactly 4 frames
+ && -1 != $sized, # frame sizes valid
+ "alcatel_type"=>IEI_ALCATEL_ANIMATION,
+ "length"=>length($r),
+ }]);
+}
+
+1;
+++ /dev/null
-package GSM::SMS::NBS::Frame;
-
-$VERSION = '0.1';
-
-sub new {
- my $proto = shift;
- my $class = ref($proto) || $proto;
-
- my $self = {};
- bless($self, $class);
- return $self;
-}
-
-
-sub destination_port {
- my ($self, $dest)=@_;
-
- $self->{destination} = $dest;
-}
-
-
-sub source_port {
- my ($self, $source)=@_;
-
- $self->{source} = $source;
-}
-
-sub datagram_reference_number {
- my ($self, $drn)=@_;
-
- $self->{drn} = $drn;
-}
-
-
-sub fragment_maximum {
- my ($self, $fmax)=@_;
-
- $self->{fmax} = $fmax;
-}
-
-sub fragment_sequence_number {
- my ($self, $fsn) = @_;
-
- $self->{fsn} = $fsn;
-}
-
-
-sub asString {
- my ($self) = @_;
-
- my $len;
- my $out='';
- my @NBS_HEADER;
-
- $NBS_HEADER[0] = 11; # header length, without this byte
- $NBS_HEADER[1] = 5; # Port address information element, 16bit
- $NBS_HEADER[2] = 4; # Length of the info element
- $NBS_HEADER[3] = ($self->{destination} & 0xff00) >> 8; # high byte destination
- $NBS_HEADER[4] = $self->{destination} & 0xff; # low byte destination
- $NBS_HEADER[5] = ($self->{source} & 0xff00) >> 8; # high byte source
- $NBS_HEADER[6] = $self->{source} & 0xff; # low byte source
- $NBS_HEADER[7] = 0; # Fragmentation information element
- $NBS_HEADER[8] = 3; # Length of Info el
- $NBS_HEADER[9] = $self->{drn}; # fragment id
- $NBS_HEADER[10] = $self->{fmax}; # max amount of frags
- $NBS_HEADER[11] = $self->{fsn}; # sequence number fragment
-
- $len=12;
- if ($self->{fmax} == 1) {
- $len=7;
- $NBS_HEADER[0] = 6;
- }
-
- for (my $j=0; $j<$len; $j++) {
- $i=$NBS_HEADER[$j];
- $out.=sprintf("%02x", $i);
- }
-
- return $out;
-}
-
-1;
-
-=head1 NAME
-
-GSM::SMS::NBS::Frame - Encapsulates frames for NBS messages.
-
-=head1 DESCRIPTION
-
-Create a frame for a NBS message.
-
-=head1 AUTHOR
-
-Johan Van den Brande <johan@vandenbrande.com>
--- /dev/null
+package GSM::SMS::NBS::Lib;
+
+use strict;
+use warnings;
+
+require Exporter;
+our @ISA=qw(Exporter);
+our @EXPORT=qw(&dcs_to_bits &empty_subload &encode_payload &nail_payload_len &min);
+
+use Carp;
+
+use vars qw($VERSION);
+$VERSION = '0.1';
+
+
+sub dcs_to_bits {
+ my ($dcs) = @_;
+
+ return ($dcs & 0xF4)==0xF4 ? 8 : 7;
+}
+
+sub empty_subload {
+ my ($dcs, $freebytes) = @_;
+
+ return $freebytes if 8==dcs_to_bits($dcs);
+ # assumed 7==dcs_to_bits($dcs)
+ return int($freebytes*8/7); # floor-rounding is the proper one
+}
+
+sub encode_payload {
+ my ($dcs, $payload) = @_;
+
+ return $payload if 8==dcs_to_bits($dcs);
+ # assumed 7==dcs_to_bits($dcs)
+
+ my $bits="";
+ $bits.=unpack("b7",$&) while ($payload=~s/^.//);
+ $bits.="0"x7; # trailing padding
+ my $r="";
+ $r.=pack("b8",$&) while ($bits=~s/^.{8}//);
+
+ return $r;
+}
+
+# returns length(encode_payload($dcs, "T"x$payload)) but it is more effective
+sub nail_payload_len {
+ my ($dcs, $payload_len) = @_;
+
+ return $payload_len if 8==dcs_to_bits($dcs);
+ # assumed 7==dcs_to_bits($dcs)
+ return int(($payload_len*7+7)/8);
+}
+
+# Very common function but where is standardized?
+sub min {
+ my $r = shift;
+
+ for (@_) {
+ $r = $_ if $_ < $r;
+ }
+ return $r;
+}
+
+1;
package GSM::SMS::NBS::Message;
+
+use strict;
+use warnings;
+
+use GSM::SMS::NBS::Lib;
use GSM::SMS::PDU;
-use GSM::SMS::NBS::Frame;
+use Carp;
+use vars qw($VERSION);
$VERSION = '0.1';
-use constant DATAGRAM_LENGTH => 128;
+use constant DEFAULT_DCS_UDH => 0xF5; # 8bit, ME specific
+use constant DEFAULT_DCS_NOUDH => 0x00; # default DCS (typically 7bit, SIM specific)
+use constant DEFAULT_VALIDITY => "1d"; # FIXME: currently fixed to 1 day
+use constant DEFAULT_SMS_CENTER => ""; # FIXME: currently fixed to empty SMS center address
+
+use constant USERDATA_LENGTH => 140;
+use constant DECONCAT_TOTAL => "_deconcat_total"; # internal: undef=>try single message, *=>try & messages
+use constant TEXT_POSITION => "text_position"; # numeric position in $payload, UDH IE will be present only once
+
+use constant IEI_ANIMATION_LARGE => 0x0E; #IEI: EMS large animation (16x16 x4 = 128 bytes)
+use constant IEI_ANIMATION_SMALL => 0x0F; #IEI: EMS small animation ( 8x 8 x4 = 32 bytes)
+use constant IEI_PICTURE_LARGE => 0x10; #IEI: EMS large picture (32x32 = 128 bytes)
+use constant IEI_PICTURE_SMALL => 0x11; #IEI: EMS small picture (16x16 = 32 bytes)
+use constant IEI_PICTURE_VARIABLE => 0x12; #IEI: EMS variable picture (other)
+use constant IEI_ALCATEL => 0x80; #IEI: Alcatel proprietary "data download" type
# SAR for NBS messages
# --------------------
return $self;
}
-# Create a message from a payload
+# Create a message from a payload -- compatibility cludge!
sub create {
my ($self, $number, $payload, $destination_port, $source_port, $datacodingscheme) = @_;
+ my %dcs_compat = (
+ "7bit" =>0x00, # default DCS (typically 7bit, SIM specific)
+ "7biti"=>0xF0, # 7bit, immediate display
+ "8bit" =>0xF6, # 8bit, SIM specific
+ "8biti"=>0xF4, # 8bit, immediate display
+ "8bitm"=>0xF5, # 8bit, ME specific
+ );
- # Reset the FRAME array
- $#{$self->{'__FRAMES__'}} = -1;
+ $datacodingscheme=$dcs_compat{$datacodingscheme} if $datacodingscheme && exists $dcs_compat{$datacodingscheme};
+
+ return $self->store($number, pack("H*", $payload),
+ "dcs"=>$datacodingscheme,
+ "udh"=>[
+ {
+ "type" =>"port16", #IEI: application port addressing scheme, 16 bit address
+ "destination_port"=>$destination_port,
+ "source_port" =>$source_port,
+ },
+ ],
+ );
+}
+
+sub _iebuilder {
+ my ($self, $iei, @iedata) = @_;
+ return ($iei,scalar(@iedata),@iedata);
+}
- my $nbs = GSM::SMS::NBS::Frame->new();
- my $pdu = GSM::SMS::PDU->new();
+sub _ie_port16 {
+ my ($self, %args) = @_;
- my $datagram_length = DATAGRAM_LENGTH;
+ return () if !defined $args{"destination_port"};
+ my $source_port=$args{"source_port"} || $args{"destination_port"};
- $source_port = $destination_port if (!defined($source_port));
- my $dcs = '7bit';
- $dcs = '8bitm' if (defined($destination_port));
+ return $self->_iebuilder(0x05, #IEI: application port addressing scheme, 16 bit address
+ ($args{"destination_port"} >> 8), ($args{"destination_port"} & 0xFF),
+ ($source_port >> 8), ($source_port & 0xFF),
+ );
+}
+
+sub _ie_concat8 {
+ my ($self, %args) = @_;
+
+ return $self->_iebuilder(0x00, #IEI: concatenated short message, 8 bit reference
+ $args{"drn"}, # reference number
+ $args{"fmax"}, # maximum number of short messages
+ $args{"fsn"}, # sequence number of the current short message
+ );
+}
+
+sub _ie_ems_picture {
+ my ($self, %args) = @_;
+ my $bitmap = $args{"bitmap"}; # GSM::SMS::Bitmap instance
- # print "DATACODINGSCHEME: $datacodingscheme\n";
-
- $dcs = $datacodingscheme if (defined($datacodingscheme));
+ $bitmap->crop(0xFF*8,0xFF);
+ my ($width, $height) = ($bitmap->{"width"}, $bitmap->{"height"});
- my $udhi = -1;
- $udhi = 0 if (!defined($destination_port));
+ my $iei;
+ if ($width == 16 && $height == 16) { $iei = IEI_PICTURE_SMALL ; }
+ elsif ($width == 32 && $height == 32) { $iei = IEI_PICTURE_LARGE ; }
+ else { $iei = IEI_PICTURE_VARIABLE; }
+ return $self->_iebuilder($iei,
+ $args{TEXT_POSITION}, # position in the SMS
+ ($iei != IEI_PICTURE_VARIABLE ? () : (int(($width+7)/8), $height)), # image size (optional)
+ $bitmap->pixlist_horiz()
+ );
+}
- my $payload_len = length($payload)/2;
- my $frags;
+sub _ie_ems_animation {
+ my ($self, %args) = @_;
+ my $bitmaps = $args{"bitmaps"}; # list of GSM::SMS::Bitmap instances
- # test if the payload can fit in one message (6= length of minimal header)
- if (($payload_len + 6) <= 140) {
- # Ok we can have 1 message
- $frags=1;
- $datagram_length = $payload_len;
- } else {
- $frags = int($payload_len/$datagram_length) + 1;
+ if (4 != @$bitmaps) {
+ carp "Invalid length of EMS animation (4 required)"; return ();
+ }
+ my $bitmap = $$bitmaps[0];
+ my ($width, $height) = ($bitmap->{"width"}, $bitmap->{"height"});
+ for my $testbitmap (@$bitmaps) {
+ if ($width != $testbitmap->{"width"} || $height != $testbitmap->{"height"}) {
+ carp "Non-matching sizes in EMS animation";
+ }
}
- $nbs->destination_port($destination_port);
- $nbs->source_port($source_port);
-
- # If no destination port defined, then also no source port can be defined
- # There can be a problem ! what if 2 nbs's send a nbs to the same phone with 2 the same datagrams?
- # We have to solve this!
- $nbs->datagram_reference_number(int(rand(255)));
- $nbs->fragment_maximum($frags);
-
- my $ok = 0;
- for (my $i=1; $i<=$frags; $i++) {
- my $subload = substr($payload, ($i-1)*$datagram_length*2, $datagram_length*2);
- $nbs->fragment_sequence_number($i);
- my $msg;
- if ($destination_port) {
- $msg = uc $nbs->asString().$subload;
+ my $iei;
+ if ($width == 8 && $height == 8) { $iei = IEI_ANIMATION_SMALL ; }
+ elsif ($width == 16 && $height == 16) { $iei = IEI_ANIMATION_LARGE ; }
+ else {
+ $iei = IEI_ANIMATION_LARGE;
+ carp "Invalid frame size of EMS animation (8x8 or 16x16 required)";
+ }
+
+ return $self->_iebuilder($iei,
+ $args{TEXT_POSITION}, # position in the SMS
+ &{sub {
+ my @r=();
+ for $bitmap (@$bitmaps) {
+ push @r,$bitmap->pixlist_horiz();
+ }
+ return @r;
+ }}
+ );
+}
+
+sub _ie_alcatel {
+ my ($self, %args) = @_;
+
+ return $self->_iebuilder(IEI_ALCATEL,
+ (!defined($args{"name"}) ? (0x00) : # bit 7=0 (use GSM charset)
+ (length($args{"name"}), unpack("C*",
+ encode_payload(0xF5, GSM::SMS::PDU->inversetranslate($args{"name"})))) # 0xF5 DCS=any 8bit
+ ),
+ (!!$args{"ems_compat"} << 7) # bit 7=EMS compatibility
+ |($args{"alcatel_type"} & 0x07), # bits 0..2=Alcatel message type
+ ($args{"length"} >> 8), ($args{"length"} & 0xFF),
+ );
+}
+
+sub _udh_build {
+ my ($self, @srcs) = @_;
+
+ my @r=();
+ no strict 'refs';
+ my @list=map { &{"_ie_".$$_{"type"}}($self,%$_); } @srcs;
+ use strict 'refs';
+ return "" if !@list;
+ return pack "C*",scalar(@list),@list;
+}
+
+# Create a message from a payload
+# returns either error message or list (DECONCAT_TOTAL,total messages)
+sub _store_try {
+ my ($self, $number, $payload, %args) = @_;
+
+ # Reset the FRAME array
+ $self->{'__FRAMES__'} = [];
+
+
+ my $PDU = GSM::SMS::PDU->new();
+
+ my @udhs=($args{"udh"} ? @{$args{"udh"}} : ());
+
+ # We put ME-specific default if ANY element of UDH was specified
+ # Autodetect $dcs BEFORE possible concatenation UDH IEs get inserted!
+ my $dcs=$args{"dcs"} || (@udhs ? DEFAULT_DCS_UDH : DEFAULT_DCS_NOUDH);
+ # print "DCS: $dcs\n";
+
+ my $deconcat_ieref; # defined only if deconcatenating
+ if (defined $args{DECONCAT_TOTAL}) {
+ my %deconcat_ie=(
+ "type"=>"concat8",
+ "drn"=>int(rand(0xFF)), # reference number
+ "fmax"=>$args{DECONCAT_TOTAL}, # maximum number of short messages
+ "fsn"=>0x00, # sequence number of the current short message
+ );
+ $deconcat_ieref=\%deconcat_ie;
+ push @udhs,$deconcat_ieref;
+ return "Too long message (".$deconcat_ie{"fmax"}." elements)" if (0xFF < $deconcat_ie{"fmax"});
+ }
+
+ my @udhstatic=();
+ my @udhpositioned=();
+ for my $udh (@udhs) {
+ if (exists $udh->{TEXT_POSITION}) {
+ push(@udhpositioned, $udh);
} else {
- $msg = $subload;
+ push(@udhstatic, $udh);
}
- # print "DCS-> $dcs\n";
- my $p = $pdu->SMSSubmit('', $number, $msg, $dcs, '1d', $udhi);
- # print "--> $p\n";
+ }
+ @udhpositioned = sort { $a->{TEXT_POSITION} <=> $b->{TEXT_POSITION}; } @udhpositioned;
+
+ my $position=0; # absolute current position in the whole long SMS
+ while (length $payload || @udhpositioned) {
+ my @udhpositioned_now=(); # items from @udhpositioned placed in the current SMS part
+ my $payload_now=""; # text from $payload placed in the current SMS part
+ while (1) {
+ if (@udhpositioned && $udhpositioned[0]{TEXT_POSITION} <= $position) { # "<=" should be "=="
+ last if USERDATA_LENGTH < length($self->_udh_build(@udhstatic,@udhpositioned_now,$udhpositioned[0]))
+ +nail_payload_len($dcs,length($payload_now));
+
+ my %udhei=%{shift @udhpositioned}; # copy it to local state - we will be modifying it
+ # we subtract the absolute position base of current SMS part:
+ $udhei{TEXT_POSITION} -= $position-length($payload_now);
+
+ push @udhpositioned_now,\%udhei;
+ next;
+ }
+ my $minlen=length $payload;
+ $minlen=min($minlen,$udhpositioned[0]{TEXT_POSITION}-$position) if @udhpositioned;
+ $minlen=min($minlen,empty_subload($dcs,
+ USERDATA_LENGTH-length($self->_udh_build(@udhstatic,@udhpositioned_now))-length($payload_now)))
+ if defined $args{DECONCAT_TOTAL};
+ last if $minlen<=0; # "<=" should be "=="
+ $payload_now.=substr($payload,0,$minlen);
+ $payload=substr($payload,$minlen);
+ $position+=$minlen;
+ }
+
+ # We will ship out the current SMS part here
+ ++$deconcat_ieref->{"fsn"} if $deconcat_ieref;
+ # We exceeded userdata size, this is probably the first undeconcatenaed try from store()!
+ my $userdata=$self->_udh_build(@udhstatic,@udhpositioned_now).encode_payload($dcs,$payload_now);
+
+ return (DECONCAT_TOTAL, undef) if USERDATA_LENGTH < length($userdata);
+
+ my $pdu = $PDU->SMSSubmit(DEFAULT_SMS_CENTER, $number, $userdata,
+ $dcs, DEFAULT_VALIDITY, !!@udhstatic || !!@udhpositioned_now
+ );
# Push on to frame array
- push(@{$self->{'__FRAMES__'}}, $p );
-
- }
- return 0 if ($ok == $frags);
- return -1;
+ # print "--> $pdu\n";
+ push(@{$self->{"__FRAMES__"}}, $pdu);
+ }
+
+ carp "Last fsn==".$deconcat_ieref->{"fsn"}." but fmax==".$deconcat_ieref->{"fmax"}
+ if $args{DECONCAT_TOTAL} && $deconcat_ieref->{"fsn"}!=$deconcat_ieref->{"fmax"};
+ return (undef,(!$deconcat_ieref ? 1 : $deconcat_ieref->{"fsn"})); # success, return number of _PRODUCED_ MSes
+}
+
+sub store {
+ my ($self, $number, $payload, %args) = @_;
+
+ my ($err, $total);
+ ($err, $total) = $self->_store_try($number, $payload, %args, DECONCAT_TOTAL=>undef ); return $err if !$err || $err ne DECONCAT_TOTAL;
+ ($err, $total) = $self->_store_try($number, $payload, %args, DECONCAT_TOTAL=>0 ); return $err if $err;
+ ($err, $total) = $self->_store_try($number, $payload, %args, DECONCAT_TOTAL=>$total); return $err;
}
# Return the frames
###########################################################################
###########################################################################
- # Data coding scheme (probably need to experiment withthis one!)
- # -------------------------------------------------------------------------
- # See SMSDeliver for a description
- # We use '00' for 7bit, SIM specific '7bit' (default)
- # 'F0' for 7bit, immediate display '7biti'
- # 'F6' for 8bit, SIM specific '8bit'
- # 'F4' for 8bit, immediate display '8biti'
- # 'F5' for 8bit, ME specific '8bitm'
+ # Data coding scheme
# -------------------------------------------------------------------------
- $pdu.=$self->encodeDataCodingScheme($dcs);
+ $pdu.=sprintf "%.2X",$dcs;
###########################################################################
###########################################################################
###########################################################################
# Length of message (Length of user data)
# -------------------------------------------------------------------------
- # $pdu.=sprintf("%.2X", length($data));
+ $pdu.=sprintf("%.2X", length($data));
###########################################################################
###########################################################################
# Message of user data.
# -------------------------------------------------------------------------
- if (($dcs eq '8bit') || ($dcs eq '8biti' || ($dcs eq '8bitm'))) {
- $pdu.=sprintf("%.2X", length($data)/2);
- $pdu.=$self->encode_8bit(substr($data,0,160*2));
- } else {
- # First to the alphabet translation on the data...
- $pdu.=sprintf("%.2X", length($data));
- $data = $self->inversetranslate($data);
- $pdu.=$self->encode_7bit(substr($data,0,160));
- }
+ $pdu.=uc unpack "H*",$data;
###########################################################################
return $pdu;
return sprintf("%.2X", $vp);
}
-sub encodeDataCodingScheme {
- my ($self, $dcs) = @_;
- my $c = '00'; # default '7bit'
- DCS: {
- $dcs eq '7bit' && do { $c = '00'; last; };
- $dcs eq '7biti' && do { $c = 'F0'; last; };
- $dcs eq '8bit' && do { $c = 'F6'; last; };
- $dcs eq '8biti' && do { $c = 'F4'; last; };
- $dcs eq '8bitm' && do { $c = 'F5'; last; };
- };
- return $c;
-}
-
sub encodeDestinationAddress {
my ($self, $number) = @_;
my $pdu;
sub inversetranslate {
my ($self, $msg) = @_;
- # $msg=~ tr (\@\$) (\x00\x02);
- # $msg=~ tr (iaaeeEOoUu) (\x07\x0f\x7f\x04\x05\x1f\x5c\x7c\x5e\x7e);
+ $msg=~ tr (\@\$) (\x00\x02);
+ $msg=~ tr (iaaeeEOoUu) (\x07\x0f\x7f\x04\x05\x1f\x5c\x7c\x5e\x7e);
return $msg;
}
1;
--- /dev/null
+package GSM::SMS::Transport::Print;
+# Printing for debugging and contect conversions
+
+$VERSION = '0.1';
+
+use strict;
+use warnings;
+
+use GSM::SMS::Transport::Transport;
+use vars qw(@ISA);
+@ISA = qw(GSM::SMS::Transport::Transport);
+
+
+# All the parameters I need to run
+my @config_vars = qw(
+ name
+ userdata
+ nomsisdn
+ binary
+);
+
+# Send a (PDU encoded) message
+sub send {
+ my ($self, $msisdn, $pdu) = @_;
+
+ print "$msisdn:" if !$self->{"cfg"}{"nomsisdn"};
+ print "$pdu\n",return 0 if !$self->{"cfg"}{"userdata"};
+
+ $pdu=pack "H*",$pdu; #unhex
+ $pdu=substr($pdu,0
+ +1+ord(substr($pdu,0,1)) # skip SMS center address octets (incl. type) length + SMS center data
+ );
+ my $validity_type=(ord(substr($pdu,0,1)) >> 3) & 0x03; # extract validity period from PDU type
+ $pdu=substr($pdu,0+1+1); # skip PDU type + message reference
+ $pdu=substr($pdu,0
+ +1+1+int(1+ord(substr($pdu,0,1))/2) # skip msisdn nibbles (w/o type) length + msisdn type + msisdn data
+ +1+1 # skip PID + DCS
+ );
+
+ if ($validity_type == 2) { $pdu=substr($pdu,1); } # validity == relative (1 octet)
+ elsif ($validity_type == 3) { $pdu=substr($pdu,7); } # validity == absolute (7 octets)
+ # otherwise validity either "none" (0 octets) or invalid
+ $pdu=substr($pdu,0+1); # skip body length
+
+ if ($self->{"cfg"}{"binary"}) {
+ print $pdu;
+ } else {
+ print uc(unpack("H*",$pdu))."\n";
+ }
+ return 0;
+};
+
+# Receive a PDU encoded message
+# $ is a ref to a PDU string
+# return
+# 0 if PDU received
+# -1 if no message pending
+sub receive {
+ my ($self, $pduref) = @_;
+
+ return -1;
+};
+
+
+# Close
+sub close {
+ my ($self) = @_;
+}
+
+# A ping command .. just return an informative string on success
+sub ping {
+ my ($self) = @_;
+
+ return "Pong.. Print transport ok";
+}
+
+
+# give out the needed config paramters
+sub get_config_parameters {
+ my ($self) = @_;
+
+ return @config_vars;
+}
+
+# Do we have a valid route for this msisdn
+sub has_valid_route {
+ my ($self, $msisdn) = @_;
+
+ # print "route";
+ return -1;
+}
+
+#####################################################################
+# transport specific
+#####################################################################
+
+1;
package GSM::SMS::Transport::XmlRpc;
#
-# HTTP for Remote Serial modem
+# XmlRpc Transport for remote node functionality
#
use GSM::SMS::Transport::Transport;
--- /dev/null
+ SMS content convertor
+ ~~~~~~~~~~~~~~~~~~~~~
+WHAT
+
+You have some images (gif/png) and/or ringing tones (rtttl) and you need to
+send them via some already existing SMS sending software/gateway. So you
+just need to convert them to the raw PDU (or just its UserData) format.
--- /dev/null
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+
+#
+# Load some modules
+use GSM::SMS::Config;
+use GSM::SMS::NBS;
+use GSM::SMS::Bitmap;
+use GSM::SMS::NBS::Message;
+use Getopt::Long;
+require GSM::SMS::NBS::Alcatel;
+
+$|=1; # No output buffering
+
+
+#
+# Arguments
+my $opt_verbose;
+my $opt_transport;
+my $opt_ring_send;
+my $opt_logo_group_send;
+my $opt_ems_picture_send;
+my $opt_ems_animation_send;
+my $opt_alcatel_name;
+my $opt_alcatel_picture_send;
+my $opt_alcatel_animation_send;
+GetOptions(
+ "v|verbose" => \$opt_verbose,
+ "transport:s" => \$opt_transport,
+ "ring-send:s" => \$opt_ring_send,
+ "logo-group-send:s" => \$opt_logo_group_send,
+ "ems-picture-send:s" => \$opt_ems_picture_send,
+ "ems-animation-send:s" => \$opt_ems_animation_send,
+ "alcatel-name:s" => \$opt_alcatel_name,
+ "alcatel-picture-send:s" => \$opt_alcatel_picture_send,
+ "alcatel-animation-send:s" => \$opt_alcatel_animation_send,
+);
+
+unless ( @ARGV ) {
+ print <<EOT;
+Usage: $0 [--verbose] [--transport=<transport.conf>]
+ [--ring-send=<rtttl file>]
+ [--logo-group-send=<image file>]
+ [--ems-picture-send=<image file>]
+ [--ems-animation-send=<image file>]
+ [--alcatel-name=<resource name>]
+ [--alcatel-picture-send=<image file>]
+ [--alcatel-animation-send=<image file>]
+ <phone numbers>...
+EOT
+ exit(1);
+}
+
+#
+# Initialize
+my $nbs = GSM::SMS::NBS->new( $opt_transport ? $opt_transport : "/dev/null" );
+
+my $msisdn;
+my $message;
+my $timestamp;
+my $transportname;
+my $port;
+
+undef $/; # Read files at once, NEVER set it before as at least NBS->new() needs line-reading!
+
+for $msisdn (@ARGV) {
+
+ if ($opt_ring_send) {
+ local (*F);
+ open F, $opt_ring_send;
+ my $rtttl_string = <F>;
+ close F;
+ $nbs->sendRTTTL($msisdn, $rtttl_string);
+ }
+
+ if ($opt_logo_group_send) {
+ $nbs->sendGroupGraphic_file($msisdn, $opt_logo_group_send);
+ }
+
+ if ($opt_ems_picture_send) {
+ my $bitmap = GSM::SMS::Bitmap->new($opt_ems_picture_send);
+ $nbs->sendsms($msisdn, "", udh=>[ { "type"=>"ems_picture", TEXT_POSITION=>0, "bitmap"=>$bitmap } ]);
+ }
+
+ if ($opt_ems_animation_send) {
+ my @names=split /:/,$opt_ems_animation_send;
+ die "Required exactly 4 colon (':') delimited filenames for --ems-animation-send" if 4 != @names;
+ my @bitmaps=map { GSM::SMS::Bitmap->new($_); } @names;
+ $nbs->sendsms($msisdn, "", udh=>[ { "type"=>"ems_animation", TEXT_POSITION=>0, "bitmaps"=>\@bitmaps } ]);
+ }
+
+ if ($opt_alcatel_picture_send) {
+ my $bitmap = GSM::SMS::Bitmap->new($opt_alcatel_picture_send);
+ my ($msg, $udh) = GSM::SMS::NBS::Alcatel->alcatel_picture($bitmap);
+ $udh->[0]{"name"}=$opt_alcatel_name if defined $opt_alcatel_name;
+ $nbs->sendsms($msisdn, $msg, udh=>$udh);
+ }
+
+ if ($opt_alcatel_animation_send) {
+ my @names=split /:/,$opt_alcatel_animation_send;
+ my %imgmap=();
+ my @list=map {
+ if (/^\d+$/) {
+ $_;
+ } else {
+ $imgmap{$_}=GSM::SMS::Bitmap->new($_) if (!exists $imgmap{$_});
+ $imgmap{$_};
+ }
+ } @names;
+ my ($msg, $udh) = GSM::SMS::NBS::Alcatel->alcatel_animation(@list);
+ $udh->[0]{"name"}=$opt_alcatel_name if defined $opt_alcatel_name;
+ $nbs->sendsms($msisdn, $msg, udh=>$udh);
+ }
+}
+
+exit(0);
--- /dev/null
+[Print]
+ type = Print
+ name = Print
+ nomsisdn = 1 # Prefixed by "tel.no:"
+ userdata = 1 # Strip PDU header, print only UserDataHeader (+UserData), UserData length NOT present
+# binary = 1 # Dump everything without hex encapsulation (multiple SMSes glued!)
use strict;
-use Test::More tests => 13;
+use Test::More tests => 16;
BEGIN {
use_ok( 'GSM::SMS::NBS');
use_ok( 'GSM::SMS::Log');
use_ok( 'GSM::SMS::Spool');
use_ok( 'GSM::SMS::Config');
- use_ok( 'GSM::SMS::NBS::Frame');
+ use_ok( 'GSM::SMS::Bitmap');
use_ok( 'GSM::SMS::NBS::Message');
use_ok( 'GSM::SMS::NBS::Stack');
+ use_ok( 'GSM::SMS::NBS::Alcatel');
+ use_ok( 'GSM::SMS::NBS::Lib');
use_ok( 'GSM::SMS::Transport::Transport');
use_ok( 'GSM::SMS::Transport::Serial');
use_ok( 'GSM::SMS::Transport::NovelSoft');
use_ok( 'GSM::SMS::Transport::MCube' );
+ use_ok( 'GSM::SMS::Transport::Print' );
}