package art.servers.gost.access.controller; import art.library.interop.serialization.Serialization; import art.library.interop.serialization.SerializationException; import art.library.model.devices.Device; import art.library.model.devices.gost.access.AccessEnforcement; import art.library.model.devices.gost.access.AccessEnforcementInformation; import art.library.model.devices.gost.access.types.AccessEnforcement_Detection; import art.library.model.devices.gost.access.types.AccessEnforcement_Detection_Image; import art.library.model.devices.gost.access.types.AccessEnforcement_Detection_State; import art.library.model.devices.gost.access.types.AccessEnforcement_Detection_State_Discarded; import art.library.model.devices.gost.access.types.AccessEnforcement_Detection_Summary; import art.library.model.devices.gost.types.permissions.Access_Contract; import art.library.model.devices.gost.types.permissions.List_Detections; import art.library.utils.licence.Licence; import art.servers.ServerException; import art.servers.gost.access.Shared; import art.servers.gost.access.configuration.Configuration; import art.servers.gost.access.configuration.ConfigurationDetail; import art.servers.gost.access.types.DatabasePoolConnection; import art.servers.gost.access.utils.ZIP; import art.servers.types.HttpAuthentication; import java.io.ByteArrayOutputStream; import java.io.File; import static java.lang.Thread.sleep; import java.nio.file.Files; import java.security.MessageDigest; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import javax.xml.bind.DatatypeConverter; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.FileHeader; import org.postgresql.util.PGobject; public class Controller_Detections extends Controller { protected String name = null; private ConfigurationDetail configuration = null; public Controller_Detections(DatabasePoolConnection database) { this.database = database; this.name = Shared.getMessage("Controller detections"); this.setName(this.name); this.configuration = ((Configuration)Shared.configuration).detail; } public void run() { Shared.traceInformation(name, Shared.getMessage("Starting")); while (isInterrupted() == false) { long lastTimestampUpdate = System.currentTimeMillis(); try { if (Shared.isServerEnabled() == true) { update(); } } catch (Exception exception) { } long polling = 3600000; long timetowait = (polling * 1000) - (System.currentTimeMillis() - lastTimestampUpdate); timetowait = Math.min(timetowait, polling * 1000); if (timetowait > 0) { try { sleep(timetowait); } catch (Exception e) { } } } Shared.traceInformation(name, Shared.getMessage("Finishing")); } private void update() { } public void listDetections(HttpAuthentication authentication, String language, List_Detections result) throws SerializationException, Exception { Connection connection = null; PreparedStatement statement = null; ResultSet resultset = null; try { // Shared.println(this.name, "1.ListDetections"); result.accessDetectionsSummary = new ArrayList(); if (result.limit == null) result.limit = 10000; if (result.offset == null) result.offset = 0; if (result.limit > 10000) throw new SerializationException(language, "Limit value too high"); result.total = 0L; result.pages = 0; result.page = 0; // Shared.println(this.name, "2.ListDetections"); connection = database.getConnection(true); // Shared.println(this.name, "3.ListDetections"); String command = "SELECT number, datetime, device, plate, state, record, count(*) OVER() AS total " + "FROM detections " + "WHERE"; command = command + " AND type='" + AccessEnforcement.class.getName() + "'"; if (result.filter != null) { String filter = result.filter; filter = filter.replaceAll("state IN", "state="); command = command + " AND " + filter; } if (result.order != null) command = command + " ORDER BY " + result.order; else command = command + " ORDER BY number ASC"; if (result.offset != null) command = command + " OFFSET " + result.offset; if (result.limit != null) command = command + " LIMIT " + result.limit; command = command.replace("WHERE AND", "WHERE"); command = command.replace("WHERE ORDER", "ORDER"); // Shared.println(this.name, "4.ListDetections"); statement = connection.prepareStatement(command); // Shared.println(getName(), statement.toString()); resultset = statement.executeQuery(); Map map = new HashMap(); // Shared.println(this.name, "5.ListDetections"); while (resultset.next()) { AccessEnforcement_Detection_Summary summary = new AccessEnforcement_Detection_Summary(); summary.number = resultset.getLong(1); summary.timestamp = resultset.getTimestamp(2).getTime(); summary.deviceIdentifier = resultset.getString(3); { AccessEnforcement device = getAccessEnforcement(summary.deviceIdentifier, map); if (device != null) summary.deviceName = device.information.name; } summary.plate = resultset.getString(4); summary.state = resultset.getInt(5); summary.stateName = AccessEnforcement_Detection_State.getDescription(language, summary.state); summary.record = resultset.getString(6); try{summary.numberViolations = getNumberViolationsPlate(summary.plate);} catch (Exception e){}; result.accessDetectionsSummary.add(summary); if (result.total == 0) { result.total = resultset.getLong(7); result.pages = (int) Math.ceil((float)result.total / (float) result.limit); result.page = (int) Math.floor((float)result.offset / (float) result.limit); } } // Shared.println(this.name, "END.ListDetections: " + result.total); } catch (Exception exception) { // Shared.printstack(this.name, exception); Shared.traceError(name, Shared.getMessage(language, "List detections"), exception, authentication, Shared.getLanguage()); // Shared.println(this.name, "EX.ListDetections: " + exception.toString()); throw exception; } finally { database.releaseConnection(connection, statement, resultset); // Shared.println(this.name, "END.ListDetections RELEASED"); } } public Access_Contract hasPermission(AccessEnforcement_Detection detection, String plate) throws Exception { return hasPermission(detection.timestamp, detection.device.getIdentifier(), plate); } public Access_Contract hasPermission(long timestamp, String device, String plate) throws Exception { Connection connection = null; PreparedStatement statement = null; ResultSet resultset = null; try { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(timestamp); int year = calendar.get(Calendar.YEAR); int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR); int minuteDay = (calendar.get(Calendar.HOUR_OF_DAY) * 60) + calendar.get(Calendar.MINUTE); plate = plate.toUpperCase(); connection = database.getConnection(true); String command = "SELECT C.value, T.minutes " + "FROM contracts C, timetables T, calendars_timetables CT " + "WHERE C.commencement < ? AND C.expiration > ? AND C.device = ? " + "AND C.calendar = CT.calendar AND CT.timetable = T.identifier AND CT.year = ? " + "AND CT.day = ? AND CT.timetable = T.identifier AND (C.plate = ? OR C.plate = ?)"; statement = connection.prepareStatement(command); statement.setTimestamp(1, new Timestamp(timestamp)); statement.setTimestamp(2, new Timestamp(timestamp)); statement.setString(3, device); statement.setInt(4, year); statement.setInt(5, dayOfYear); statement.setString(6, plate.trim()); statement.setString(7, "*"); // Shared.println(getName(), statement.toString()); resultset = statement.executeQuery(); while (resultset.next()) { Access_Contract contract = Serialization.deserialize(Access_Contract.class, resultset.getString(1)); Integer[] minutes = (Integer[]) resultset.getArray(2).getArray(); if (minutes[minuteDay] > 0) return contract; } command = "SELECT C.value, T.minutes " + "FROM contracts C, timetables T, calendars_timetables CT " + "WHERE C.commencement < ? AND C.expiration > ? AND C.device = ? " + "AND C.calendar = CT.calendar AND CT.timetable = T.identifier AND CT.year = ? " + "AND CT.day = ? AND CT.timetable = T.identifier AND (C.plate like '%*%')"; statement = connection.prepareStatement(command); statement.setTimestamp(1, new Timestamp(timestamp)); statement.setTimestamp(2, new Timestamp(timestamp)); statement.setString(3, device); statement.setInt(4, year); statement.setInt(5, dayOfYear); // Shared.println(getName(), statement.toString()); resultset = statement.executeQuery(); while (resultset.next()) { Access_Contract contract = Serialization.deserialize(Access_Contract.class, resultset.getString(1)); String contractPlate = contract.vehicle.plate; boolean patternMatch = false; if (contractPlate.startsWith("*")) patternMatch = plate.endsWith(contractPlate.substring(0)); else if (contractPlate.endsWith("*")) patternMatch = plate.startsWith(contractPlate.substring(0, contractPlate.length() - 1)); if(patternMatch) return contract; } return null; } catch (Exception exception) { throw exception; } finally { database.releaseConnection(connection, statement, resultset); } } public void getDetections(HttpAuthentication authentication, String language, List_Detections result) throws SerializationException, Exception { Connection connection = null; PreparedStatement statement = null; ResultSet resultset = null; try { result.accessDetections = new ArrayList(); if (result.limit == null) result.limit = 10000; if (result.offset == null) result.offset = 0; if (result.limit > 10000) throw new SerializationException(language, "Limit value too high"); result.total = 0L; result.pages = 0; result.page = 0; connection = database.getConnection(true); String command = "SELECT number, value, count(*) OVER() AS total " + "FROM detections " + "WHERE"; command = command + " AND type='" + AccessEnforcement.class.getName() + "'"; if (result.filter != null) command = command + " AND " + result.filter; if (result.order != null) command = command + " ORDER BY " + result.order; else command = command + " ORDER BY number ASC"; if (result.offset != null) command = command + " OFFSET " + result.offset; if (result.limit != null) command = command + " LIMIT " + result.limit; command = command.replace("WHERE AND", "WHERE"); command = command.replace("WHERE ORDER", "ORDER"); statement = connection.prepareStatement(command); resultset = statement.executeQuery(); while (resultset.next()) { AccessEnforcement_Detection detection = new AccessEnforcement_Detection(); detection = Serialization.deserialize(AccessEnforcement_Detection.class, resultset.getString(2)); detection.number = resultset.getLong(1); try{detection.numberViolations = getNumberViolationsPlate(detection.getLastState().vehicle.plate);} catch (Exception e){}; result.accessDetections.add(detection); if (result.total == 0) { result.total = resultset.getLong(3); result.pages = (int) Math.ceil((float)result.total / (float) result.limit); result.page = (int) Math.floor((float)result.offset / (float) result.limit); } } } catch (Exception exception) { Shared.traceError(name, Shared.getMessage(language, "Get detections"), exception, authentication, Shared.getLanguage()); throw exception; } finally { database.releaseConnection(connection, statement, resultset); } } public AccessEnforcement_Detection getDetection(long detectionNumber, boolean images) throws ServerException { try { Connection connection = null; PreparedStatement statement = null; ResultSet resultset = null; try { connection = database.getConnection(true); statement = connection.prepareStatement("SELECT value FROM detections WHERE number = ?"); statement.setLong(1, detectionNumber); resultset = statement.executeQuery(); if (resultset.next() == true) { AccessEnforcement_Detection detection = Serialization.deserialize(AccessEnforcement_Detection.class, resultset.getString(1)); detection.number = detectionNumber; try{detection.numberViolations = getNumberViolationsPlate(detection.getLastState().vehicle.plate);} catch (Exception e){}; if (images == true) { addImages(detection); } return detection; } throw new Exception(Shared.getMessage("Detection does not exists")); } catch (Exception e) { throw new ServerException(e.toString()); } finally { database.releaseConnection(connection, statement, resultset); } } catch (Exception e) { throw new ServerException(e.toString()); } } public long getLastDetection(AccessEnforcement access) throws ServerException { try { Connection connection = null; PreparedStatement statement = null; ResultSet resultset = null; try { connection = database.getConnection(true); statement = connection.prepareStatement("SELECT value, number FROM detections WHERE device = ? ORDER BY datetime desc LIMIT 1"); statement.setString(1, access.getIdentifier()); resultset = statement.executeQuery(); if (resultset.next() == true) { AccessEnforcement_Detection detection = Serialization.deserialize(AccessEnforcement_Detection.class, resultset.getString(1)); return detection.timestamp; } } catch (Exception e) { } finally { database.releaseConnection(connection, statement, resultset); } } catch (Exception e) { } return(-1); } public AccessEnforcement_Detection getDetection(HttpAuthentication authentication, AccessEnforcement_Detection detection, String language) throws ServerException { try { addImages(detection); String resource = Shared.getMessage(language, "Device") + " = " + String.format("%02d", detection.device.number) + ", " + Shared.getMessage(language, "date") + " = " + Device.getDate(detection.timestamp) + ", " + Shared.getMessage(language, "plate") + " = " + detection.getLastState().vehicle.getPlate(); Shared.traceInformation(this.name, Shared.getMessage("Get detection"), resource, authentication, language); return detection; } catch (Exception e) { throw new ServerException(e.toString()); } } public boolean setDetection(HttpAuthentication authentication, AccessEnforcement_Detection detection, AccessEnforcement_Detection_State detectionState, byte[] selectedImage, String language) throws ServerException, Exception { Connection connection = null; PreparedStatement statement = null; // Update detection try { connection = database.getConnection(true); AccessEnforcementInformation information = getAccessEnforcementInformation(detection.device.getIdentifier()); AccessEnforcement_Detection_State currentDetectionState = detection.getLastState(); detection.images = null; if (detection == null) throw new Exception(Shared.getMessage(language, "Invalid detection")); // Check if state change is allowed if (Serialization.equals(currentDetectionState, detectionState) == false) { boolean correct = ( (currentDetectionState.state == AccessEnforcement_Detection_State.STATE_REVISION_PENDING) && ((detectionState.state == AccessEnforcement_Detection_State.STATE_REVISED)) || (currentDetectionState.state == AccessEnforcement_Detection_State.STATE_REVISION_PENDING) && ((detectionState.state == AccessEnforcement_Detection_State.STATE_DISCARDED)) || (currentDetectionState.state == AccessEnforcement_Detection_State.STATE_REVISED) && ((detectionState.state == AccessEnforcement_Detection_State.STATE_DISCARDED)) || (currentDetectionState.state == AccessEnforcement_Detection_State.STATE_REVISED) && ((detectionState.state == AccessEnforcement_Detection_State.STATE_VALIDATED)) || (currentDetectionState.state == AccessEnforcement_Detection_State.STATE_VALIDATED) && ((detectionState.state == AccessEnforcement_Detection_State.STATE_DISCARDED)) || (currentDetectionState.state == AccessEnforcement_Detection_State.STATE_VALIDATED) && ((detectionState.state == AccessEnforcement_Detection_State.STATE_VALIDATED)) || (currentDetectionState.state == AccessEnforcement_Detection_State.STATE_DISCARDED) && ((detectionState.state == AccessEnforcement_Detection_State.STATE_DISCARDED)) || (currentDetectionState.state == AccessEnforcement_Detection_State.STATE_DISCARDED) && ((detectionState.state == AccessEnforcement_Detection_State.STATE_REVISED)) ); if (correct == false) throw new ServerException(Shared.getMessage(language, "Invalid state")); if (authentication.username == null) throw new ServerException(Shared.getMessage(language, "Invalid username")); } else { return true; } // STATE_REVISION_PENDING -> STATE_REVISED if (detectionState.state == AccessEnforcement_Detection_State.STATE_REVISED) { if (detectionState.revision == null) { throw new ServerException(Shared.getMessage(language, "Invalid state")); } detectionState.revision.user = authentication.username; detectionState.revision.timestamp = System.currentTimeMillis(); } // STATE_REVISED -> STATE_VALIDATED if (detectionState.state == AccessEnforcement_Detection_State.STATE_VALIDATED) { if (detectionState.validation == null) { throw new ServerException(Shared.getMessage(language, "Invalid state")); } if (detectionState.validation.policeLicense == null) { throw new ServerException(Shared.getMessage(language, "Invalid police license")); } detectionState.validation.user = authentication.username; detectionState.validation.timestamp = System.currentTimeMillis(); } // STATE_REVISION_PENDING -> STATE_DISCARDED // STATE_REVISED -> STATE_DISCARDED if (detectionState.state == AccessEnforcement_Detection_State.STATE_DISCARDED) { if (detectionState.discarded == null) { throw new ServerException(Shared.getMessage(language, "Invalid reason")); } detectionState.discarded.codeName = AccessEnforcement_Detection_State_Discarded.getDescription(detectionState.discarded.code); detectionState.discarded.user = authentication.username; detectionState.discarded.timestamp = System.currentTimeMillis(); } detection.states.add(detectionState); // Update detection if (detectionState.vehicle.plate != null) detectionState.vehicle.plate = detectionState.vehicle.plate.replaceAll("\\s+",""); statement = connection.prepareStatement("UPDATE detections SET plate = ?, state = ?, value = ? WHERE number = ?"); statement.setString(1, detectionState.vehicle.getPlate()); statement.setShort(2, detectionState.state); PGobject jsonObject = new PGobject(); jsonObject.setType("json"); jsonObject.setValue(Serialization.toString(detection)); statement.setObject(3, jsonObject); statement.setLong(4, detection.number); statement.executeUpdate(); Shared.traceInformation(this.name, Shared.getMessage("Set detection"), statement.toString(), authentication, language); // Change selected image if ((selectedImage != null) && (selectedImage.length > 0)) { addImages(detection); if (detection.setImage("selected", selectedImage) == true) { // Save images ZIP.zip(detection, Licence.decrypt(information.storage.storagePassword), information.storage.storageFolder); } } return true; } catch (ServerException exception) { Shared.traceError(this.name, Shared.getMessage("Set detection"), exception); throw exception; } catch (Exception exception) { Shared.traceError(this.name, Shared.getMessage("Set detection"), exception); throw exception; } finally { database.releaseConnection(connection, statement); } } private int getNumberViolationsPlate(String plate) throws SerializationException, Exception { Connection connection = null; PreparedStatement statement = null; ResultSet resultset = null; try { connection = database.getConnection(true); String command = "SELECT COUNT(*) " + "FROM detections " + "WHERE plate='" + plate + "' AND type='" + AccessEnforcement.class.getName() + "' AND record is not null"; statement = connection.prepareStatement(command); resultset = statement.executeQuery(); int count = 0; if (resultset.next() == true) { count = resultset.getInt(1); } return(count); } catch (Exception exception) { } finally { database.releaseConnection(connection, statement, resultset); } return(0); } private void addImages (AccessEnforcement_Detection detection) { try { SimpleDateFormat formatoImagen1 = new SimpleDateFormat(Shared.getMessage("yyyy/MM/dd")); SimpleDateFormat formatoImagen2 = new SimpleDateFormat(Shared.getMessage("yyyyMMddHHmmssSSS")); AccessEnforcement device = (AccessEnforcement)Shared.model.getDevice(detection.device.getIdentifier()); AccessEnforcementInformation information = device.getDeviceInformation(); String path = information.storage.storageFolder; File fpathImage = new File(path + "/" + formatoImagen1.format(detection.timestamp)); File imagezip = new File(fpathImage.getAbsolutePath() + "/" + formatoImagen2.format(detection.timestamp) + ".zip"); File imagesha = new File(fpathImage.getAbsolutePath() + "/" + formatoImagen2.format(detection.timestamp) + ".sha"); // Check first of all if images are in server disk // If there are not images then request to Erm and save to server disk if ((imagezip.exists() == true) && (imagesha.exists() == true)) { // Unzip file, fill images to received detection MessageDigest messageDigest = MessageDigest.getInstance("SHA-512"); messageDigest.reset(); messageDigest.update(Files.readAllBytes(imagezip.toPath())); String sha1 = DatatypeConverter.printHexBinary(messageDigest.digest()); String sha2 = new String(Files.readAllBytes(imagesha.toPath())); if (sha1.equals(sha2) == true) { ZipFile zipFile = new ZipFile(imagezip, Licence.decrypt(information.storage.storagePassword).toCharArray()); for (FileHeader fileHeader : zipFile.getFileHeaders()) { String filename = fileHeader.getFileName(); if (filename.endsWith("json") == false) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ZipInputStream zis = zipFile.getInputStream(fileHeader); int length = 0; byte[] buffer = new byte[8192]; while ((length = zis.read(buffer)) != -1) bos.write(buffer, 0, length); zis.close(); bos.close(); AccessEnforcement_Detection_Image image = new AccessEnforcement_Detection_Image(); image.name = filename.substring(0, filename.lastIndexOf(".")); image.format = filename.substring(filename.lastIndexOf(".") + 1, filename.length()); image.data = bos.toByteArray(); if (detection.images == null) detection.images = new ArrayList(); detection.images.add(image); } } } } } catch (Exception exception) { Shared.traceError(this.name, Shared.getMessage("Get image"), exception); // Shared.printstack(name, exception); } } // private AccessEnforcement getAccessEnforcement(String identifier, Map map) { try { if (map.containsKey(identifier) == true) { return (AccessEnforcement)map.get(identifier); } AccessEnforcement device = (AccessEnforcement)Shared.model.getDevice(identifier); if (device == null) { device = (AccessEnforcement)Shared.model.getDeviceExternal(identifier); } if (device != null) { map.put(identifier, device); return device; } } catch (Exception exception) { } return null; } private AccessEnforcement getAccessEnforcement(String identifier) throws ServerException, Exception { AccessEnforcement device = (AccessEnforcement)Shared.model.getDevice(identifier); if (device == null) { device = (AccessEnforcement)Shared.model.getDeviceExternal(identifier); } if (device == null) { throw new ServerException(Shared.getMessage("Device not found")); } return device; } private AccessEnforcementInformation getAccessEnforcementInformation(String identifier) throws ServerException, Exception { AccessEnforcement device = (AccessEnforcement)Shared.model.getDevice(identifier); if (device == null) { device = (AccessEnforcement)Shared.model.getDeviceExternal(identifier); } if (device == null) { throw new ServerException(Shared.getMessage("Device not found")); } return device.getDeviceInformation(); } // }