/*******************************************************************************
* HelloNzb -- The Binary Usenet Tool
* Copyright (C) 2010-2013 Matthias F. Brandstetter
* https://sourceforge.net/projects/hellonzb/
*
* 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.unrar;
import me.mabra.hellonzb.HelloNzb;
import me.mabra.hellonzb.HelloNzbConstants;
import me.mabra.hellonzb.HelloNzbToolkit;
import me.mabra.hellonzb.parser.NzbParser;
import me.mabra.hellonzb.util.MyLogger;
import me.mabra.hellonzb.util.StreamGobbler;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class is used as background thread to extract a RAR archive.
* It will assume all volumes of the RAR archive have already been downloaded.
*
* @author Matthias F. Brandstetter
*/
public class CompleteRarExtractor implements Runnable
{
/** main application object */
private HelloNzb mainApp;
/** parser object to process */
private NzbParser parser;
/** central logger object */
private MyLogger logger;
/** download directory from application preferences */
private File dlDir;
/** nzb file name **/
private String nzbFilename;
/** A list of all part files of the RAR archive */
private List<String> rarPartFiles;
public CompleteRarExtractor(HelloNzb mainApp, NzbParser parser) throws RuntimeException
{
this.mainApp = mainApp;
this.logger = mainApp.getLogger();
this.parser = parser;
this.rarPartFiles = new ArrayList<>();
// download directory
nzbFilename = parser.getName();
nzbFilename = HelloNzbToolkit.getLastFilename(nzbFilename);
dlDir = new File(parser.getDownloadFolder());
if(!dlDir.isDirectory())
throw new RuntimeException("Invalid directory for RAR extraction");
}
public void run()
{
String rarFilename = "";
// search all files within destination directory
String filenames[] = dlDir.list();
if(filenames == null)
return;
else
java.util.Arrays.sort(filenames);
for(String filename : filenames)
{
if(filename.toLowerCase().endsWith(".rar"))
{
rarFilename = filename;
break;
}
}
// valid rar filename found?
try
{
if(!rarFilename.isEmpty())
{
logger.msg("Extracting RAR archive to " + dlDir, MyLogger.SEV_INFO);
String userPw = parser.getArchivePassword();
int exitVal = callUnrarCmdLine(rarFilename, dlDir, userPw != null ? userPw : "-");
// if extraction failed, try to extract password from file names
if(exitVal > 0)
{
// target extraction folder empty?
File dir = new File(dlDir + File.separator + HelloNzbConstants.UNRAR_SUBDIR);
File [] files = dir.listFiles();
if(files == null || files.length == 0)
{
// check for a password in the NZB file name
Pattern pattern = Pattern.compile("\\{\\{.*\\}\\}$");
Matcher matcher = pattern.matcher(nzbFilename);
if(matcher.find())
{
String pwd = matcher.group();
pwd = pwd.substring(2, pwd.length() - 2);
logger.msg("Retrying to extract archive with password = " + pwd, MyLogger.SEV_INFO);
exitVal = callUnrarCmdLine(rarFilename, dlDir, pwd);
}
}
}
if(exitVal == 0)
{
parser.saveToDelete(rarPartFiles);
parser.unrarSucceeded(new File(rarFilename).getName());
parser.setUnrarDestFolder(dlDir + File.separator + HelloNzbConstants.UNRAR_SUBDIR);
}
else
parser.unrarFailed();
}
else
{
logger.msg("No (suitable) .rar file for extraction found", MyLogger.SEV_INFO);
parser.unrarFailed();
}
}
catch(Exception ex)
{
logger.printStackTrace(ex);
parser.unrarFailed();
}
finally
{
mainApp.rarExtractDone(parser);
}
}
private int callUnrarCmdLine(String rarFilename, File destination, String pwd)
throws IOException, InterruptedException
{
Vector<String> cmd = new Vector<String>();
String finalDestDir = destination + File.separator + HelloNzbConstants.UNRAR_SUBDIR;
File finalDest = new File(finalDestDir);
// check if destination directory exists
if(!finalDest.exists())
finalDest.mkdirs();
if(!finalDest.exists())
{
logger.msg("Could not create target archive for RAR extraction", MyLogger.SEV_ERROR);
return -1;
}
// yes, do the unrar extract!
String execLocation = mainApp.getPrefValue("DownloadSettingsUnrarExeLocation");
// create command to execute
cmd.add(execLocation);
cmd.add("x");
cmd.add("-p" + pwd);
cmd.add("-y");
cmd.add("-r");
cmd.add("-idp");
cmd.add(rarFilename);
cmd.add(finalDestDir);
rarPartFiles.clear();
logger.msg("Starting Unrar extraction: " + cmd, MyLogger.SEV_INFO);
// execute command
String [] cmdArray = cmd.toArray(new String[] {});
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmdArray, null, destination);
StreamGobbler errGobbler = new StreamGobbler(logger, proc.getErrorStream(), "Unrar (Error)");
StreamGobbler outGobbler = new StreamGobbler(logger, proc.getInputStream(), "Unrar");
// fetch command's STDOUT and STDERR
errGobbler.start();
outGobbler.start();
// wait until program has finished
int exitVal = proc.waitFor();
logger.msg("Unrar command exit value: " + exitVal, MyLogger.SEV_INFO);
// get RAR archive part file names
for(String line : outGobbler.getLines())
{
String l = line.trim();
if(l.startsWith("Extracting from "))
rarPartFiles.add(l.substring(16));
}
return exitVal;
}
}