/*******************************************************************************
* HelloNzb -- The Binary Usenet Tool
* Copyright (C) 2010-2013 Matthias F. Brandstetter
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package me.mabra.hellonzb.nntpclient;
import com.sun.mail.util.UUDecoderStream;
import me.mabra.hellonzb.HelloNzb;
import me.mabra.hellonzb.helloyenc.HelloYenc;
import me.mabra.hellonzb.helloyenc.HelloYencRunnable;
import me.mabra.hellonzb.parser.DownloadFile;
import me.mabra.hellonzb.util.MyLogger;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Vector;
/**
* This class is called by the class NntpFileDownload as a thread's runnable
* object when a file has been finished downloading and should now be decoded.
*
* @author Matthias F. Brandstetter
*/
public class FileDecoder implements Runnable, HelloYencRunnable
{
/** The main HelloNzb application object */
private final HelloNzb mainApp;
/** Current file downloader thread */
private final NntpFileDownloader fileDownloader;
/** central logger object */
private MyLogger logger;
/** download file to decode */
private DownloadFile dlFile;
/** the data to decode */
private ArrayList<byte[]> articleData;
/** data encoding */
private String encoding;
/** The download directory on local disk */
private File dlDir;
public FileDecoder(HelloNzb mainApp, NntpFileDownloader downloader, File dlDir, DownloadFile dlFile,
ArrayList<byte[]> data, String encoding)
{
this.mainApp = mainApp;
this.fileDownloader = downloader;
this.logger = mainApp.getLogger();
this.dlDir = dlDir;
this.dlFile = dlFile;
this.articleData = data;
this.encoding = encoding;
this.dlDir = dlDir;
}
public void run()
{
Vector<byte[]> outVector = new Vector<byte[]>();
HelloYenc yencDecoder = null;
UUDecoderStream uuDecoder = null;
FileOutputStream fileOutStream = null;
int uuLength = 0;
// prepare suitable decoder
if(encoding.equals("yenc"))
yencDecoder = new HelloYenc(logger);
else if(encoding.equals("uu"))
uuDecoder = null;
else
{
logger.msg("Could not find a suitable decoder!", MyLogger.SEV_ERROR);
return;
}
try
{
int i = 0;
while(articleData.size() > 0)
{
// check for corrupt download
if(articleData.get(0) == null || articleData.get(0).length == 0)
{
logger.msg("FileDecoder: Corrupt data found", MyLogger.SEV_WARNING);
articleData.remove(0);
continue;
}
// set data input stream of the yenc decoder object
if(encoding.equals("yenc"))
{
yencDecoder.setInputData(articleData.get(0));
yencDecoder.setPartNum(i + 1);
yencDecoder.setRunnable(this);
}
// set data for the UUDecoder object
else if(encoding.equals("uu"))
{
byte[] src = articleData.get(0);
uuLength = src.length;
ByteArrayInputStream inStream = new ByteArrayInputStream(src);
uuDecoder = new UUDecoderStream(inStream);
}
// do we have the first (yenc) part loaded (then get filename)?
if(i == 0)
{
File resultFile = createOutFile(yencDecoder, uuDecoder);
fileOutStream = new FileOutputStream(resultFile);
}
// now decode the current article data block
if(articleData.get(0).length > 0)
{
if(encoding.equals("yenc"))
outVector.add(yencDecoder.decode());
else if(encoding.equals("uu"))
{
byte[] bytes = new byte[uuLength];
int b;
b = uuDecoder.read();
int outBufCounter = 0;
for(; b != -1 && outBufCounter < uuLength; outBufCounter++)
{
bytes[outBufCounter] = (byte) b;
b = uuDecoder.read();
}
byte[] newOutBuf = new byte[outBufCounter];
System.arraycopy(bytes, 0, newOutBuf, 0, outBufCounter);
outVector.add(newOutBuf);
}
}
// update progress bar in main window
final int j = i + 1;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
mainApp.updateDownloadQueue(dlFile.getFilename(), j);
}
});
// remove processed element
articleData.remove(0);
i++;
}
// notify main application that writing of file started
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
mainApp.fileWritingStarted(dlFile.getFilename());
mainApp.updateDownloadQueue(dlFile.getFilename(), 0);
}
});
// write data to output file and close it afterwards
writeData(outVector, fileOutStream);
fileOutStream.close();
}
catch(IOException ex)
{
logger.printStackTrace(ex);
mainApp.IoErrorWhileSavingData(ex);
return;
}
catch(Exception ex)
{
logger.printStackTrace(ex);
}
// update main application window that file decoding is finished now
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// notify main application that decoding of this file has finished
mainApp.fileDecodingFinished(dlFile.getFilename());
}
});
fileDownloader.fileDecoderFinished(this);
}
// create output file
private File createOutFile(HelloYenc yencDecoder, UUDecoderStream uuDecoder) throws IOException
{
File resultFile = null;
File resultDir = null;
String filename = "";
filename = dlFile.getFilename();
resultFile = new File(dlDir.getAbsolutePath().trim() + File.separator + filename);
resultDir = new File(dlDir.getAbsolutePath().trim());
resultDir.mkdirs();
if(resultFile.exists())
resultFile = createNewFilename(resultFile);
logger.msg("Creating new output file: " + resultFile.getCanonicalPath(), MyLogger.SEV_DEBUG);
resultFile.createNewFile();
return resultFile;
}
// if the given file already exists, create another file instead
private File createNewFilename(File resultFile)
{
@SuppressWarnings("serial")
File fileRename = new File(resultFile.getPath())
{
public File unique()
{
String sExtension;
String sFileWithoutExt = getPath();
int iIndexDot = sFileWithoutExt.lastIndexOf(".");
if(iIndexDot < 0)
sExtension = "";
else
{
sExtension = sFileWithoutExt.substring(iIndexDot, sFileWithoutExt.length());
sFileWithoutExt = sFileWithoutExt.substring(0, iIndexDot + 1);
}
int iT1 = 1;
while(true)
{
File fileHelp = new File(sFileWithoutExt + Integer.toString(iT1) + sExtension);
if(!fileHelp.exists())
return fileHelp;
iT1++;
}
}
}.unique();
return fileRename;
}
// write data to given output file stream
private void writeData(Vector<byte[]> outVector, FileOutputStream fileOutStream) throws IOException
{
int i = 0;
while(outVector.size() > 0)
{
byte[] buffer = outVector.get(0);
fileOutStream.write(buffer);
fileOutStream.flush();
// update progress bar in main window
final int j = i + 1;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
mainApp.updateDownloadQueue(dlFile.getFilename(), j);
}
});
outVector.remove(0);
i++;
}
}
public String getFilename()
{
return dlFile.getFilename();
}
public void crc32Error()
{
// TODO Auto-generated method stub
}
}