Encapsulates rudimentary functionality of a PBX to list and invite users (peers) to secure calls. More...
Classes | |
interface | Context |
Provides a call-back context for the instance of PBXClient. More... | |
class | ControlMessage |
PBX signaling message sent via call-back to the upper layer. More... | |
Public Types | |
enum | CMType { _INVALID_, INVITE, RING, ACCEPT, BYE, INSTANTMESSAGE, LIST, ALIVE } |
PBX Signaling Messages' Types. More... | |
Public Member Functions | |
PBXClient (String host, int port, Context context) | |
Creates new instance of PBXClient that posts messages to specified Context . | |
String | getLocalAddress () |
Returns local IP address. | |
void | start () |
Starts the thread. | |
void | send (String message) |
Sends message (appended with new-line) to chat server. | |
void | send (String message, String userId) |
Sends message (appended with new-line) to chat server prefixed with userId. | |
void | close () |
Closes the connection gracefully. | |
void | sendInvite (String remoteUserId, String localIpAddress, int localUdpPort, String publicKey) |
Broadcasts INVITE message. | |
void | sendRing (String remoteUserId, String localIpAddress, int localUdpPort, String publicKey) |
Broadcasts RING message. | |
void | sendAccept (String remoteUserId, String localIpAddress, int localUdpPort, String publicKey) |
Broadcasts ACCEPT message. | |
void | sendBye (String remoteUserId, String localIpAddress, int localUdpPort) |
Broadcasts BYE message. | |
void | sendInstantMessage (String remoteUserId, String encryptedMessage) |
Broadcasts IMSG message. | |
void | sendListPeers (String regex) |
Broadcasts LIST message (to list potential peers) | |
void | run () |
Connects socket, then reads messages from server while running flag is enabled. | |
Private Member Functions | |
void | report (String style, String str) |
Reports a system message to log. | |
void | reportIncomingTextMessage (String userId, String message) |
Reports incoming message. | |
void | parseInputMessage (String message) |
Parses input message. | |
void | parseControlMessage (String remoteUserId, String[] args, String original) |
Parses control messages. | |
Private Attributes | |
String | host = null |
Host name or IP address of the remote chat server. | |
int | port = -1 |
TCP port where to connect to on remote chat server. | |
String | myID |
Chat client ID when presented to user (== host + ":" + port) | |
PrintWriter | out = null |
Output stream to remote server. | |
Socket | socket = null |
Instance of the TCP socket to chat server. | |
volatile boolean | running = false |
Indicates/enables the thread to be running. | |
Context | context = null |
Event (call-back) context for this instance of the PBXClient. | |
Static Private Attributes | |
static final String | WARN = "logWarn" |
HTML CSS class for warning and error messages. | |
static final String | INFO = "logInfo" |
HTML CSS class for info messages. |
Encapsulates rudimentary functionality of a PBX to list and invite users (peers) to secure calls.
The instances of PBXClient class expect to be connected to plain public chat server that distributes (broadcasts their messages (terminated by the new-line) to all other connected users (possible kryptofon peers).
Communication with the upper layer (which owns instance of the PBXClient) is done using call-backs over the PBXClient.Context interface.
Definition at line 31 of file PBXClient.java.
PBX Signaling Messages' Types.
Definition at line 36 of file PBXClient.java.
{ /** Invalid CMType */ _INVALID_, /** The message sent to remote peer to start the call */ INVITE, /** The message sent back from remote peer informing about remote alerting status */ RING, /** The message sent back from the remote peer indicating accepted call */ ACCEPT, /** Indicates call clear down -- either normal or abrupt (like call reject). */ BYE, /** Instant message exchanged between users with encrypted messages */ INSTANTMESSAGE, /** Query all peers */ LIST, /** Respond to Query all pears */ ALIVE }
pbx.PBXClient.PBXClient | ( | String | host, |
int | port, | ||
Context | context | ||
) |
Creates new instance of PBXClient
that posts messages to specified Context
.
host | host name or IP address of the chat server |
port | TCP port |
context | where to log messages (also error and info messages) |
Definition at line 206 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.host, pbx.PBXClient.myID, and pbx.PBXClient.port.
void pbx.PBXClient.close | ( | ) |
Closes the connection gracefully.
Definition at line 277 of file PBXClient.java.
References pbx.PBXClient.running, and pbx.PBXClient.socket.
Referenced by CryptoPhoneApp.executeCommand(), and CryptoPhoneApp.formWindowClosing().
String pbx.PBXClient.getLocalAddress | ( | ) |
Returns local IP address.
Definition at line 220 of file PBXClient.java.
References pbx.PBXClient.socket.
Referenced by CryptoPhoneApp.acceptIncomingCall(), CryptoPhoneApp.deferredOnInvite(), and CryptoPhoneApp.executeCommand().
{ return socket.getLocalAddress ().getHostAddress (); }
void pbx.PBXClient.parseControlMessage | ( | String | remoteUserId, |
String[] | args, | ||
String | original | ||
) | [private] |
Parses control messages.
TODO Format messages in XML instead. (It would be too much to do for IP1 course.)
Syntax:
[$] INVITE local-name remote-ip-address remote-udp-port [ public-key ] [$] RING local-name remote-ip-address remote-udp-port [ public-key ] [$] ACCEPT local-name remote-ip-address remote-udp-port [ secret-key ] [$] BYE local-name [ remote-ip-address [ remote-udp-port ] ] [$] IMSG local-name encrypted-message [$] LIST [ username-regex ] [$] ALIVE
Definition at line 377 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.Context.getUserId(), pbx.PBXClient.host, pbx.PBXClient.INFO, pbx.PBXClient.Context.onAccept(), pbx.PBXClient.Context.onBye(), pbx.PBXClient.Context.onInstantMessage(), pbx.PBXClient.Context.onInvite(), pbx.PBXClient.Context.onRing(), pbx.PBXClient.port, pbx.PBXClient.report(), and pbx.PBXClient.send().
Referenced by pbx.PBXClient.parseInputMessage().
{ assert args.length >= 1 && args[0].equals( "[$]" ); /* Parse args[1] as CMType */ CMType cmType = CMType._INVALID_; if ( args[1].equalsIgnoreCase( "invite" ) ) { cmType = CMType.INVITE; } else if ( args[1].equalsIgnoreCase( "ring" ) ) { cmType = CMType.RING; } else if ( args[1].equalsIgnoreCase( "accept" ) ) { cmType = CMType.ACCEPT; } else if ( args[1].equalsIgnoreCase( "bye" ) ) { cmType = CMType.BYE; } else if ( args[1].equalsIgnoreCase( "imsg" ) ) { cmType = CMType.INSTANTMESSAGE; } else if ( args[1].equalsIgnoreCase( "list" ) ) { cmType = CMType.LIST; } else if ( args[1].equalsIgnoreCase( "alive" ) ) { cmType = CMType.ALIVE; } else { return; // ignore unknown types } String destinationUserId = null; /* Parse destination user id, then ignore loop messages and * messages that are not explicitly for us. */ if ( cmType != CMType.LIST && cmType != CMType.ALIVE && args.length >= 3 ) { destinationUserId = args[2]; if ( remoteUserId.equalsIgnoreCase( destinationUserId ) ) { return; } else if ( ! destinationUserId.equalsIgnoreCase( context.getUserId () ) ) { return; } } ////////////////////////////////////////////////////////////////////////////////// /* [$] INVITE local-name remote-ip-address remote-udp-port [ public-key ] * 0 1 2 3 4 5 opt. */ if ( args.length >= 5 && args[1].equalsIgnoreCase( "invite" ) ) { String publicKey = args.length >= 6 ? args[5] : null; try { int port = Integer.parseInt( args[4] ); // remote port context.onInvite( new ControlMessage( CMType.INVITE, remoteUserId, destinationUserId, args[3], port, publicKey ) ); } catch( NumberFormatException e ) { /* ignore message */ } } ////////////////////////////////////////////////////////////////////////////////// /* [$] RING local-name remote-ip-address remote-udp-port [ public-key ] * 0 1 2 3 4 5 */ else if ( args.length >= 5 && args[1].equalsIgnoreCase( "ring" ) ) { String publicKey = args.length >= 6 ? args[5] : null; try { int port = Integer.parseInt( args[4] ); // remote port context.onRing( new ControlMessage( CMType.RING, remoteUserId, destinationUserId, args[3], port, publicKey ) ); } catch( NumberFormatException e ) { /* ignore message if port is not integer */ } } ////////////////////////////////////////////////////////////////////////////////// /* [$] ACCEPT local-name remote-ip-address remote-udp-port [ secret-key ] * 0 1 2 3 4 5 */ else if ( args.length >= 5 && args[1].equalsIgnoreCase( "accept" ) ) { String secretKey = args.length >= 6 ? args[5] : null; try { int port = Integer.parseInt( args[4] ); // remote port context.onAccept( new ControlMessage( CMType.ACCEPT, remoteUserId, destinationUserId, args[3], port, secretKey ) ); } catch( NumberFormatException e ) { /* ignore message if port is not integer */ } } ////////////////////////////////////////////////////////////////////////////////// /* [$] BYE local-name [ remote-ip-address [ remote-udp-port ] ] * 0 1 2 3 4 */ else if ( args.length >= 3 && args[1].equalsIgnoreCase( "bye" ) ) { try { String host = args.length >= 4 ? args[3] : ""; int port = args.length >= 5 ? Integer.parseInt( args[4] ) : 0; context.onBye( new ControlMessage( CMType.BYE, remoteUserId, destinationUserId, host, port, null ) ); } catch( NumberFormatException e ) { /* ignore message if port is not integer */ } } ////////////////////////////////////////////////////////////////////////////////// /* [$] IMSG local-name encrypted-message * 0 1 2 3 */ else if ( args.length >= 4 && args[1].equalsIgnoreCase( "imsg" ) ) { context.onInstantMessage( new ControlMessage( CMType.INSTANTMESSAGE, remoteUserId, destinationUserId, "", 0, args[3] ) ); } ////////////////////////////////////////////////////////////////////////////////// /* [$] LIST [ username-regex ] * 0 1 2 opt. */ else if ( args.length >= 2 && args[1].equalsIgnoreCase( "list" ) ) { String myUserId = context.getUserId (); report( INFO, "Listing users..." ); if ( ! myUserId.isEmpty () ) { if ( args.length < 3 ) // query all users (without regex) { /* Respond back to query */ send( "[$] ALIVE", myUserId ); } else // case-insensitive query with regex { Pattern p = null; try { p = Pattern.compile( args[2], Pattern.CASE_INSENSITIVE ); } catch ( Throwable e ) { /* ignored */ } if ( p != null && p.matcher( myUserId ).find () ) { /* Respond back to query */ send( "[$] ALIVE", myUserId ); } } } } ////////////////////////////////////////////////////////////////////////////////// /* [$] ALIVE * 0 1 */ else if ( args.length >= 2 && args[1].equalsIgnoreCase( "alive" ) ) { report( INFO, "-- User '" + remoteUserId + "' is alive." ); // TODO this might as well update some list of possible peers? // -- but that requires little more functionality on the PBX (chat) server // side. } }
void pbx.PBXClient.parseInputMessage | ( | String | message ) | [private] |
Parses input message.
Syntax:
[ [ <userId> ] : ] <text-or-control>
where default userId is [Anonymous]
.
If the text-or-control begins with "[$]" it represents control message and it will not be displayed to the user.
Definition at line 321 of file PBXClient.java.
References pbx.PBXClient.parseControlMessage(), and pbx.PBXClient.reportIncomingTextMessage().
Referenced by pbx.PBXClient.run().
{ /* Parse input with syntax: [ [ <userId> ] ":: " ] <message> * where default userId is [Anonymous]. */ String[] parts = message.split( ":: ", 2 ); String userId = "[Anonymous]"; if ( message.startsWith( "WWHHOO: " ) ) { userId = "WWHHOO"; message = message.substring( 8 ); } else if ( parts.length == 0 ) { message = parts[0]; } else if ( parts[0].trim().length () == 0 && parts.length >= 2 ) { message = parts[1]; } else if ( parts.length >= 2 ) { userId = parts[0].trim (); message = parts[1]; } else { message = parts[0]; } /* Now, check if we have a control message beginning with token [$] * but not comming from the anonymous user. */ parts = message.trim().split( "\\s{1,}" ); if ( ! userId.equals( "[Anonymous]" ) && parts.length >= 1 && parts[0].equals( "[$]" ) ) { parseControlMessage( userId, parts, message ); } else { reportIncomingTextMessage( userId, message ); } }
void pbx.PBXClient.report | ( | String | style, |
String | str | ||
) | [private] |
Reports a system message to log.
Definition at line 296 of file PBXClient.java.
References pbx.PBXClient.context, and pbx.PBXClient.Context.report().
Referenced by pbx.PBXClient.parseControlMessage(), and pbx.PBXClient.run().
void pbx.PBXClient.reportIncomingTextMessage | ( | String | userId, |
String | message | ||
) | [private] |
Reports incoming message.
Definition at line 304 of file PBXClient.java.
References pbx.PBXClient.context, and pbx.PBXClient.Context.reportIncomingTextMessage().
Referenced by pbx.PBXClient.parseInputMessage().
{ context.reportIncomingTextMessage( "chatMessage", userId, message ); }
void pbx.PBXClient.run | ( | ) |
Connects socket, then reads messages from server while running
flag is enabled.
Finally, closes connection in graceful manner.
Incoming messages are dispatched to the owner via the call-back context (see PBXClient.Context).
Definition at line 629 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.host, pbx.PBXClient.INFO, pbx.PBXClient.myID, pbx.PBXClient.out, pbx.PBXClient.parseInputMessage(), pbx.PBXClient.port, pbx.PBXClient.report(), pbx.PBXClient.running, pbx.PBXClient.Context.setPbxStatus(), pbx.PBXClient.socket, and pbx.PBXClient.WARN.
{ Log.trace( "Thread started" ); ////////////////////////////////////////////////////////////////////////////////// /* Open connection */ report( INFO, "Connecting to " + myID + "..." ); context.setPbxStatus( "Connecting to " + myID + "..." ); try { synchronized( this ) { socket = new Socket( host, port ); } } catch( UnknownHostException e ) { report( WARN, "'Unknown host' exception while creating socket" ); report( WARN, e.toString () ); running = false; } catch( IOException e ) { report( WARN, "I/O exception while connecting" ); report( WARN, e.toString () ); running = false; } ////////////////////////////////////////////////////////////////////////////////// /* Get input stream. Consider input characters UTF-8 encoded. * TODO: It would be nice to have this as a parameter. */ InputStreamReader reader = null; try { if ( socket != null ) { reader = new InputStreamReader( socket.getInputStream (), "utf-8" ); } } catch( IOException e ) { report( WARN, "I/O exception while getting input stream" ); report( WARN, e.toString () ); running = false; reader = null; } ////////////////////////////////////////////////////////////////////////////////// /* Get output stream. Encode our character strings as UTF-8. */ try { if ( socket != null ) { out = new PrintWriter( new OutputStreamWriter( socket.getOutputStream(), "utf-8" ), /*autoflush*/ true ); } } catch( IOException e ) { report( WARN, "I/O exception while getting output stream" ); report( WARN, e.toString () ); running = false; out = null; } ////////////////////////////////////////////////////////////////////////////////// /* Finally connected... (if running == true survived until here) */ BufferedReader in = null; if ( running ) { context.setPbxStatus( "Connected to " + myID ); report( "logOk", "Connected to " + myID + ". Ready to communicate..." ); in = new BufferedReader( reader ); } ////////////////////////////////////////////////////////////////////////////////// /* Read messages from the socket and dump them on log area */ while( running ) { try { parseInputMessage( in.readLine () ); } catch( IOException e ) { report( WARN, "Connection lost!" ); report( WARN, e.toString () ); running = false; } } report( INFO, "Closing connection " + myID + "..." ); ////////////////////////////////////////////////////////////////////////////////// /* Close connection gracefully */ try { if ( out != null ) { out.close (); } if ( in != null ) { in.close (); } synchronized( this ) { if ( socket != null && ! socket.isClosed () ) { socket.close (); } } } catch( IOException e ) { report( WARN, "I/O exception while closing connection" ); report( WARN, e.toString () ); } report( INFO, "... connection closed " + myID ); Log.trace( "Thread completed" ); }
void pbx.PBXClient.send | ( | String | message, |
String | userId | ||
) |
Sends message (appended with new-line) to chat server prefixed with userId.
message | message to be sent |
userId | user identifier |
Definition at line 263 of file PBXClient.java.
References pbx.PBXClient.send().
{ /* Discard spaces from the userId first */ userId = userId.trim().replaceAll( "\\s{1,}", "-" ); /* Send message with userId as a prefix */ send( userId + " :: " + message ); }
void pbx.PBXClient.send | ( | String | message ) |
Sends message (appended with new-line) to chat server.
message | message to be sent |
Definition at line 244 of file PBXClient.java.
References pbx.PBXClient.out.
Referenced by CryptoPhoneApp.executeCommand(), pbx.PBXClient.parseControlMessage(), CryptoPhoneApp.parseInputMessage(), pbx.PBXClient.send(), pbx.PBXClient.sendAccept(), pbx.PBXClient.sendBye(), pbx.PBXClient.sendInstantMessage(), CryptoPhoneApp.sendInstantMessage(), pbx.PBXClient.sendInvite(), pbx.PBXClient.sendListPeers(), and pbx.PBXClient.sendRing().
void pbx.PBXClient.sendAccept | ( | String | remoteUserId, |
String | localIpAddress, | ||
int | localUdpPort, | ||
String | publicKey | ||
) |
Broadcasts ACCEPT message.
Definition at line 583 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.Context.getUserId(), and pbx.PBXClient.send().
Referenced by CryptoPhoneApp.acceptIncomingCall().
void pbx.PBXClient.sendBye | ( | String | remoteUserId, |
String | localIpAddress, | ||
int | localUdpPort | ||
) |
Broadcasts BYE message.
Definition at line 595 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.Context.getUserId(), and pbx.PBXClient.send().
Referenced by CryptoPhoneApp.deferredOnInvite(), and CryptoPhoneApp.executeCommand().
void pbx.PBXClient.sendInstantMessage | ( | String | remoteUserId, |
String | encryptedMessage | ||
) |
Broadcasts IMSG message.
Definition at line 606 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.Context.getUserId(), and pbx.PBXClient.send().
Referenced by CryptoPhoneApp.sendInstantMessage().
void pbx.PBXClient.sendInvite | ( | String | remoteUserId, |
String | localIpAddress, | ||
int | localUdpPort, | ||
String | publicKey | ||
) |
Broadcasts INVITE message.
Definition at line 559 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.Context.getUserId(), and pbx.PBXClient.send().
Referenced by CryptoPhoneApp.executeCommand().
void pbx.PBXClient.sendListPeers | ( | String | regex ) |
Broadcasts LIST message (to list potential peers)
Definition at line 615 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.Context.getUserId(), and pbx.PBXClient.send().
Referenced by CryptoPhoneApp.executeCommand().
void pbx.PBXClient.sendRing | ( | String | remoteUserId, |
String | localIpAddress, | ||
int | localUdpPort, | ||
String | publicKey | ||
) |
Broadcasts RING message.
Definition at line 571 of file PBXClient.java.
References pbx.PBXClient.context, pbx.PBXClient.Context.getUserId(), and pbx.PBXClient.send().
Referenced by CryptoPhoneApp.deferredOnInvite().
void pbx.PBXClient.start | ( | ) |
Starts the thread.
Definition at line 229 of file PBXClient.java.
References pbx.PBXClient.running.
Referenced by CryptoPhoneApp.CryptoPhoneApp(), CryptoPhoneApp.executeCommand(), and CryptoPhoneApp.mainTimerEvent().
Context pbx.PBXClient.context = null [private] |
Event (call-back) context for this instance of the PBXClient.
Definition at line 194 of file PBXClient.java.
Referenced by pbx.PBXClient.parseControlMessage(), pbx.PBXClient.PBXClient(), pbx.PBXClient.report(), pbx.PBXClient.reportIncomingTextMessage(), pbx.PBXClient.run(), pbx.PBXClient.sendAccept(), pbx.PBXClient.sendBye(), pbx.PBXClient.sendInstantMessage(), pbx.PBXClient.sendInvite(), pbx.PBXClient.sendListPeers(), and pbx.PBXClient.sendRing().
String pbx.PBXClient.host = null [private] |
Host name or IP address of the remote chat server.
Definition at line 164 of file PBXClient.java.
Referenced by pbx.PBXClient.parseControlMessage(), pbx.PBXClient.PBXClient(), and pbx.PBXClient.run().
final String pbx.PBXClient.INFO = "logInfo" [static, private] |
HTML CSS class for info messages.
Definition at line 159 of file PBXClient.java.
Referenced by pbx.PBXClient.parseControlMessage(), and pbx.PBXClient.run().
String pbx.PBXClient.myID [private] |
Chat client ID when presented to user (== host + ":" + port)
Definition at line 174 of file PBXClient.java.
Referenced by pbx.PBXClient.PBXClient(), and pbx.PBXClient.run().
PrintWriter pbx.PBXClient.out = null [private] |
Output stream to remote server.
Definition at line 179 of file PBXClient.java.
Referenced by pbx.PBXClient.run(), and pbx.PBXClient.send().
int pbx.PBXClient.port = -1 [private] |
TCP port where to connect to on remote chat server.
Definition at line 169 of file PBXClient.java.
Referenced by pbx.PBXClient.parseControlMessage(), pbx.PBXClient.PBXClient(), and pbx.PBXClient.run().
volatile boolean pbx.PBXClient.running = false [private] |
Indicates/enables the thread to be running.
Definition at line 189 of file PBXClient.java.
Referenced by pbx.PBXClient.close(), pbx.PBXClient.run(), and pbx.PBXClient.start().
Socket pbx.PBXClient.socket = null [private] |
Instance of the TCP socket to chat server.
Definition at line 184 of file PBXClient.java.
Referenced by pbx.PBXClient.close(), pbx.PBXClient.getLocalAddress(), and pbx.PBXClient.run().
final String pbx.PBXClient.WARN = "logWarn" [static, private] |
HTML CSS class for warning and error messages.
Definition at line 154 of file PBXClient.java.
Referenced by pbx.PBXClient.run().