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 }