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         }