/*
* SMSServer.java
* Copyright � 1998-2011 Research In Motion Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.server.smsdemo;
import java.net.*;
import java.util.*;
import java.io.*;
/**
* A simple SMS loopback server that will return to the source any
* messages sent to it. For port bound messages, the source and destination
* port must be the same. On the device, listen on the same port used for
* sending a message and this server will send the received message back
* on that port.
*/
public final class SMSServer implements Runnable
{
// Constants ---------------------------------------------------------------
private static final String RESOURCES = "com/rim/samples/server/smsdemo/resources";
private static final String INTRODUCTION = "SMS Server";
private static final int SMSPORT = 0x5345;
private static final int MAX_SMSPACKET_SIZE = 424;
private static final int SIZE_OF_ADDRESS = 44;
private static final int ADDRESS_SEGMENT_LENGTH = 60;
private static final int PAYLOAD_INDEX = 335;
// Resource strings
private static final String USAGE = "Usage";
private static final String RECEIVED = "Received";
private static final String SOURCE = "Source";
private static final String DATA = "Data";
private static final String DEST = "Dest";
private static final String SENDING = "Sending";
private static final String RAW = "Raw";
// Members
private volatile boolean _stop = false;
private DatagramSocket _socket;
private String[] _charmap = {"A", "B", "C", "D", "E", "F"};
// Statics -----------------------------------------------------------------
private static ResourceBundle _resources = ResourceBundle.getBundle(RESOURCES);
/**
* Entry point
* @param Command line args (not used)
*/
public static void main(String[] args)
{
new SMSServer();
}
// Constructor
SMSServer()
{
run();
}
/**
* This inner class represents an SMS address
*/
private static final class SMSAddress
{
// Constants -----------------------------------------------------------
private static final int INDEX_LENGTH = 4;
private static final int INDEX_START_OF_ADDRESS = 8;
// Members -------------------------------------------------------------
private byte[] _data;
private int _length;
public SMSAddress(byte[] data)
{
this(data, 0, data.length);
}
public SMSAddress(byte[] data, int start, int length)
{
_data = new byte[length];
for (int i = 0; i < length; ++i)
{
_data[i] = data[start + i];
}
_length = data[start + INDEX_LENGTH];
}
public String getAddress()
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < _length; ++i)
{
sb.append(Byte.toString(_data[INDEX_START_OF_ADDRESS + i]));
}
return sb.toString();
}
public byte[] getAddressBytes()
{
byte[] address = new byte[_length];
for (int i = 0; i < _length; ++i)
{
address[i] = _data[INDEX_START_OF_ADDRESS + i];
}
return address;
}
public void setAddress(byte[] address)
{
_data[INDEX_LENGTH] = (byte)address.length;
for (int i = 0; i < address.length; ++i)
{
_data[INDEX_START_OF_ADDRESS + i] = address[i];
}
}
public byte[] getBytes()
{
return _data;
}
}
/**
* Runs a thread that listens for incoming messages
*/
public void run()
{
System.out.println(INTRODUCTION);
System.out.println(_resources.getString(USAGE));
Thread t = new Thread() {
public void run()
{
try
{
_socket = new DatagramSocket(SMSPORT );
System.out.println("Listening on port:"+SMSPORT);
byte[] data = new byte[MAX_SMSPACKET_SIZE];
while (!_stop)
{
DatagramPacket p = new DatagramPacket(data, data.length);
_socket.receive(p);
receivedSms(p);
returnSms(p);
Arrays.fill(data, (byte)0);
}
}
catch (IOException e)
{
System.err.println(e);
return;
}
}
};
t.start();
try
{
while ( 'x' != System.in.read() )
{}
}
catch (IOException e)
{
System.err.println(e);
}
_stop = true;
_socket.close();
try
{
t.join();
}
catch (InterruptedException e)
{
System.err.println(e);
}
}
/**
* Some simple parsing on the received datagram
* @param p A received Datagram containing an SMS message
*/
private void receivedSms(DatagramPacket p)
{
byte[] data = p.getData();
// Extract the source address
SMSAddress src = new SMSAddress(data, 0, SIZE_OF_ADDRESS);
String srcaddress = src.getAddress();
// Extract the destination address
SMSAddress dest = new SMSAddress(data, ADDRESS_SEGMENT_LENGTH, SIZE_OF_ADDRESS );
String destaddress = dest.getAddress();
StringBuffer sb = new StringBuffer();
sb.append(_resources.getString(RECEIVED) + "\n");
sb.append(_resources.getString(SOURCE) + srcaddress + "\n");
sb.append(_resources.getString(DEST) + destaddress + "\n");
sb.append(_resources.getString(DATA) + new String(data, PAYLOAD_INDEX, data.length - PAYLOAD_INDEX));
System.out.println(sb.toString());
System.out.println("\n");
System.out.println(_resources.getString(RAW));
System.out.println("\n");
printAsHex(data);
System.out.println("\n");
}
/**
* Utility method to print data to the console
* @param data The data to print
*/
private void printAsHex(byte[] data)
{
for (int i = 1; i < data.length + 1; ++i)
{
byte b = data[i-1];
String octet = Integer.toString((int)b, 16);
octet = getChar((b>>4)&0x0f);
octet += getChar(b&0xf);
System.out.print(octet + " ");
if ( i % 8 == 0 )
{
// Loop over the last 8 chars and print out as ascii
System.out.print("\t");
for (int j = i-7; j <= i; ++j)
{
char c = (char)data[j-1];
System.out.print(c > 0x0032 && c < 0x00FF ? c : '.');
}
System.out.println();
}
}
}
/**
*Returns a string representation of a hex value
*/
private String getChar(int nibble)
{
if ( nibble < 10 )
{
return Integer.toString(nibble);
}
else if( nibble < 16 )
{
return _charmap[nibble - 10];
}
throw new IllegalArgumentException("bad char: " + nibble);
}
/**
* Send the message back to the source
* @param p A received Datagram containing an SMS message
*/
private void returnSms(DatagramPacket p) throws IOException
{
System.out.println(_resources.getString(SENDING));
byte[] data = p.getData();
SMSAddress src = new SMSAddress(data, 0, SIZE_OF_ADDRESS);
SMSAddress dest = new SMSAddress(data, ADDRESS_SEGMENT_LENGTH, SIZE_OF_ADDRESS);
// Swap the addresses
byte[] srcBytes = src.getAddressBytes();
src.setAddress(dest.getAddressBytes());
dest.setAddress(srcBytes);
// Now swap them in the main data array
byte[] srcbytes = src.getBytes();
for (int i = 0; i < srcbytes.length; ++i)
{
data[i] = srcbytes[i];
}
byte[] destbytes = dest.getBytes();
for (int i = 0; i < destbytes.length; ++i)
{
data[i + SIZE_OF_ADDRESS] = destbytes[i];
}
DatagramPacket returnpacket = new DatagramPacket(data, p.getLength());
returnpacket.setAddress(p.getAddress());
returnpacket.setPort(p.getPort());
//Dump to hex just for a check
System.out.println("\n");
printAsHex(data);
_socket.send(returnpacket);
}
}