/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.kanishka.virustotalv2;
import com.google.gson.Gson;
import com.kanishka.net.commons.BasicHTTPRequestImpl;
import com.kanishka.net.commons.HTTPRequest;
import com.kanishka.net.exception.RequestNotComplete;
import com.kanishka.net.model.MultiPartEntity;
import com.kanishka.net.model.RequestMethod;
import com.kanishka.net.model.Response;
import com.kanishka.virustotal.dto.DomainReport;
import com.kanishka.virustotal.dto.FileScanReport;
import com.kanishka.virustotal.dto.GeneralResponse;
import com.kanishka.virustotal.dto.IPAddressReport;
import com.kanishka.virustotal.dto.ScanInfo;
import com.kanishka.virustotal.exception.APIKeyNotFoundException;
import com.kanishka.virustotal.exception.InvalidArguentsException;
import com.kanishka.virustotal.exception.QuotaExceededException;
import com.kanishka.virustotal.exception.UnauthorizedAccessException;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author kdkanishka@gmail.com
*/
public class VirustotalPublicV2Impl implements VirustotalPublicV2 {
private Gson gsonProcessor;
private String apiKey;
private static final String API_KEY_FIELD = "apikey";
private static final String RESOURCE_FIELD = "resource";
private static final String ERR_MSG_EXCEED_MAX_REQ_PM = "Exceeded maximum" +
" number of requests per minute, Please try again later.";
private static final String ERR_MSG_INVALID_API_KEY = "Invalid api key";
private static final String ERR_MSG_API_KEY_NOT_FOUND =
"API Key is not set. Please set api key.\nSample :" +
" VirusTotalConfig.getConfigInstance()." +
"setVirusTotalAPIKey(\"APIKEY\")";
private static final String ERR_MSG_FILE_NOT_FOUND =
"Could not access file, either the file may not exists or not" +
" accessible!";
private static final String ERR_MSG_INCORRECT_PARAM =
"Incorrect parameter \'%s\', resource should be an array" +
" with at least one element";
private static final String ERR_MSG2_INCORRECT_PARAM =
"Incorrect parameter \'%s\' , " +
"maximum number(%d) of %s per request has been exceeded.";
private static final String ERR_MSG3_INCORRECT_PARAM =
"Incorrect parameter '%s', it should be a valid %s";
private static final String ERR_COMMENTING = "Could not publish the " +
"comment," +
" API error occured!";
public static final String URLS_LITERAL = "urls";
private HTTPRequest httpRequestObject;
public VirustotalPublicV2Impl() throws APIKeyNotFoundException {
initialize();
httpRequestObject = new BasicHTTPRequestImpl();
}
public VirustotalPublicV2Impl(HTTPRequest httpRequestObject) throws
APIKeyNotFoundException {
initialize();
this.httpRequestObject = httpRequestObject;
}
private void initialize() throws APIKeyNotFoundException {
gsonProcessor = new Gson();
apiKey = VirusTotalConfig.getConfigInstance().getVirusTotalAPIKey();
if (apiKey == null || apiKey.length() == 0) {
throw new APIKeyNotFoundException(ERR_MSG_API_KEY_NOT_FOUND);
}
}
@Override
public ScanInfo scanFile(File fileToScan) throws
IOException, UnauthorizedAccessException,
QuotaExceededException {
if (!fileToScan.canRead()) {
throw new FileNotFoundException(ERR_MSG_FILE_NOT_FOUND);
}
Response responseWrapper = new Response();
ScanInfo scanInfo = new ScanInfo();
FileBody fileBody = new FileBody(fileToScan);
MultiPartEntity file = new MultiPartEntity("file", fileBody);
MultiPartEntity apikey = new MultiPartEntity(API_KEY_FIELD,
new StringBody(apiKey));
List<MultiPartEntity> multiParts = new ArrayList<MultiPartEntity>();
multiParts.add(file);
multiParts.add(apikey);
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(URI_VT2_FILE_SCAN,
null, null, RequestMethod.GET, multiParts);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
scanInfo = gsonProcessor.fromJson(serviceResponse, ScanInfo.class);
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return scanInfo;
}
@Override
public ScanInfo[] reScanFiles(String[] resources) throws
IOException, UnauthorizedAccessException,
InvalidArguentsException, QuotaExceededException {
ScanInfo[] scanInfo = null;
if (resources == null) {
String errorMsg = String.format(ERR_MSG_INCORRECT_PARAM,
"resources");
throw new InvalidArguentsException(errorMsg);
}
Response responseWrapper = new Response();
MultiPartEntity apikey = new MultiPartEntity(API_KEY_FIELD,
new StringBody(apiKey));
StringBuilder resourceStr = new StringBuilder();
for (String resource : resources) {
resourceStr.append(resource).append(", ");
}
//clean up resource string
int lastCommaIdx = resourceStr.lastIndexOf(",");
if (lastCommaIdx > 0) {
resourceStr.deleteCharAt(lastCommaIdx);
}
MultiPartEntity part = new MultiPartEntity(RESOURCE_FIELD,
new StringBody(resourceStr.toString()));
List<MultiPartEntity> multiParts = new ArrayList<MultiPartEntity>();
multiParts.add(part);
multiParts.add(apikey);
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(URI_VT2_RESCAN,
null, null, RequestMethod.POST, multiParts);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
if (resources.length > 1) {
scanInfo =
gsonProcessor.fromJson(serviceResponse,
ScanInfo[].class);
} else {
ScanInfo scanInfo1Elem = gsonProcessor.fromJson(serviceResponse,
ScanInfo.class);
scanInfo = new ScanInfo[]{scanInfo1Elem};
}
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return scanInfo;
}
@Override
public FileScanReport getScanReport(String resource)
throws IOException, UnauthorizedAccessException,
QuotaExceededException {
Response responseWrapper = new Response();
FileScanReport fileScanReport = new FileScanReport();
MultiPartEntity apikey = new MultiPartEntity(API_KEY_FIELD,
new StringBody(apiKey));
MultiPartEntity resourcePart = new MultiPartEntity(RESOURCE_FIELD,
new StringBody(resource));
List<MultiPartEntity> multiParts = new ArrayList<MultiPartEntity>();
multiParts.add(apikey);
multiParts.add(resourcePart);
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(URI_VT2_FILE_SCAN_REPORT
, null, null, RequestMethod.POST, multiParts);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
fileScanReport = gsonProcessor.fromJson(serviceResponse,
FileScanReport.class);
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return fileScanReport;
}
@Override
public FileScanReport[] getScanReports(String[] resources) throws
IOException, UnauthorizedAccessException,
QuotaExceededException, InvalidArguentsException {
Response responseWrapper = new Response();
FileScanReport[] fileScanReport = null;
if (resources == null) {
String errorMsg = String.format(ERR_MSG_INCORRECT_PARAM,
"resources");
throw new InvalidArguentsException(errorMsg);
}
MultiPartEntity apikey = new MultiPartEntity(API_KEY_FIELD,
new StringBody(apiKey));
StringBuilder resourceStr = new StringBuilder();
for (String resource : resources) {
resourceStr.append(resource).append(", ");
}
//clean up resource string
int lastCommaIdx = resourceStr.lastIndexOf(",");
if (lastCommaIdx > 0) {
resourceStr.deleteCharAt(lastCommaIdx);
}
MultiPartEntity part = new MultiPartEntity(RESOURCE_FIELD,
new StringBody(resourceStr.toString()));
List<MultiPartEntity> multiParts = new ArrayList<MultiPartEntity>();
multiParts.add(apikey);
multiParts.add(part);
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(URI_VT2_FILE_SCAN_REPORT
, null, null, RequestMethod.POST, multiParts);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
if (resources.length > 1) {
fileScanReport = gsonProcessor.fromJson(serviceResponse,
FileScanReport[].class);
} else {
FileScanReport fScanRep =
gsonProcessor.fromJson(serviceResponse,
FileScanReport.class);
fileScanReport = new FileScanReport[]{fScanRep};
}
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return fileScanReport;
}
@Override
public ScanInfo[] scanUrls(String[] urls) throws IOException,
UnauthorizedAccessException, QuotaExceededException,
InvalidArguentsException {
Response responseWrapper = new Response();
ScanInfo[] scanInfo = null;
if (urls == null) {
String errorMsg = String.format(ERR_MSG_INCORRECT_PARAM, URLS_LITERAL);
throw new InvalidArguentsException(errorMsg);
} else if (urls.length > VT2_MAX_ALLOWED_URLS_PER_REQUEST) {
String errMsg = String.format(ERR_MSG2_INCORRECT_PARAM, URLS_LITERAL,
VT2_MAX_ALLOWED_URLS_PER_REQUEST, URLS_LITERAL);
throw new InvalidArguentsException(errMsg);
}
MultiPartEntity apikey = new MultiPartEntity(API_KEY_FIELD,
new StringBody(apiKey));
StringBuilder resourceStr = new StringBuilder();
for (String url : urls) {
resourceStr.append(url).append(VT2_URLSEPERATOR);
}
//clean up resource string
int lastUrlSepIdx = resourceStr.lastIndexOf(VT2_URLSEPERATOR);
if (lastUrlSepIdx > 0) {
resourceStr.deleteCharAt(lastUrlSepIdx);
}
MultiPartEntity part = new MultiPartEntity("url",
new StringBody(resourceStr.toString()));
List<MultiPartEntity> multiParts = new ArrayList<MultiPartEntity>();
multiParts.add(apikey);
multiParts.add(part);
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(URI_VT2_URL_SCAN, null,
null, RequestMethod.POST, multiParts);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
if (urls.length > 1) {
scanInfo = gsonProcessor.fromJson(serviceResponse,
ScanInfo[].class);
} else {
ScanInfo scanInforElem = gsonProcessor.fromJson(serviceResponse,
ScanInfo.class);
scanInfo = new ScanInfo[]{scanInforElem};
}
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return scanInfo;
}
@Override
public FileScanReport[] getUrlScanReport(String[] urls, boolean scan) throws
IOException, UnauthorizedAccessException,
QuotaExceededException, InvalidArguentsException {
Response responseWrapper = new Response();
FileScanReport[] fileScanReport = null;
if (urls == null) {
String errMsg = String.format(ERR_MSG_INCORRECT_PARAM, "resources");
throw new InvalidArguentsException(errMsg);
} else if (urls.length > VT2_MAX_ALLOWED_URLS_PER_REQUEST) {
String errMsg = String.format(ERR_MSG2_INCORRECT_PARAM,
"urls", VT2_MAX_ALLOWED_URLS_PER_REQUEST, "urls");
throw new InvalidArguentsException(errMsg);
}
MultiPartEntity apikey = new MultiPartEntity(API_KEY_FIELD,
new StringBody(apiKey));
StringBuilder resourceStr = new StringBuilder();
for (String resource : urls) {
resourceStr.append(resource).append("\n");
}
//clean up resource string
int lastCommaIdx = resourceStr.lastIndexOf("\n");
if (lastCommaIdx > 0) {
resourceStr.deleteCharAt(lastCommaIdx);
}
MultiPartEntity part = new MultiPartEntity(RESOURCE_FIELD,
new StringBody(resourceStr.toString()));
List<MultiPartEntity> multiParts = new ArrayList<MultiPartEntity>();
multiParts.add(apikey);
multiParts.add(part);
if (scan) {
MultiPartEntity scanPart =
new MultiPartEntity("scan", new StringBody("1"));
multiParts.add(scanPart);
}
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(URI_VT2_URL_SCAN_REPORT,
null, null, RequestMethod.POST, multiParts);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
if (urls.length > 1) {
fileScanReport = gsonProcessor.fromJson(serviceResponse,
FileScanReport[].class);
} else {
FileScanReport fileScanReportElem =
gsonProcessor.fromJson(serviceResponse,
FileScanReport.class);
fileScanReport = new FileScanReport[]{fileScanReportElem};
}
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return fileScanReport;
}
@Override
public IPAddressReport getIPAddresReport(String ipAddress) throws
InvalidArguentsException, QuotaExceededException,
UnauthorizedAccessException, IOException {
Response responseWrapper = new Response();
IPAddressReport ipReport = new IPAddressReport();
if (ipAddress == null) {
String errMsg = String.format(ERR_MSG3_INCORRECT_PARAM,
"ipAddress", "IP address");
throw new InvalidArguentsException(errMsg);
}
String uriWithParams = URI_VT2_IP_REPORT + "?apikey=" + apiKey + "&ip="
+ ipAddress;
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(uriWithParams, null,
null, RequestMethod.GET, null);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
ipReport = gsonProcessor.fromJson(serviceResponse,
IPAddressReport.class);
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return ipReport;
}
@Override
public DomainReport getDomainReport(String domain) throws
InvalidArguentsException, UnauthorizedAccessException,
QuotaExceededException, IOException {
Response responseWrapper = new Response();
DomainReport domainReport = new DomainReport();
if (domain == null) {
String errMsg = String.format(ERR_MSG3_INCORRECT_PARAM, "domain",
"domain name");
throw new InvalidArguentsException(errMsg);
}
String uriWithParams = URI_VT2_DOMAIN_REPORT + "?apikey=" +
apiKey + "&domain=" + domain;
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(uriWithParams, null,
null, RequestMethod.GET, null);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
domainReport = gsonProcessor.fromJson(serviceResponse,
DomainReport.class);
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return domainReport;
}
@Override
public GeneralResponse makeAComment(String resource, String comment) throws
IOException, UnauthorizedAccessException,
InvalidArguentsException, QuotaExceededException {
if (resource == null || resource.length() == 0) {
String errMsg = String.format(ERR_MSG3_INCORRECT_PARAM,
"resource", "string representing a hash value (md2,sha1," +
"sha256)");
throw new InvalidArguentsException(errMsg);
}
Response responseWrapper = new Response();
GeneralResponse generalResponse = new GeneralResponse();
generalResponse.setResponseCode(-1);
generalResponse.setVerboseMessage(ERR_COMMENTING);
MultiPartEntity apikey = new MultiPartEntity(API_KEY_FIELD,
new StringBody(apiKey));
MultiPartEntity resourcePart = new MultiPartEntity(RESOURCE_FIELD,
new StringBody(resource));
MultiPartEntity commentPart = new MultiPartEntity("comment",
new StringBody(comment));
List<MultiPartEntity> multiParts = new ArrayList<MultiPartEntity>();
multiParts.add(apikey);
multiParts.add(resourcePart);
multiParts.add(commentPart);
Integer statusCode = -1;
try {
responseWrapper = httpRequestObject.request(URI_VT2_PUT_COMMENT,
null, null, RequestMethod.POST, multiParts);
statusCode = responseWrapper.getStatus();
} catch (RequestNotComplete e) {
statusCode = e.getHttpStatus().getStatusCode();
if (statusCode == VirustotalStatus.FORBIDDEN) {
//fobidden
throw new UnauthorizedAccessException(ERR_MSG_INVALID_API_KEY,
e);
}
}
if (statusCode == VirustotalStatus.SUCCESSFUL) {
//valid response
String serviceResponse = responseWrapper.getResponse();
generalResponse = gsonProcessor.fromJson(serviceResponse,
GeneralResponse.class);
} else if (statusCode == VirustotalStatus.API_LIMIT_EXCEEDED) {
//limit exceeded
throw new QuotaExceededException(ERR_MSG_EXCEED_MAX_REQ_PM);
}
return generalResponse;
}
}