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="https://www.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         }