package SMS::Send::TW::chtsms; use strict; use Carp; use Socket; use Switch; use Text::Iconv; use base 'SMS::Send::Driver'; use vars qw{$VERSION}; BEGIN { $VERSION = '0.02'; } use constant { SENDBUF_SIZE => 217, RECVBUF_SIZE => 189, TYPE_SERV_CHECK => 0, TYPE_SERV_EDIT_PASSWD => 1, TYPE_SERV_SEND => 2, TYPE_SERV_QUERY => 3, TYPE_SERV_GET => 4, TYPE_SERV_SEND_WITH_UDHI => 6, TRAN_SEND_NOW => 100, TRAN_SEND_ORDER => 101, # Uncodumented coding CODING_ASCII => 0x00, CODING_BIG5 => 0x01, CODING_BINARY => 0xf5, CODING_UNICODE => 0x08, SMS_SERVER_IP => '203.66.172.133', SMS_SERVER_PORT => '8001', }; # Preloaded methods go here. sub new { my ($class, %params) = @_; foreach(qw/username password/) { Carp::croak("No $_ specified") unless(defined $params{"_$_"}); } $params{'host_ip'} = $params{'_host_ip'} || SMS_SERVER_IP; $params{'host_port'} = $params{'_host_port'} || SMS_SERVER_PORT; my $proto = getprotobyname('tcp'); socket(CONN, PF_INET, SOCK_STREAM, $proto); my $conn = \*CONN; my $srv_ipaddr = inet_aton($params{'host_ip'}); my $srv_addr = sockaddr_in($params{'host_port'}, $srv_ipaddr); unless (connect($conn, $srv_addr)) { Carp::croak('Not able to connect to CHT SMS Server'); } $params{'conn'} = $conn; $params{'auth'} = 0; my $self = bless { %params }, $class; return $self; } sub _send_packet { my $self = shift; my %params = @_; my $send_buf = ''; my $tmp; my $zero_buf = "\0"x230; my $conn = $self->{'conn'}; foreach (qw/type/) { Carp::croak("No $_ specified") unless(defined $params{"$_"}); } foreach (qw/coding length tran_type/) { $params{"_$_"} = 0 unless (defined $params{"$_"}); } foreach (qw/pchID pchPasswd pchMsisdn pchMessageID pchMessage pchSendTime/) { $params{"_$_"} = '' unless (defined $params{"$_"}); } $send_buf = pack("CCCCZ9Z9Z13Z9a160Z13", $params{'type'}, $params{'coding'}, $params{'length'}, $params{'tran_type'}, $params{'pchID'}, $params{'pchPasswd'}, $params{'pchMsisdn'}, $params{'pchMessageID'}, $params{'pchMessage'}, $params{'pchSendTime'} ); # Fill 0... $send_buf = pack("a" . SENDBUF_SIZE, $send_buf); # Send to server send($conn, $send_buf, 0); } sub _recv_packet { my $self = shift; my $recv_buf = ''; my $tmp; my %ret; my $conn = $self->{'conn'}; recv($conn, $recv_buf, RECVBUF_SIZE, 0); # unpack ($ret{'code'}, $ret{'coding'}, $ret{'length'}, $ret{'send_msisdn'}, $ret{'recv_msisdn'}, $ret{'buffer'}) = unpack("cccZ13Z13a160", $recv_buf); # Convert Message to UTF-8 my $from_code; switch($ret{'coding'}) { case CODING_ASCII { $ret{'msg'} = $ret{'buffer'}; } case CODING_BIG5 { $from_code = 'big5'; } case CODING_UNICODE { $from_code = 'ucs-2'; } } if (defined($from_code)) { my $converter = Text::Iconv->new($from_code, "utf-8"); $ret{'msg'} = $converter->convert($ret{'buffer'}); } return %ret; } sub send_sms { my $self = shift; my %params = @_; my %ret; # Get the message and destination my $message = $self->_MESSAGE( $params{text} ); my $recipient = $self->_TO( delete $params{to} ); my $conn = $self->{'conn'}; # Login if (not $self->{'auth'}) { $self->_send_packet( 'type' => TYPE_SERV_CHECK, 'pchID' => $self->{'_username'}, 'pchPasswd' => $self->{'_password'} ); %ret = $self->_recv_packet(); Carp::croak($ret{'buffer'}) unless(0 == $ret{'code'}); $self->{'auth'} = 1; } $self->_send_packet( 'type' => TYPE_SERV_SEND, 'coding' => CODING_UNICODE, 'pchMsisdn' => $recipient, 'pchMessage' => $message, 'length' => length($message), 'tran_type' => TRAN_SEND_NOW ); %ret = $self->_recv_packet(); return %ret; } sub _MESSAGE { my $class = ref $_[0] ? ref shift : shift; my $message = shift; my $converter = Text::Iconv->new("utf-8", "ucs-2"); unless ( length($message) <= 160 ) { Carp::croak("Message length limit is 160 characters"); } return $converter->convert($message); } sub _TO { my $class = ref $_[0] ? ref shift : shift; my $to = shift; # International numbers need their + removed $to =~ y/0123456789//cd; return $to; } 1;