Source code: com/synaptics/elvis/ElvisMessage.java
1 /*
2 * The contents of this file are subject to the Mozilla Public License Version
3 * 1.1 (the "License"); you may not use this file except in compliance with the
4 * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
5 *
6 * Software distributed under the License is distributed on an "AS IS" basis,
7 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
8 * the specific language governing rights and limitations under the License.
9 *
10 * The Original Code is com.synaptics.elvis code.
11 *
12 * The Initial Developers of the Original Code are Synaptics, Inc. and Christopher Heiny.
13 * Portions created by Synaptics, Inc. and Christopher Heiny are
14 * Copyright (C) 2002 Synaptics, Inc. and Christopher Heiny. All Rights Reserved.
15 *
16 * Contributor(s):
17 * Christopher Heiny <cheiny@synaptics.com>
18 */
19
20 package com.synaptics.elvis;
21
22 import java.beans.*;
23
24 /** ElvisMessage is used internally by Elvis to both build and parse the messages
25 * exchanged between the One True Elvis and various impersonators.
26 * <P>
27 * Each message has five fields:
28 * <UL>
29 * <LI>the Elvis identified field. This field is always the string <CODE>"Elvis"</code>
30 * <LI>the Elvis program name. This is the program name associated with this application.
31 * We check this to ensure that Elvis impersonators are talking to the One True Elvis they
32 * are expecting to talk to.
33 * <LI>the key. This is a random integer that helps to uniquely identify a particular instance of
34 * the One True Elvis. It is used mainly for security purposes - an attacker must not only know the
35 * Elvis program name he wishes to exploit, but he must be able to guess the key the program is currently
36 * using. While by no means bulletproof, this makes it much harder for an attacker to either submit
37 * fraudulent requests to the One True Elvis, or to spoof responses to the Elvis impersonators.
38 * <LI>the keyword. This indicates what the message is, and what semantics should be applied to it.
39 * <LI>the data. This is a string, which may contain status information, or may contain information of
40 * interest to the client. It may be <CODE>null</code> or empty.
41 * </ol>
42 * For transmission to the One True Elvis, the fields are assembled into a single string, with the fields
43 * delimited by colon (<CODE>':'</code>) characters. This means it is a bad idea for any of the fields (except
44 * the <CODE>data</code> field) to contain a <CODE>':'</code>.
45 * <P>
46 * Additionally, the transmitted message is read as a single string. This means that it is a bad idea for
47 * any of the fields to contain an end-of-line sequence of any sort.
48 *
49 * @author cheiny
50 * @version $Id: ElvisMessage.java,v 1.1 2002/05/09 07:17:17 clheiny Exp $
51 */
52 class ElvisMessage extends Object implements java.io.Serializable {
53
54 private PropertyChangeSupport propertySupport;
55
56 /** Holds value of property program. */
57 private String program;
58
59 /** Holds value of property key. */
60 private String key;
61
62 /** Holds value of property keyword. */
63 private String keyword;
64
65 /** Holds value of property data. */
66 private String data;
67
68 public static final char SEPARATOR = ':';
69
70 public static final String HEADER = SEPARATOR + "Elvis" + SEPARATOR;
71
72 /** Creates new ElvisMessage */
73 private ElvisMessage() {
74 propertySupport = new PropertyChangeSupport ( this );
75 }
76
77 /** Create a new ElvisMessage for the given program, key, keyword and data
78 * @throws IllegalArgumentException if any of the above are null or zero length (except data).
79 */
80 ElvisMessage ( String program, String key, String keyword, String data ) {
81 if ( (program == null) || (program.length() == 0) ) throw new IllegalArgumentException ( "Invalid program provided" );
82 if ( (key == null) || (key.length() == 0) ) throw new IllegalArgumentException ( "Invalid key provided" );
83 if ( (keyword == null) || (keyword.length() == 0) ) throw new IllegalArgumentException ( "Invalid keyword provided" );
84 if ( data == null ) data = "";
85 propertySupport = new PropertyChangeSupport ( this );
86 this.program = program;
87 this.key = key;
88 this.keyword = keyword;
89 this.data = data;
90 }
91
92
93 public void addPropertyChangeListener (PropertyChangeListener listener) {
94 propertySupport.addPropertyChangeListener (listener);
95 }
96
97 public void removePropertyChangeListener (PropertyChangeListener listener) {
98 propertySupport.removePropertyChangeListener (listener);
99 }
100
101 /** Get the program name associated with this message.
102 * @return A String: the program name.
103 */
104 public String getProgram() {
105 return program;
106 }
107
108 /** Set the program name. Not available to anyone else.
109 * @param program New program name.
110 */
111 private void setProgram(String program) {
112 this.program = program;
113 }
114
115 /** Get the key associated with this message.
116 * @return A String.
117 */
118 public String getKey() {
119 return key;
120 }
121
122 /** Set the message's key. Not available to anyone else.
123 * @param key A String, the new key.
124 */
125 private void setKey(String key) {
126 this.key = key;
127 }
128
129 /** Get the keyword associated with this message.
130 * @return A String - the keyword.
131 */
132 public String getKeyword() {
133 return keyword;
134 }
135
136 /** Set the message's keyword. Not available to anyone else.
137 * @param keyword A String - the new keyword.
138 */
139 private void setKeyword(String keyword) {
140 this.keyword = keyword;
141 }
142
143 /** Get the data field associated with this message.
144 * @return The data field. May be null or empty.
145 */
146 public String getData() {
147 return data;
148 }
149
150 /** Set the message's data. Not available to anyone else.
151 * @param data The new data value. If <CODE>null</code>, the empty string
152 * will be substituted.
153 */
154 private void setData(String data) {
155 if ( data == null ) data = "";
156 this.data = data;
157 }
158
159 /** This is a factory method for creating an ElvisMessage from a given
160 * input string (hopefully, one received from an Elvis or an Elvis client.
161 * @throws ElvisException if the message is not parseable.
162 */
163 public static ElvisMessage parse(java.lang.String input) {
164 ElvisMessage msg = new ElvisMessage();
165 String token;
166 int index;
167 int field;
168
169 if ( input.length() < 10 ) {
170 // we expect at least 5 fields, each at least one character long
171 throw new ElvisException ( "Message is too short" );
172 }
173
174 // first char should be delimiter
175 index = input.indexOf ( SEPARATOR );
176 if ( index != 0 ) {
177 throw new ElvisException ( "Malformed message - first character is not '" + SEPARATOR + "'" );
178 }
179
180 // first token should be Elvis
181 if ( !input.startsWith(HEADER) ) {
182 throw new ElvisException ( "Incorrect header on message" );
183 }
184
185 // next token is program name
186 int startIndex = HEADER.length();
187 index = input.indexOf ( SEPARATOR, HEADER.length() );
188 if ( index < 0 ) {
189 // no separator
190 throw new ElvisException ( "Malformed message - no program name" );
191 }
192 token = input.substring( startIndex, index );
193 if ( token.length() == 0 ) {
194 // empty not allowed
195 throw new ElvisException ( "Malformed message - empty program name" );
196 }
197 msg.setProgram ( token );
198
199 // next token is key
200 startIndex = index+1;
201 index = input.indexOf ( SEPARATOR, startIndex );
202 if ( index < 0 ) {
203 // no separator
204 throw new ElvisException ( "Malformed message - no key" );
205 }
206 token = input.substring( startIndex, index );
207 if ( token.length() == 0 ) {
208 // empty not allowed
209 throw new ElvisException ( "Malformed message - empty key" );
210 }
211 msg.setKey ( token );
212
213 // next token is keyword
214 startIndex = index+1;
215 index = input.indexOf ( SEPARATOR, startIndex );
216 if ( index < 0 ) {
217 // no separator
218 throw new ElvisException ( "Malformed message - no keyword" );
219 }
220 token = input.substring( startIndex, index );
221 if ( token.length() == 0 ) {
222 // empty not allowed
223 throw new ElvisException ( "Malformed message - empty keyword" );
224 }
225 if ( ! (ElvisRequest.valid(token) || ElvisResponse.valid(token)) ) {
226 // we don't recognize what they're asking us to do
227 throw new ElvisException ( "Invalid keyword " + token );
228 }
229 msg.setKeyword ( token );
230
231 // remainder of string is data, allowed to be empty
232 if ( index == input.length()-1 ) msg.setData ( "" );
233 else msg.setData ( input.substring (index+1) );
234
235 return msg;
236 }
237
238 /** Return the String representation of this message. This will be suitable for sending
239 * to Elvis, or for an Elvis to return to an ElvisClient.
240 * @returns the string as described
241 */
242 public String toString() {
243 return HEADER + program + SEPARATOR + key + SEPARATOR + keyword + SEPARATOR + data;
244 }
245
246 }