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 }