001 package com.croftsoft.apps.agoracast.c2p;
002
003 import java.awt.*;
004 import java.awt.event.*;
005 import java.io.*;
006 import java.util.*;
007 import javax.swing.*;
008
009 import com.croftsoft.core.gui.ButtonPanel2;
010 import com.croftsoft.core.gui.LabeledFieldsPanel2;
011 import com.croftsoft.core.lang.NullArgumentException;
012 import com.croftsoft.core.lang.Pair;
013 import com.croftsoft.core.lang.StringLib;
014 import com.croftsoft.core.net.news.Newsgroup;
015 import com.croftsoft.core.net.news.NntpConstants;
016 import com.croftsoft.core.net.news.NntpSocket;
017 import com.croftsoft.core.net.news.UsenetMessage;
018 import com.croftsoft.core.text.DateFormatLib;
019 import com.croftsoft.core.util.log.Log;
020
021 /*********************************************************************
022 *
023 * <p />
024 *
025 * @version
026 * 2001-09-12
027 * @since
028 * 2001-07-31
029 * @author
030 * <a href="http://croftsoft.com/">David Wallace Croft</a>
031 *********************************************************************/
032
033 public final class AgoracastDownloadPanel
034 extends JPanel
035 implements ActionListener, Log, Runnable
036 //////////////////////////////////////////////////////////////////////
037 //////////////////////////////////////////////////////////////////////
038 {
039
040 private final AgoracastMediator agoracastMediator;
041
042 private final AgoracastBrowsePanel agoracastBrowsePanel;
043
044 private final JTextArea jTextArea;
045
046 private final JButton cancelButton;
047
048 //
049
050 private NntpSocket nntpSocket;
051
052 //////////////////////////////////////////////////////////////////////
053 //////////////////////////////////////////////////////////////////////
054
055 public AgoracastDownloadPanel (
056 AgoracastMediator agoracastMediator,
057 AgoracastBrowsePanel agoracastBrowsePanel )
058 //////////////////////////////////////////////////////////////////////
059 {
060 super ( new BorderLayout ( ), true ); // isDoubleBuffered
061
062 NullArgumentException.check (
063 this.agoracastMediator = agoracastMediator );
064
065 NullArgumentException.check (
066 this.agoracastBrowsePanel = agoracastBrowsePanel );
067
068 AgoracastLib.setColor ( this, agoracastMediator );
069
070 add ( new JScrollPane (
071 jTextArea = new JTextArea ( ) ), BorderLayout.CENTER );
072
073 jTextArea.setFont ( AgoracastConstants.LOG_FONT );
074
075 cancelButton = new JButton ( "Cancel" );
076
077 cancelButton.addActionListener ( this );
078
079 add ( cancelButton, BorderLayout.SOUTH );
080 }
081
082 //////////////////////////////////////////////////////////////////////
083 //////////////////////////////////////////////////////////////////////
084
085 public void actionPerformed ( ActionEvent actionEvent )
086 //////////////////////////////////////////////////////////////////////
087 {
088 try
089 {
090 Object source = actionEvent.getSource ( );
091
092 if ( source == cancelButton )
093 {
094 if ( nntpSocket != null )
095 {
096 nntpSocket.close ( );
097 }
098
099 agoracastBrowsePanel.showTable ( );
100 }
101 }
102 catch ( Exception ex )
103 {
104 record ( ex );
105 }
106 }
107
108 public synchronized void download ( )
109 //////////////////////////////////////////////////////////////////////
110 {
111 new Thread ( this ).start ( );
112 }
113
114 public synchronized void run ( )
115 //////////////////////////////////////////////////////////////////////
116 {
117 try
118 {
119 jTextArea.setText (
120 DateFormatLib.toIsoDateFormat ( new Date ( ) )
121 + " Downloading messages..." );
122
123 cancelButton.setText ( "Cancel" );
124
125 String server = agoracastMediator.getServer ( );
126
127 String newsgroupName = agoracastMediator.getNewsgroup ( );
128
129 nntpSocket = new NntpSocket ( server, this );
130
131 // 200 server ready - posting allowed
132 // 201 server ready - no posting allowed
133
134 if ( nntpSocket.getResponseCode ( ).startsWith ( "20" ) )
135 {
136 String responseCode = nntpSocket.command (
137 NntpConstants.COMMAND_GROUP + " " + newsgroupName );
138
139 if ( responseCode.startsWith ( "480" ) )
140 {
141 // 480 Authentication Required
142
143 AgoracastLib.authenticate ( nntpSocket, agoracastMediator );
144
145 // throws SecurityException if it fails
146
147 responseCode = nntpSocket.command (
148 NntpConstants.COMMAND_GROUP + " " + newsgroupName );
149 }
150
151 // 211 n f l s group selected
152 // (n = estimated number of articles in group,
153 // f = first article number in the group,
154 // l = last article number in the group,
155 // s = name of the group.)
156 // 411 no such news group
157
158 if ( responseCode.startsWith ( "211" ) )
159 {
160 Newsgroup newsgroup = Newsgroup.parse ( responseCode );
161
162 AgoracastDatabase agoracastDatabase
163 = agoracastMediator.getAgoracastDatabase ( );
164
165 AgoracastNewsrc agoracastNewsrc
166 = agoracastMediator.getAgoracastNewsrc ( );
167
168 long lastRead = agoracastNewsrc.getLastArticleNumber (
169 server, newsgroupName );
170
171 long newsgroupLastArticle = newsgroup.getLastArticle ( );
172
173 if ( lastRead > newsgroupLastArticle )
174 {
175 lastRead = -1;
176 }
177
178 if ( newsgroupLastArticle
179 > lastRead + AgoracastConstants.DOWNLOAD_MAX )
180 {
181 lastRead
182 = newsgroupLastArticle - AgoracastConstants.DOWNLOAD_MAX;
183 }
184
185 responseCode = nntpSocket.command ( NntpConstants.COMMAND_HEAD
186 + ( lastRead > -1 ? " " + ( lastRead + 1 ) : "" ) );
187
188 boolean hasNext = true;
189
190 while ( hasNext )
191 {
192 // 221 n <a> article retrieved - head follows
193
194 if ( responseCode.startsWith ( "221 " ) )
195 {
196
197 System.out.println ( "response: " + responseCode );
198
199 StringTokenizer stringTokenizer
200 = new StringTokenizer ( responseCode, " " );
201
202 stringTokenizer.nextToken ( );
203
204 long articleNumber
205 = Long.parseLong ( stringTokenizer.nextToken ( ) );
206
207 String articleId = stringTokenizer.nextToken ( );
208
209 BufferedReader bufferedReader
210 = nntpSocket.getBufferedReader ( );
211
212 UsenetMessage usenetMessage
213 = UsenetMessage.parse ( bufferedReader );
214
215 String subject
216 = usenetMessage.getHeader ( UsenetMessage.HEADER_SUBJECT );
217
218 subject = StringLib.trimToNull ( subject );
219
220 if ( subject != null
221 && subject.toLowerCase ( ).startsWith (
222 AgoracastConstants.SUBJECT_PREFIX_LOWER_CASE ) )
223 {
224 responseCode
225 = nntpSocket.command ( NntpConstants.COMMAND_BODY );
226
227 // 222 n <a> article retrieved - body follows
228
229 if ( responseCode.startsWith ( "222" ) )
230 {
231 Pair [ ] pairs = parseBody (
232 UsenetMessage.parseBody ( bufferedReader ) );
233
234 if ( ( pairs != null )
235 && ( pairs.length > 0 ) )
236 {
237 agoracastDatabase.add ( new AgoracastData (
238 newsgroupName, articleId, pairs ) );
239 }
240 }
241 }
242
243 agoracastNewsrc.setLastArticleNumber (
244 server, newsgroupName, articleNumber );
245 }
246
247 responseCode
248 = nntpSocket.command ( NntpConstants.COMMAND_NEXT );
249
250 // 223 n a article retrieved - request text separately
251 // (n = article number, a = unique article id)
252 // 412 no newsgroup selected
253 // 420 no current article has been selected
254 // 421 no next article in this group
255
256 if ( !responseCode.startsWith ( "223" ) )
257 {
258 hasNext = false;
259 }
260 else
261 {
262 responseCode
263 = nntpSocket.command ( NntpConstants.COMMAND_HEAD );
264 }
265 }
266 }
267 }
268
269 nntpSocket.command ( NntpConstants.COMMAND_QUIT );
270 }
271 catch ( Exception ex )
272 {
273 record ( ex );
274 }
275 finally
276 {
277 if ( nntpSocket != null )
278 {
279 try
280 {
281 nntpSocket.close ( );
282 }
283 catch ( Exception ex )
284 {
285 record ( ex );
286 }
287 }
288
289 cancelButton.setText ( "Done" );
290 }
291 }
292
293 //////////////////////////////////////////////////////////////////////
294 //////////////////////////////////////////////////////////////////////
295
296 // cut-and-paste from AgoracastSendPanel, make reusable, see LogPanel
297
298 public synchronized void record ( String message )
299 //////////////////////////////////////////////////////////////////////
300 {
301 jTextArea.append ( '\n' + message );
302
303 String text = jTextArea.getText ( );
304
305 int textLength = text.length ( );
306
307 if ( textLength > AgoracastConstants.LOG_TEXT_LENGTH_MAX )
308 {
309 jTextArea.setText ( text.substring (
310 textLength - AgoracastConstants.LOG_TEXT_LENGTH_MAX ) );
311 }
312 }
313
314 public synchronized void record ( Throwable throwable )
315 //////////////////////////////////////////////////////////////////////
316 {
317 record ( '\n' + throwable.toString ( ) );
318 }
319
320
321 public synchronized void record (
322 String message, Throwable throwable )
323 //////////////////////////////////////////////////////////////////////
324 {
325 record ( '\n' + message + '\n' + throwable.toString ( ) );
326 }
327
328 //////////////////////////////////////////////////////////////////////
329 //////////////////////////////////////////////////////////////////////
330
331 private static final Pair [ ] parseBody ( String body )
332 //////////////////////////////////////////////////////////////////////
333 {
334 NullArgumentException.check ( body );
335
336 java.util.List pairList = new ArrayList ( );
337
338 String [ ] lines = StringLib.toStringArray ( body );
339
340 for ( int i = 0; i < lines.length; i++ )
341 {
342 Pair pair = parseLine ( lines [ i ] );
343
344 if ( pair == null )
345 {
346 break;
347 }
348
349 pairList.add ( pair );
350 }
351
352 return ( Pair [ ] ) pairList.toArray ( new Pair [ ] { } );
353 }
354
355 private static final Pair parseLine ( String line )
356 //////////////////////////////////////////////////////////////////////
357 {
358 int index = line.indexOf ( ':' );
359
360 if ( index < 0 )
361 {
362 return null;
363 }
364
365 String name
366 = StringLib.trimToNull ( line.substring ( 0, index ) );
367
368 String value
369 = StringLib.trimToNull ( line.substring ( index + 1 ) );
370
371 if ( ( name == null )
372 || ( value == null ) )
373 {
374 return null;
375 }
376
377 // the following could be much more efficient
378
379 while ( name.endsWith ( "." ) )
380 {
381 name = name.substring ( 0, name.length ( ) - 1 ).trim ( );
382 }
383
384 if ( "".equals ( name ) )
385 {
386 return null;
387 }
388
389 return new Pair ( name, value );
390 }
391
392 //////////////////////////////////////////////////////////////////////
393 //////////////////////////////////////////////////////////////////////
394 }