Add updated brazilian portuguese translation by @almircanella! #64

Refactor NETCommunications. Not it's readable :D
A lot of small changes, code refactoring, updates and fixes.
This commit is contained in:
Dmitry Isaenko 2020-07-10 16:57:29 +03:00
parent 96e85056dd
commit 267ffcf5d2
22 changed files with 553 additions and 522 deletions

View file

@ -18,7 +18,6 @@
*/
package nsusbloader.COM.NET;
import nsusbloader.COM.INSTask;
import nsusbloader.ModelControllers.ILogPrinter;
import nsusbloader.NSLDataTypes.EFileStatus;
import nsusbloader.ModelControllers.Log;
@ -32,305 +31,124 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class NETCommunications implements INSTask { // todo: rewrite
public class NETCommunications implements Runnable {
private ILogPrinter logPrinter;
private final ILogPrinter logPrinter;
private String hostIP;
private int hostPort;
private String extras;
private final String switchIP;
private final static int SWITCH_PORT = 2000;
private final String hostIP;
private final int hostPort;
private final String extras;
private final boolean doNotServe;
private String switchIP;
private final HashMap<String, UniFile> files;
private HashMap<String, File> nspMap;
private HashMap<String, Long> nspFileSizes;
private final ServerSocket serverSocket;
private Socket clientSocket;
private ServerSocket serverSocket;
private boolean isValid;
private boolean doNotServeRequests;
private final boolean isValid;
private OutputStream currSockOS;
private PrintWriter currSockPW;
private volatile boolean cancel;
/**
* Simple constructor that everybody uses
* */
public NETCommunications(List<File> filesList,
String switchIP,
boolean doNotServeRequests,
String hostIPaddr,
boolean doNotServe,
String hostIP,
String hostPortNum,
String extras) {
this.doNotServeRequests = doNotServeRequests;
if (doNotServeRequests)
String extras)
{
this.doNotServe = doNotServe;
if (doNotServe)
this.extras = extras;
else
this.extras = "";
this.switchIP = switchIP;
this.logPrinter = Log.getPrinter(EModule.USB_NET_TRANSFERS);
this.nspMap = new HashMap<>();
this.nspFileSizes = new HashMap<>();
// Filter and remove empty/incorrect split-files
filesList.removeIf(f -> {
if (f.isDirectory()){
File[] subFiles = f.listFiles((file, name) -> name.matches("[0-9]{2}"));
if (subFiles == null || subFiles.length == 0) {
logPrinter.print("NET: Removing empty folder: " + f.getName(), EMsgType.WARNING);
return true;
}
else {
Arrays.sort(subFiles, Comparator.comparingInt(file -> Integer.parseInt(file.getName())));
for (int i = subFiles.length - 2; i > 0 ; i--){
if (subFiles[i].length() < subFiles[i-1].length()) {
logPrinter.print("NET: Removing strange split file: "+f.getName()+
"\n (Chunk sizes of the split file are not the same, but has to be.)", EMsgType.WARNING);
return true;
}
}
}
}
return false;
});
// Collect and encode NSP files list
try {
for (File nspFile : filesList)
nspMap.put(URLEncoder.encode(nspFile.getName(), "UTF-8").replaceAll("\\+", "%20"), nspFile); // replace + to %20
}
catch (UnsupportedEncodingException uee){
isValid = false;
logPrinter.print("NET: Unsupported encoding for 'URLEncoder'. Internal issue you can't fix. Please report. Returned:\n\t"+uee.getMessage(), EMsgType.FAIL);
//for (File nspFile : filesList)
// nspMap.put(nspFile.getName(), nspFile);
NetworkSetupValidator validator =
new NetworkSetupValidator(filesList, doNotServe, hostIP, hostPortNum, logPrinter);
this.hostIP = validator.getHostIP();
this.hostPort = validator.getHostPort();
this.files = validator.getFiles();
this.serverSocket = validator.getServerSocket();
this.isValid = validator.isValid();
if (! isValid)
close(EFileStatus.FAILED);
return;
}
// Collect sizes since now we can have split-files support
for (Map.Entry<String, File> entry : nspMap.entrySet()){
File inFile = entry.getValue();
long fSize = 0;
if (inFile.isDirectory()){
File[] subFiles = inFile.listFiles((file, name) -> name.matches("[0-9]{2}"));
for (File subFile : subFiles)
fSize += subFile.length();
nspFileSizes.put(entry.getKey(), fSize);
}
else
nspFileSizes.put(entry.getKey(), inFile.length());
}
// Resolve IP
if (hostIPaddr.isEmpty()) {
DatagramSocket socket;
try { // todo: check other method if internet unavaliable
socket = new DatagramSocket();
socket.connect(InetAddress.getByName("8.8.8.8"), 10002); // Google
hostIP = socket.getLocalAddress().getHostAddress();
socket.close();
}
catch (SocketException | UnknownHostException e) {
logPrinter.print("NET: Can't get your computer IP using Google DNS server. Returned:\n\t"+e.getMessage(), EMsgType.INFO);
try {
socket = new DatagramSocket();
socket.connect(InetAddress.getByName("193.0.14.129"), 10002); // RIPE NCC
hostIP = socket.getLocalAddress().getHostAddress();
socket.close();
}
catch (SocketException | UnknownHostException e1) {
logPrinter.print("NET: Can't get your computer IP using RIPE NCC root server. Returned:\n\t"+e1.getMessage(), EMsgType.INFO);
try {
socket = new DatagramSocket();
socket.connect(InetAddress.getByName("people.com.cn"), 10002); // Renmin Ribao
hostIP = socket.getLocalAddress().getHostAddress();
socket.close();
}
catch (SocketException | UnknownHostException e2) {
logPrinter.print("NET: Can't get your computer IP using Renmin Ribao server. Returned:\n\t"+e2.getMessage(), EMsgType.FAIL);
logPrinter.print("Try using 'Expert mode' and set IP manually.", EMsgType.INFO);
this.showAvalIpExamples();
isValid = false;
close(EFileStatus.FAILED);
return;
}
}
}
// Say hello to MacOS Catalina
// Also this part could be used instead of what we have above. One day it has to be tested on all platforms and fixed (replace code above).
if (hostIP.equals("0.0.0.0")) {
Socket scoketK;
try {
scoketK = new Socket();
scoketK.connect(new InetSocketAddress("google.com", 80));
hostIP = scoketK.getLocalAddress().getHostAddress();
scoketK.close();
} catch (Exception scoketKex) {
scoketKex.printStackTrace();
logPrinter.print("NET: Can't get your computer IP using Google server (InetSocketAddress). Returned:\n\t"+scoketKex.getMessage(), EMsgType.INFO);
try {
scoketK = new Socket();
scoketK.connect(new InetSocketAddress("people.com.cn", 80));
hostIP = scoketK.getLocalAddress().getHostAddress();
scoketK.close();
} catch (Exception scoketKexx) {
scoketKex.printStackTrace();
logPrinter.print("NET: Can't get your computer IP using Renmin Ribao server (InetSocketAddress). Returned:\n\t"+scoketKexx.getMessage(), EMsgType.FAIL);
logPrinter.print("Try using 'Expert mode' and set IP manually.", EMsgType.INFO);
this.showAvalIpExamples();
isValid = false;
close(EFileStatus.FAILED);
return;
}
}
}
logPrinter.print("NET: Your IP detected as: " + hostIP, EMsgType.PASS);
}
else {
this.hostIP = hostIPaddr;
logPrinter.print("NET: Your IP defined as: " + hostIP, EMsgType.PASS);
}
// Get port
if (! doNotServeRequests) {
if (hostPortNum.isEmpty()) {
Random portRandomizer = new Random();
for (int i = 0; i < 5; i++) {
try {
this.hostPort = portRandomizer.nextInt(999) + 6000;
serverSocket = new ServerSocket(hostPort); //System.out.println(serverSocket.getInetAddress()); 0.0.0.0
logPrinter.print("NET: Your port detected as: " + hostPort, EMsgType.PASS);
break;
}
catch (IOException ioe) {
if (i == 4) {
logPrinter.print("NET: Can't find good port", EMsgType.FAIL);
logPrinter.print("Try using 'Expert mode' and set port by yourself.", EMsgType.INFO);
isValid = false;
close(EFileStatus.FAILED);
return;
} else
logPrinter.print("NET: Can't use port " + hostPort + "\nLooking for another one.", EMsgType.WARNING);
}
}
} else {
try {
this.hostPort = Integer.parseInt(hostPortNum);
serverSocket = new ServerSocket(hostPort);
logPrinter.print("NET: Using defined port number: " + hostPort, EMsgType.PASS);
}
catch (NumberFormatException nfe) { // Literally never happens.
logPrinter.print("NET: Can't use port defined in settings: " + hostPortNum + "\nIt's not a valid number!", EMsgType.FAIL);
isValid = false;
close(EFileStatus.FAILED);
return;
}
catch (IOException ioex){
logPrinter.print("NET: Can't use port defined in settings: " + hostPortNum + "\n\t"+ioex.getMessage(), EMsgType.FAIL);
isValid = false;
close(EFileStatus.FAILED);
return;
}
}
}
else {
if (hostPortNum.isEmpty()){
logPrinter.print("NET: Port must be defined if 'Don't serve requests' option selected!", EMsgType.FAIL);
isValid = false;
close(EFileStatus.FAILED);
return;
}
try {
this.hostPort = Integer.parseInt(hostPortNum);
}
catch (NumberFormatException fex){
logPrinter.print("NET: Can't use port defined in settings: " + hostPortNum + "\nIt's not a valid number!", EMsgType.WARNING);
isValid = false;
close(EFileStatus.FAILED);
return;
}
}
isValid = true;
}
/**
* Show possible variants to log area
* */
private void showAvalIpExamples(){
try {
Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
while (enumeration.hasMoreElements()) {
NetworkInterface n = enumeration.nextElement();
Enumeration<InetAddress> enumeration1 = n.getInetAddresses();
while (enumeration1.hasMoreElements())
logPrinter.print("Check for: " + enumeration1.nextElement().getHostAddress(), EMsgType.INFO);
}
}
catch (SocketException socketException) { // Good block.
logPrinter.print("Can't determine possible variants. Returned:\n\t"+socketException.getMessage(), EMsgType.FAIL);
}
}
@Override
public boolean isCancelled(){
return cancel;
}
@Override
public void cancel() {
cancel = true;
}
@Override
public void run() {
if (!isValid | isCancelled())
if (! isValid || Thread.interrupted() )
return;
logPrinter.print("\tStart chain", EMsgType.INFO);
// Create string that we'll send to TF and which initiates chain
StringBuilder myStrBuilder;
myStrBuilder = new StringBuilder();
for (String fileNameEncoded : nspMap.keySet()) {
myStrBuilder.append(hostIP);
myStrBuilder.append(':');
myStrBuilder.append(hostPort);
myStrBuilder.append('/');
myStrBuilder.append(extras);
myStrBuilder.append(fileNameEncoded);
myStrBuilder.append('\n');
}
final String handshakeContent = buildHandshakeContent();
byte[] nspListNames = myStrBuilder.toString().getBytes(StandardCharsets.UTF_8); // Follow the
byte[] nspListSize = ByteBuffer.allocate(Integer.BYTES).putInt(nspListNames.length).array(); // defining order
byte[] handshakeCommand = handshakeContent.getBytes(StandardCharsets.UTF_8);
byte[] handshakeCommandSize = ByteBuffer.allocate(Integer.BYTES).putInt(handshakeCommand.length).array();
try {
Socket handShakeSocket = new Socket(InetAddress.getByName(switchIP), 2000);
OutputStream os = handShakeSocket.getOutputStream();
os.write(nspListSize);
os.write(nspListNames);
os.flush();
handShakeSocket.close();
}
catch (IOException uhe){
logPrinter.print("NET: Unable to connect to NS and send files list. Returned:\n\t"+uhe.getMessage(), EMsgType.FAIL);
close(EFileStatus.UNKNOWN);
if (sendHandshake(handshakeCommandSize, handshakeCommand))
return;
}
// Check if we should serve requests
if (this.doNotServeRequests){
logPrinter.print("NET: List of files transferred. Replies won't be served.", EMsgType.PASS);
if (this.doNotServe){
logPrinter.print("List of files transferred. Replies won't be served.", EMsgType.PASS);
close(EFileStatus.UNKNOWN);
return;
}
logPrinter.print("NET: Initiation files list has been sent to NS.", EMsgType.PASS);
logPrinter.print("Initiation files list has been sent to NS.", EMsgType.PASS);
// Go transfer
Socket clientSocket;
work_routine:
while (true){
try {
serveRequestsLoop();
}
/**
* Create string that we'll send to TF/AW and which initiates chain
* */
private String buildHandshakeContent(){
StringBuilder builder = new StringBuilder();
for (String fileNameEncoded : files.keySet()) {
builder.append(hostIP);
builder.append(':');
builder.append(hostPort);
builder.append('/');
builder.append(extras);
builder.append(fileNameEncoded);
builder.append('\n');
}
return builder.toString();
}
private boolean sendHandshake(byte[] handshakeCommandSize, byte[] handshakeCommand){
try {
Socket handshakeSocket = new Socket(InetAddress.getByName(switchIP), SWITCH_PORT);
OutputStream os = handshakeSocket.getOutputStream();
os.write(handshakeCommandSize);
os.write(handshakeCommand);
os.flush();
handshakeSocket.close();
}
catch (IOException uhe){
logPrinter.print("Unable to connect to NS and send files list:\n "
+ uhe.getMessage(), EMsgType.FAIL);
close(EFileStatus.UNKNOWN);
return true;
}
return false;
}
private void serveRequestsLoop(){
try {
while (true){
clientSocket = serverSocket.accept();
BufferedReader br = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream())
@ -345,8 +163,7 @@ public class NETCommunications implements INSTask { // todo: rewrite
while ((line = br.readLine()) != null) {
//System.out.println(line); // Debug
if (line.trim().isEmpty()) { // If TCP packet is ended
if (handleRequest(tcpPacket)) // Proceed required things
break work_routine;
handleRequest(tcpPacket); // Proceed required things
tcpPacket.clear(); // Clear data and wait for next TCP packet
}
else
@ -356,206 +173,219 @@ public class NETCommunications implements INSTask { // todo: rewrite
// and reopen client sock
clientSocket.close();
}
catch (IOException ioe){ // If server socket closed, then client socket also closed.
break;
}
}
if ( ! isCancelled() )
catch (Exception e){
if (Thread.interrupted())
logPrinter.print("Interrupted by user.", EMsgType.INFO);
else
logPrinter.print(e.getMessage(), EMsgType.INFO);
close(EFileStatus.UNKNOWN);
return;
}
}
// 200 206 400 (inv range) 404 416 (Range Not Satisfiable )
/**
* Handle requests
* @return true if failed
* */
private boolean handleRequest(LinkedList<String> packet){
//private boolean handleRequest(LinkedList<String> packet, OutputStreamWriter pw){
private void handleRequest(LinkedList<String> packet) throws Exception{
File requestedFile;
String reqFileName = packet.get(0).replaceAll("(^[A-z\\s]+/)|(\\s+?.*$)", "");
if (! nspFileSizes.containsKey(reqFileName)){
currSockPW.write(NETPacket.getCode404());
currSockPW.flush();
logPrinter.print("NET: File "+reqFileName+" doesn't exists or have 0 size. Returning 404", EMsgType.FAIL);
return true;
if (! files.containsKey(reqFileName)){
writeToSocket(NETPacket.getCode404());
logPrinter.print("File "+reqFileName+" doesn't exists or have 0 size. Returning 404", EMsgType.FAIL);
return;
}
long reqFileSize = nspFileSizes.get(reqFileName);
requestedFile = nspMap.get(reqFileName);
long reqFileSize = files.get(reqFileName).getSize();
requestedFile = files.get(reqFileName).getFile();
if (! requestedFile.exists() || reqFileSize == 0){ // well.. tell 404 if file exists with 0 length is against standard, but saves time
currSockPW.write(NETPacket.getCode404());
currSockPW.flush();
logPrinter.print("NET: File "+requestedFile.getName()+" doesn't exists or have 0 size. Returning 404", EMsgType.FAIL);
writeToSocket(NETPacket.getCode404());
logPrinter.print("File "+requestedFile.getName()+" doesn't exists or have 0 size. Returning 404", EMsgType.FAIL);
logPrinter.update(requestedFile, EFileStatus.FAILED);
return true;
return;
}
if (packet.get(0).startsWith("HEAD")){
currSockPW.write(NETPacket.getCode200(reqFileSize));
currSockPW.flush();
logPrinter.print("NET: Replying for requested file: "+requestedFile.getName(), EMsgType.INFO);
return false;
writeToSocket(NETPacket.getCode200(reqFileSize));
logPrinter.print("Replying for requested file: "+requestedFile.getName(), EMsgType.INFO);
return;
}
if (packet.get(0).startsWith("GET")) {
for (String line: packet) {
if (line.toLowerCase().startsWith("range")) { //todo: fix
try {
String[] rangeStr = line.toLowerCase().replaceAll("^range:\\s+?bytes=", "").split("-", 2);
if (!rangeStr[0].isEmpty() && !rangeStr[1].isEmpty()) { // If both ranges defined: Read requested
if (Long.parseLong(rangeStr[0]) > Long.parseLong(rangeStr[1])){ // If start bytes greater then end bytes
currSockPW.write(NETPacket.getCode400());
currSockPW.flush();
logPrinter.print("NET: Requested range for "+requestedFile.getName()+" is incorrect. Returning 400", EMsgType.FAIL);
logPrinter.update(requestedFile, EFileStatus.FAILED);
return true;
}
if (writeToSocket(reqFileName, Long.parseLong(rangeStr[0]), Long.parseLong(rangeStr[1]))) // DO WRITE
return true;
if (! line.toLowerCase().startsWith("range")) //todo: fix
continue;
}
else if (!rangeStr[0].isEmpty()) { // If only START defined: Read all
if (writeToSocket(reqFileName, Long.parseLong(rangeStr[0]), reqFileSize)) // DO WRITE
return true;
}
else if (!rangeStr[1].isEmpty()) { // If only END defined: Try to read last 500 bytes
if (reqFileSize > 500){
if (writeToSocket(reqFileName, reqFileSize-500, reqFileSize)) // DO WRITE
return true;
}
else { // If file smaller than 500 bytes
currSockPW.write(NETPacket.getCode416());
currSockPW.flush();
logPrinter.print("NET: File size requested for "+requestedFile.getName()+" while actual size of it: "+reqFileSize+". Returning 416", EMsgType.FAIL);
logPrinter.update(requestedFile, EFileStatus.FAILED);
return true;
}
}
else {
currSockPW.write(NETPacket.getCode400()); // If Range not defined: like "Range: bytes=-"
currSockPW.flush();
logPrinter.print("NET: Requested range for "+requestedFile.getName()+" is incorrect (empty start & end). Returning 400", EMsgType.FAIL);
logPrinter.update(requestedFile, EFileStatus.FAILED);
return true;
}
break;
}
catch (NumberFormatException nfe){
currSockPW.write(NETPacket.getCode400());
currSockPW.flush();
logPrinter.print("NET: Requested range for "+requestedFile.getName()+" has incorrect format. Returning 400\n\t"+nfe.getMessage(), EMsgType.FAIL);
logPrinter.update(requestedFile, EFileStatus.FAILED);
return true;
}
}
parseGETrange(requestedFile, reqFileName, reqFileSize, line);
return;
}
}
return false;
}
private void parseGETrange(File file, String fileName, long fileSize, String rangeDirective) throws Exception{
try {
String[] rangeStr = rangeDirective.toLowerCase().replaceAll("^range:\\s+?bytes=", "").split("-", 2);
if (! rangeStr[0].isEmpty() && ! rangeStr[1].isEmpty()) { // If both ranges defined: Read requested
long fromRange = Long.parseLong(rangeStr[0]);
long toRange = Long.parseLong(rangeStr[1]);
if (fromRange > toRange){ // If start bytes greater then end bytes
writeToSocket(NETPacket.getCode400());
logPrinter.print("Requested range for "
+ file.getName()
+ " is incorrect. Returning 400", EMsgType.FAIL);
logPrinter.update(file, EFileStatus.FAILED);
return;
}
writeToSocket(fileName, fromRange, toRange);
return;
}
if (! rangeStr[0].isEmpty()) { // If only START defined: Read all
writeToSocket(fileName, Long.parseLong(rangeStr[0]), fileSize);
return;
}
if (rangeStr[1].isEmpty()) { // If Range not defined: like "Range: bytes=-"
writeToSocket(NETPacket.getCode400());
logPrinter.print("Requested range for "
+ file.getName()
+ " is incorrect (empty start & end). Returning 400", EMsgType.FAIL);
logPrinter.update(file, EFileStatus.FAILED);
return;
}
if (fileSize > 500){
writeToSocket(fileName, fileSize - 500, fileSize);
return;
}
// If file smaller than 500 bytes
writeToSocket(NETPacket.getCode416());
logPrinter.print("File size requested for "
+ file.getName()
+ " while actual size of it: "
+ fileSize+". Returning 416", EMsgType.FAIL);
logPrinter.update(file, EFileStatus.FAILED);
}
catch (NumberFormatException nfe){
writeToSocket(NETPacket.getCode400());
logPrinter.print("Requested range for "
+ file.getName()
+ " has incorrect format. Returning 400\n\t"
+ nfe.getMessage(), EMsgType.FAIL);
logPrinter.update(file, EFileStatus.FAILED);
}
}
private void writeToSocket(String string) {
currSockPW.write(string);
currSockPW.flush();
}
/**
* Send files.
* */
private boolean writeToSocket(String fileName, long start, long end){
File reqFile = nspMap.get(fileName);
// Inform
logPrinter.print("NET: Responding to requested range: "+start+"-"+end, EMsgType.INFO);
// Reply
currSockPW.write(NETPacket.getCode206(nspFileSizes.get(fileName), start, end));
currSockPW.flush();
// Prepare transfer
private void writeToSocket(String fileName, long start, long end) throws Exception{
File file = files.get(fileName).getFile();
logPrinter.print("Reply to range: "+start+"-"+end, EMsgType.INFO);
writeToSocket(NETPacket.getCode206(files.get(fileName).getSize(), start, end));
try{
if (file.isDirectory())
handleSplitFile(file, start, end);
else
handleRegularFile(file, start, end);
logPrinter.updateProgress(1.0);
}
catch (Exception e){
logPrinter.update(file, EFileStatus.FAILED);
throw new Exception("File transmission failed:\n "+e.getMessage());
}
}
private void handleSplitFile(File file, long start, long end) throws Exception{
long count = end - start + 1;
int readPice = 8388608; // = 8Mb
int readPice = 8388608;
byte[] byteBuf;
long currentOffset = 0;
try{
//================================= SPLIT FILE ====================================
if (reqFile.isDirectory()){
NSSplitReader nsr = new NSSplitReader(reqFile, start);
NSSplitReader nsr = new NSSplitReader(file, start);
while (currentOffset < count){
if (isCancelled())
return true;
if ((currentOffset + readPice) >= count){
readPice = Math.toIntExact(count - currentOffset);
}
byteBuf = new byte[readPice];
if (nsr.read(byteBuf) != readPice){
logPrinter.print("NET: Reading of file stream suddenly ended.", EMsgType.FAIL);
return true;
}
currSockOS.write(byteBuf);
//-------/
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
//-------/
currentOffset += readPice;
}
currSockOS.flush(); // TODO: check if this really needed.
nsr.close();
while (currentOffset < count){
if ((currentOffset + readPice) >= count){
readPice = Math.toIntExact(count - currentOffset);
}
//================================= REGULAR FILE ====================================
else {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(reqFile));
byteBuf = new byte[readPice];
if (bis.skip(start) != start){
logPrinter.print("NET: Unable to skip requested range.", EMsgType.FAIL);
logPrinter.update(reqFile, EFileStatus.FAILED);
return true;
}
if (nsr.read(byteBuf) != readPice)
throw new IOException("File stream suddenly ended.");
while (currentOffset < count){
if (isCancelled())
return true;
if ((currentOffset + readPice) >= count){
readPice = Math.toIntExact(count - currentOffset);
}
byteBuf = new byte[readPice];
currSockOS.write(byteBuf);
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
if (bis.read(byteBuf) != readPice){
logPrinter.print("NET: Reading of file stream suddenly ended.", EMsgType.FAIL);
return true;
}
currSockOS.write(byteBuf);
//-------/
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
//-------/
currentOffset += readPice;
}
currSockOS.flush(); // TODO: check if this really needed.
bis.close();
currentOffset += readPice;
}
currSockOS.flush(); // TODO: check if this really needed.
nsr.close();
}
private void handleRegularFile(File file, long start, long end) throws Exception{
long count = end - start + 1;
int readPice = 8388608;
byte[] byteBuf;
long currentOffset = 0;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
if (bis.skip(start) != start)
throw new IOException("Unable to skip requested range.");
while (currentOffset < count){
if ((currentOffset + readPice) >= count){
readPice = Math.toIntExact(count - currentOffset);
}
//-------/
logPrinter.updateProgress(1.0);
//-------/
byteBuf = new byte[readPice];
if (bis.read(byteBuf) != readPice){
throw new IOException("File stream suddenly ended.");
}
currSockOS.write(byteBuf);
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
currentOffset += readPice;
}
catch (IOException | NullPointerException e){
logPrinter.print("NET: File transmission failed. Returned:\n\t"+e.getMessage(), EMsgType.FAIL);
logPrinter.update(reqFile, EFileStatus.FAILED);
return true;
}
return false;
currSockOS.flush(); // TODO: check if this really needed.
bis.close();
}
public ServerSocket getServerSocket(){
return serverSocket;
}
public Socket getClientSocket(){
return clientSocket;
}
/**
* Close when done
* */
private void close(EFileStatus status){
if (isCancelled())
logPrinter.print("NET: Interrupted by user.", EMsgType.INFO);
try {
if (serverSocket != null) {
if (serverSocket != null && ! serverSocket.isClosed()) {
serverSocket.close();
logPrinter.print("NET: Closing server socket.", EMsgType.PASS);
logPrinter.print("Closing server socket.", EMsgType.PASS);
}
}
catch (IOException ioe){
logPrinter.print("NET: Closing server socket failed. Sometimes it's not an issue.", EMsgType.WARNING);
}
if (status != null) {
logPrinter.update(nspMap, status);
logPrinter.print("Closing server socket failed. Sometimes it's not an issue.", EMsgType.WARNING);
}
HashMap<String, File> tempMap = new HashMap<>();
for (UniFile sf : files.values())
tempMap.put(sf.getFile().getName(), sf.getFile());
logPrinter.update(tempMap, status);
logPrinter.print("\tEnd chain", EMsgType.INFO);
logPrinter.close();
}

View file

@ -0,0 +1,205 @@
/*
Copyright 2019-2020 Dmitry Isaenko
This file is part of NS-USBloader.
NS-USBloader 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.
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
*/
package nsusbloader.COM.NET;
import nsusbloader.ModelControllers.ILogPrinter;
import nsusbloader.NSLDataTypes.EMsgType;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.*;
import java.util.*;
public class NetworkSetupValidator {
private String hostIP;
private int hostPort;
private final HashMap<String, UniFile> files;
private ServerSocket serverSocket;
private final boolean valid;
private final ILogPrinter logPrinter;
private final boolean doNotServe;
NetworkSetupValidator(List<File> filesList,
boolean doNotServe,
String hostIP,
String hostPortNum,
ILogPrinter logPrinter) {
this.files = new HashMap<>();
this.logPrinter = logPrinter;
this.doNotServe = doNotServe;
try {
validateFiles(filesList);
encodeAndAddFilesToMap(filesList);
resolveIp(hostIP);
resolvePort(hostPortNum);
}
catch (Exception e){
logPrinter.print(e.getMessage(), EMsgType.FAIL);
valid = false;
return;
}
valid = true;
}
private void validateFiles(List<File> filesList) {
filesList.removeIf(f -> {
if (f.isFile())
return false;
File[] subFiles = f.listFiles((file, name) -> name.matches("[0-9]{2}"));
if (subFiles == null || subFiles.length == 0) {
logPrinter.print("NET: Exclude folder: " + f.getName(), EMsgType.WARNING);
return true;
}
Arrays.sort(subFiles, Comparator.comparingInt(file -> Integer.parseInt(file.getName())));
for (int i = subFiles.length - 2; i > 0 ; i--){
if (subFiles[i].length() < subFiles[i-1].length()) {
logPrinter.print("NET: Exclude split file: "+f.getName()+
"\n Chunk sizes of the split file are not the same, but has to be.", EMsgType.WARNING);
return true;
}
}
return false;
});
}
private void encodeAndAddFilesToMap(List<File> filesList) throws UnsupportedEncodingException, FileNotFoundException {
for (File file : filesList){
String encodedName = URLEncoder.encode(file.getName(), "UTF-8").replaceAll("\\+", "%20"); // replace '+' to '%20'
UniFile uniFile = new UniFile(file);
files.put(encodedName, uniFile);
}
if (files.size() == 0) {
throw new FileNotFoundException("NET: No files to send.");
}
}
private void resolveIp(String hostIPaddr) throws IOException{
if (! hostIPaddr.isEmpty()){
this.hostIP = hostIPaddr;
logPrinter.print("NET: Host IP defined as: " + hostIP, EMsgType.PASS);
return;
}
if (findIpUsingHost("google.com"))
return;
if (findIpUsingHost("people.com.cn"))
return;
throw new IOException("Try using 'Expert mode' and set IP manually. " + getAvaliableIpExamples());
}
private boolean findIpUsingHost(String host) {
try {
Socket scoketK;
scoketK = new Socket();
scoketK.connect(new InetSocketAddress(host, 80));
hostIP = scoketK.getLocalAddress().getHostAddress();
scoketK.close();
logPrinter.print("NET: Host IP detected as: " + hostIP, EMsgType.PASS);
return true;
}
catch (IOException e){
logPrinter.print("NET: Can't get your computer IP using "
+ host
+ " server (InetSocketAddress). Returned:\n\t"+e.getMessage(), EMsgType.INFO);
return false;
}
}
private String getAvaliableIpExamples(){
try {
StringBuilder builder = new StringBuilder("Check for:\n");
Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
while (enumeration.hasMoreElements()) {
NetworkInterface n = enumeration.nextElement();
Enumeration<InetAddress> enumeration1 = n.getInetAddresses();
while (enumeration1.hasMoreElements()){
builder.append("- ");
builder.append(enumeration1.nextElement().getHostAddress());
builder.append("\n");
}
}
return builder.toString();
}
catch (SocketException socketException) {
return "";
}
}
private void resolvePort(String hostPortNum) throws Exception{
if (! hostPortNum.isEmpty()) {
parsePort(hostPortNum);
return;
}
if (doNotServe)
throw new Exception("NET: Port must be defined if 'Don't serve requests' option selected!");
findPort();
}
private void findPort() throws Exception{
Random portRandomizer = new Random();
for (int i = 0; i < 5; i++) {
try {
this.hostPort = portRandomizer.nextInt(999) + 6000;
serverSocket = new ServerSocket(hostPort); //System.out.println(serverSocket.getInetAddress()); 0.0.0.0
logPrinter.print("NET: Your port detected as: " + hostPort, EMsgType.PASS);
break;
}
catch (IOException ioe) {
if (i == 4) {
throw new Exception("NET: Can't find good port\n"
+ "Set port by in settings ('Expert mode').");
}
logPrinter.print("NET: Can't use port " + hostPort + "\nLooking for another one.", EMsgType.WARNING);
}
}
}
private void parsePort(String hostPortNum) throws Exception{
try {
this.hostPort = Integer.parseInt(hostPortNum);
serverSocket = new ServerSocket(hostPort);
logPrinter.print("NET: Using defined port number: " + hostPort, EMsgType.PASS);
}
catch (IllegalArgumentException | IOException eee){
throw new Exception("NET: Can't use port defined in settings: " + hostPortNum + "\n\t"+eee.getMessage());
}
}
String getHostIP() { return hostIP; }
int getHostPort() { return hostPort; }
HashMap<String, UniFile> getFiles() { return files; }
ServerSocket getServerSocket() { return serverSocket; }
boolean isValid() { return valid; }
}

View file

@ -0,0 +1,44 @@
/*
Copyright 2019-2020 Dmitry Isaenko
This file is part of NS-USBloader.
NS-USBloader 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.
NS-USBloader 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 NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
*/
package nsusbloader.COM.NET;
import java.io.File;
class UniFile {
private final long size;
private final File file;
UniFile(File file) {
this.file = file;
if (file.isFile()) {
size = file.length();
}
else {
long fSize = 0;
File[] subFiles = file.listFiles((myFile, name) -> name.matches("[0-9]{2}"));
for (File subFile : subFiles)
fSize += subFile.length();
size = fSize;
}
}
public long getSize() { return size; }
public File getFile() { return file; }
}