001 package com.croftsoft.core.net.news;
002
003 import java.io.*;
004 import java.util.*;
005
006 import com.croftsoft.core.lang.Pair;
007
008 /*********************************************************************
009 * USENET message (RFC 1036).
010 *
011 * @see
012 * <a target="blank"
013 * href="http://www.w3.org/Protocols/rfc1036/rfc1036.html">
014 * RFC 1036: Standard for Interchange of USENET Messages</a>
015 *
016 * @author
017 * <a href="https://www.croftsoft.com/">David Wallace Croft</A>
018 * @version
019 * 2001-08-18
020 * @since
021 * 2001-07-27
022 *********************************************************************/
023
024 public final class UsenetMessage
025 implements Serializable
026 //////////////////////////////////////////////////////////////////////
027 //////////////////////////////////////////////////////////////////////
028 {
029
030 private static final long serialVersionUID = 1L;
031
032 // required headers
033
034 public static final String HEADER_FROM = "From";
035
036 public static final String HEADER_DATE = "Date";
037
038 public static final String HEADER_NEWSGROUPS = "Newsgroups";
039
040 public static final String HEADER_SUBJECT = "Subject";
041
042 public static final String HEADER_MESSAGE_ID = "Message-ID";
043
044 public static final String HEADER_PATH = "Path";
045
046 // optional headers
047
048 public static final String HEADER_FOLLOWUP_TO = "Followup-To";
049
050 public static final String HEADER_EXPIRES = "Expires";
051
052 public static final String HEADER_REPLY_TO = "Reply-To";
053
054 public static final String HEADER_SENDER = "Sender";
055
056 public static final String HEADER_REFERENCES = "References";
057
058 public static final String HEADER_CONTROL = "Control";
059
060 public static final String HEADER_DISTRIBUTION = "Distribution";
061
062 public static final String HEADER_KEYWORDS = "Keywords";
063
064 public static final String HEADER_SUMMARY = "Summary";
065
066 public static final String HEADER_APPROVED = "Approved";
067
068 public static final String HEADER_LINES = "Lines";
069
070 public static final String HEADER_XREF = "Xref";
071
072 public static final String HEADER_ORGANIZATION = "Organization";
073
074 //
075
076 public static final String [ ] HEADERS = {
077 // required
078 HEADER_FROM,
079 HEADER_DATE,
080 HEADER_NEWSGROUPS,
081 HEADER_SUBJECT,
082 HEADER_MESSAGE_ID,
083 HEADER_PATH,
084 // optional
085 HEADER_FOLLOWUP_TO,
086 HEADER_EXPIRES,
087 HEADER_REPLY_TO,
088 HEADER_SENDER,
089 HEADER_REFERENCES,
090 HEADER_CONTROL,
091 HEADER_DISTRIBUTION,
092 HEADER_KEYWORDS,
093 HEADER_SUMMARY,
094 HEADER_APPROVED,
095 HEADER_LINES,
096 HEADER_XREF,
097 HEADER_ORGANIZATION };
098
099 //
100
101 private final Map headerNameToValueMap;
102
103 private String body;
104
105 //////////////////////////////////////////////////////////////////////
106 //////////////////////////////////////////////////////////////////////
107
108 public static UsenetMessage parse ( BufferedReader bufferedReader )
109 throws IOException
110 //////////////////////////////////////////////////////////////////////
111 {
112 UsenetMessage usenetMessage = new UsenetMessage ( );
113
114 Pair lastPair = null;
115
116 String line;
117
118 while ( true )
119 {
120 line = bufferedReader.readLine ( );
121
122 System.out.println ( line );
123
124 if ( ".".equals ( line )
125 || ( line == null )
126 || "".equals ( line ) )
127 {
128 break;
129 }
130
131 Pair headerPair = parseHeaderLine ( line );
132
133 if ( headerPair != null )
134 {
135 lastPair = headerPair;
136
137 usenetMessage.setHeader ( headerPair.name, headerPair.value );
138 }
139 else
140 {
141 if ( lastPair != null
142 && ( line.startsWith ( "\t" )
143 || line.startsWith ( " " ) ) )
144 {
145 usenetMessage.setHeader ( lastPair.name,
146 lastPair.value + line.substring ( 1 ) );
147 }
148 else
149 {
150 throw new IllegalArgumentException ( line );
151 }
152 }
153 }
154
155 if ( "".equals ( line ) )
156 {
157 String body = parseBody ( bufferedReader );
158
159 usenetMessage.setBody ( body );
160 }
161
162 return usenetMessage;
163 }
164
165 public static String parseBody ( BufferedReader bufferedReader )
166 throws IOException
167 //////////////////////////////////////////////////////////////////////
168 {
169 StringBuffer stringBuffer = new StringBuffer ( );
170
171 while ( true )
172 {
173 String line = bufferedReader.readLine ( );
174
175 System.out.println ( line );
176
177 if ( ".".equals ( line )
178 || line == null )
179 {
180 break;
181 }
182 // changing the end-of-line here, will mess up message digests
183
184 stringBuffer.append ( NntpLib.decodeLine ( line ) + "\n" );
185 }
186
187 return stringBuffer.toString ( );
188 }
189
190 public static Pair parseHeaderLine ( String headerLine )
191 //////////////////////////////////////////////////////////////////////
192 {
193 int index = headerLine.indexOf ( ':' );
194
195 if ( index < 1 )
196 {
197 return null;
198 }
199
200 String name = headerLine.substring ( 0, index ).trim ( );
201
202 if ( "".equals ( name ) )
203 {
204 return null;
205 }
206
207 String value = headerLine.substring ( index + 1 ).trim ( );
208
209 return new Pair ( name, value );
210 }
211
212 //////////////////////////////////////////////////////////////////////
213 //////////////////////////////////////////////////////////////////////
214
215 public UsenetMessage ( )
216 //////////////////////////////////////////////////////////////////////
217 {
218 headerNameToValueMap = new HashMap ( );
219 }
220
221 public UsenetMessage (
222 String from,
223 String newsgroup,
224 String subject,
225 String messageBody )
226 //////////////////////////////////////////////////////////////////////
227 {
228 this ( );
229
230 setHeader ( HEADER_FROM , from );
231
232 setHeader ( HEADER_NEWSGROUPS, newsgroup );
233
234 setHeader ( HEADER_SUBJECT , subject );
235
236 // setHeader ( HEADER_PATH , "" );
237
238 setBody ( messageBody );
239 }
240
241 //////////////////////////////////////////////////////////////////////
242 //////////////////////////////////////////////////////////////////////
243
244 public String getHeader ( String name )
245 //////////////////////////////////////////////////////////////////////
246 {
247 return ( String ) headerNameToValueMap.get ( name );
248 }
249
250 public String getBody ( ) { return body; }
251
252 //////////////////////////////////////////////////////////////////////
253 //////////////////////////////////////////////////////////////////////
254
255 public void setHeader (
256 String name,
257 String value )
258 //////////////////////////////////////////////////////////////////////
259 {
260 headerNameToValueMap.put ( name, value );
261 }
262
263 public void setBody ( String body )
264 //////////////////////////////////////////////////////////////////////
265 {
266 this.body = body;
267 }
268
269 public String toString ( )
270 //////////////////////////////////////////////////////////////////////
271 {
272 StringBuffer stringBuffer = new StringBuffer ( );
273
274 // First append the standard headers in normal order.
275
276 Map temporaryMap = new HashMap ( headerNameToValueMap );
277
278 for ( int i = 0; i < HEADERS.length; i++ )
279 {
280 String name = HEADERS [ i ];
281
282 if ( temporaryMap.containsKey ( name ) )
283 {
284 String value = ( String ) temporaryMap.remove ( name );
285
286 stringBuffer.append (
287 name + ": " + ( value == null ? "" : value ) + "\r\n" );
288 }
289 }
290
291 // Then append any non-standard headers.
292
293 Iterator iterator = temporaryMap.keySet ( ).iterator ( );
294
295 while ( iterator.hasNext ( ) )
296 {
297 String name = ( String ) iterator.next ( );
298
299 String value = ( String ) temporaryMap.get ( name );
300
301 stringBuffer.append (
302 name + ": " + ( value == null ? "" : value ) + "\r\n" );
303 }
304
305 stringBuffer.append ( "\r\n" );
306
307 if ( body == null )
308 {
309 body = "";
310 }
311
312 stringBuffer.append ( NntpLib.encode ( body ) );
313
314 return stringBuffer.toString ( );
315 }
316
317 //////////////////////////////////////////////////////////////////////
318 //////////////////////////////////////////////////////////////////////
319 }