diff options
Diffstat (limited to 'src/main/java/org/traccar/helper')
18 files changed, 731 insertions, 52 deletions
diff --git a/src/main/java/org/traccar/helper/BitUtil.java b/src/main/java/org/traccar/helper/BitUtil.java index b6108edff..829ddebc9 100644 --- a/src/main/java/org/traccar/helper/BitUtil.java +++ b/src/main/java/org/traccar/helper/BitUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ public final class BitUtil { } public static boolean check(long number, int index) { - return (number & (1 << index)) != 0; + return (number & (1L << index)) != 0; } public static int between(int number, int from, int to) { diff --git a/src/main/java/org/traccar/helper/BufferUtil.java b/src/main/java/org/traccar/helper/BufferUtil.java index 9485c17c6..12c31ba9d 100644 --- a/src/main/java/org/traccar/helper/BufferUtil.java +++ b/src/main/java/org/traccar/helper/BufferUtil.java @@ -27,6 +27,24 @@ public final class BufferUtil { private BufferUtil() { } + public static int readSignedMagnitudeInt(ByteBuf buffer) { + long value = buffer.readUnsignedInt(); + int result = (int) BitUtil.to(value, 31); + return BitUtil.check(value, 31) ? -result : result; + } + + public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value, int count) { + int startIndex = fromIndex; + for (int i = 0; i < count; i++) { + int result = buffer.indexOf(startIndex, toIndex, value); + if (result < 0 || i == count - 1) { + return result; + } + startIndex = result + 1; + } + return -1; + } + public static int indexOf(String needle, ByteBuf haystack) { return indexOf(needle, haystack, haystack.readerIndex(), haystack.writerIndex()); } @@ -41,16 +59,28 @@ public final class BufferUtil { } public static int indexOf(ByteBuf needle, ByteBuf haystack, int startIndex, int endIndex) { - ByteBuf wrappedHaystack; - if (startIndex == haystack.readerIndex() && endIndex == haystack.writerIndex()) { - wrappedHaystack = haystack; - } else { - wrappedHaystack = Unpooled.wrappedBuffer(haystack); - wrappedHaystack.readerIndex(startIndex - haystack.readerIndex()); - wrappedHaystack.writerIndex(endIndex - haystack.readerIndex()); + int originalReaderIndex = haystack.readerIndex(); + int originalWriterIndex = haystack.writerIndex(); + try { + haystack.readerIndex(startIndex); + haystack.writerIndex(endIndex); + return ByteBufUtil.indexOf(needle, haystack); + } finally { + haystack.readerIndex(originalReaderIndex); + haystack.writerIndex(originalWriterIndex); + } + } + + public static boolean isPrintable(ByteBuf buf, int length) { + boolean printable = true; + for (int i = 0; i < length; i++) { + byte b = buf.getByte(buf.readerIndex() + i); + if (b < 32 && b != '\r' && b != '\n') { + printable = false; + break; + } } - int result = ByteBufUtil.indexOf(needle, wrappedHaystack); - return result < 0 ? result : startIndex + result; + return printable; } } diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java index 8c3d0063a..db5817275 100644 --- a/src/main/java/org/traccar/helper/Checksum.java +++ b/src/main/java/org/traccar/helper/Checksum.java @@ -200,4 +200,19 @@ public final class Checksum { return (10 - (checksum % 10)) % 10; } + public static int ip(ByteBuffer data) { + int sum = 0; + while (data.remaining() > 0) { + sum += data.get() & 0xff; + if ((sum & 0x80000000) > 0) { + sum = (sum & 0xffff) + (sum >> 16); + } + } + while ((sum >> 16) > 0) { + sum = (sum & 0xffff) + sum >> 16; + } + sum = (sum == 0xffff) ? sum & 0xffff : (~sum) & 0xffff; + return sum; + } + } diff --git a/src/main/java/org/traccar/helper/ClassScanner.java b/src/main/java/org/traccar/helper/ClassScanner.java index c928f6a12..c201d101f 100644 --- a/src/main/java/org/traccar/helper/ClassScanner.java +++ b/src/main/java/org/traccar/helper/ClassScanner.java @@ -46,7 +46,7 @@ public final class ClassScanner { URL packageUrl = baseClass.getClassLoader().getResource(packagePath); if (packageUrl.getProtocol().equals("jar")) { - String jarFileName = URLDecoder.decode(packageUrl.getFile(), StandardCharsets.UTF_8.name()); + String jarFileName = URLDecoder.decode(packageUrl.getFile(), StandardCharsets.UTF_8); try (JarFile jf = new JarFile(jarFileName.substring(5, jarFileName.indexOf("!")))) { Enumeration<JarEntry> jarEntries = jf.entries(); while (jarEntries.hasMoreElements()) { diff --git a/src/main/java/org/traccar/helper/Log.java b/src/main/java/org/traccar/helper/Log.java index 8c67f9ddc..9aaf1cfd3 100644 --- a/src/main/java/org/traccar/helper/Log.java +++ b/src/main/java/org/traccar/helper/Log.java @@ -28,6 +28,10 @@ import java.io.StringWriter; import java.io.Writer; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.FileStore; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.ConsoleHandler; @@ -51,10 +55,12 @@ public final class Log { private String suffix; private Writer writer; private final boolean rotate; + private final String template; - RollingFileHandler(String name, boolean rotate) { + RollingFileHandler(String name, boolean rotate, String rotateInterval) { this.name = name; this.rotate = rotate; + this.template = rotateInterval.equalsIgnoreCase("HOUR") ? "yyyyMMddHH" : "yyyyMMdd"; } @Override @@ -63,7 +69,7 @@ public final class Log { try { String suffix = ""; if (rotate) { - suffix = new SimpleDateFormat("yyyyMMdd").format(new Date(record.getMillis())); + suffix = new SimpleDateFormat(template).format(new Date(record.getMillis())); if (writer != null && !suffix.equals(this.suffix)) { writer.close(); writer = null; @@ -165,7 +171,7 @@ public final class Log { public static void setupDefaultLogger() { String path = null; - URL url = ClassLoader.getSystemClassLoader().getResource("."); + URL url = ClassLoader.getSystemClassLoader().getResource("."); if (url != null) { File jarPath = new File(url.getPath()); File logsPath = new File(jarPath, "logs"); @@ -174,7 +180,7 @@ public final class Log { } path = new File(logsPath, "tracker-server.log").getPath(); } - setupLogger(path == null, path, Level.WARNING.getName(), false, true); + setupLogger(path == null, path, Level.WARNING.getName(), false, true, "DAY"); } public static void setupLogger(Config config) { @@ -183,11 +189,13 @@ public final class Log { config.getString(Keys.LOGGER_FILE), config.getString(Keys.LOGGER_LEVEL), config.getBoolean(Keys.LOGGER_FULL_STACK_TRACES), - config.getBoolean(Keys.LOGGER_ROTATE)); + config.getBoolean(Keys.LOGGER_ROTATE), + config.getString(Keys.LOGGER_ROTATE_INTERVAL)); } private static void setupLogger( - boolean console, String file, String levelString, boolean fullStackTraces, boolean rotate) { + boolean console, String file, String levelString, + boolean fullStackTraces, boolean rotate, String rotateInterval) { Logger rootLogger = Logger.getLogger(""); for (Handler handler : rootLogger.getHandlers()) { @@ -198,7 +206,7 @@ public final class Log { if (console) { handler = new ConsoleHandler(); } else { - handler = new RollingFileHandler(file, rotate); + handler = new RollingFileHandler(file, rotate, rotateInterval); } handler.setFormatter(new LogFormatter(fullStackTraces)); @@ -269,4 +277,18 @@ public final class Log { return s.toString(); } + public static long[] getStorageSpace() { + long usable = 0; + long total = 0; + for (Path root : FileSystems.getDefault().getRootDirectories()) { + try { + FileStore store = Files.getFileStore(root); + usable += store.getUsableSpace(); + total += store.getTotalSpace(); + } catch (IOException ignored) { + } + } + return new long[]{usable, total}; + } + } diff --git a/src/main/java/org/traccar/helper/LogAction.java b/src/main/java/org/traccar/helper/LogAction.java index d16b25483..b255b9206 100644 --- a/src/main/java/org/traccar/helper/LogAction.java +++ b/src/main/java/org/traccar/helper/LogAction.java @@ -47,7 +47,7 @@ public final class LogAction { private static final String PATTERN_OBJECT = "user: %d, action: %s, object: %s, id: %d"; private static final String PATTERN_LINK = "user: %d, action: %s, owner: %s, id: %d, property: %s, id: %d"; - private static final String PATTERN_LOGIN = "user: %d, action: %s"; + private static final String PATTERN_LOGIN = "user: %d, action: %s, from: %s"; private static final String PATTERN_LOGIN_FAILED = "login failed from: %s"; private static final String PATTERN_DEVICE_ACCUMULATORS = "user: %d, action: %s, deviceId: %d"; private static final String PATTERN_REPORT = "user: %d, report: %s, from: %s, to: %s, devices: %s, groups: %s"; @@ -72,12 +72,12 @@ public final class LogAction { logLinkAction(ACTION_UNLINK, userId, owner, ownerId, property, propertyId); } - public static void login(long userId) { - logLoginAction(ACTION_LOGIN, userId); + public static void login(long userId, String remoteAddress) { + logLoginAction(ACTION_LOGIN, userId, remoteAddress); } - public static void logout(long userId) { - logLoginAction(ACTION_LOGOUT, userId); + public static void logout(long userId, String remoteAddress) { + logLoginAction(ACTION_LOGOUT, userId, remoteAddress); } public static void failedLogin(String remoteAddress) { @@ -105,8 +105,11 @@ public final class LogAction { Introspector.decapitalize(property.getSimpleName()), propertyId)); } - private static void logLoginAction(String action, long userId) { - LOGGER.info(String.format(PATTERN_LOGIN, userId, action)); + private static void logLoginAction(String action, long userId, String remoteAddress) { + if (remoteAddress == null || remoteAddress.isEmpty()) { + remoteAddress = "unknown"; + } + LOGGER.info(String.format(PATTERN_LOGIN, userId, action, remoteAddress)); } public static void logReport( diff --git a/src/main/java/org/traccar/helper/NetworkUtil.java b/src/main/java/org/traccar/helper/NetworkUtil.java new file mode 100644 index 000000000..2fe3487da --- /dev/null +++ b/src/main/java/org/traccar/helper/NetworkUtil.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper; + +import io.netty.channel.Channel; +import io.netty.channel.socket.DatagramChannel; + +public final class NetworkUtil { + + private NetworkUtil() { + } + + public static String session(Channel channel) { + char transport = channel instanceof DatagramChannel ? 'U' : 'T'; + return transport + channel.id().asShortText(); + } + +} diff --git a/src/main/java/org/traccar/helper/ObdDecoder.java b/src/main/java/org/traccar/helper/ObdDecoder.java index b22065f4e..3cbae334a 100644 --- a/src/main/java/org/traccar/helper/ObdDecoder.java +++ b/src/main/java/org/traccar/helper/ObdDecoder.java @@ -51,22 +51,7 @@ public final class ObdDecoder { StringBuilder codes = new StringBuilder(); for (int i = 0; i < value.length() / 4; i++) { int numValue = Integer.parseInt(value.substring(i * 4, (i + 1) * 4), 16); - codes.append(' '); - switch (numValue >> 14) { - case 1: - codes.append('C'); - break; - case 2: - codes.append('B'); - break; - case 3: - codes.append('U'); - break; - default: - codes.append('P'); - break; - } - codes.append(String.format("%04X", numValue & 0x3FFF)); + codes.append(' ').append(decodeCode(numValue)); } if (codes.length() > 0) { return createEntry(Position.KEY_DTCS, codes.toString().trim()); @@ -75,6 +60,25 @@ public final class ObdDecoder { } } + public static String decodeCode(int value) { + char prefix; + switch (value >> 14) { + case 1: + prefix = 'C'; + break; + case 2: + prefix = 'B'; + break; + case 3: + prefix = 'U'; + break; + default: + prefix = 'P'; + break; + } + return String.format("%c%04X", prefix, value & 0x3FFF); + } + public static Map.Entry<String, Object> decodeData(int pid, long value, boolean convert) { switch (pid) { case 0x04: diff --git a/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java new file mode 100644 index 000000000..634950b85 --- /dev/null +++ b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java @@ -0,0 +1,38 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.inject.Inject; +import jakarta.ws.rs.ext.ContextResolver; + +// This does not work as a lambda +public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> { + + private final ObjectMapper objectMapper; + + @Inject + public ObjectMapperContextResolver(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public ObjectMapper getContext(Class<?> clazz) { + return objectMapper; + } + +} diff --git a/src/main/java/org/traccar/helper/Parser.java b/src/main/java/org/traccar/helper/Parser.java index 75106e2ba..c2aea28fa 100644 --- a/src/main/java/org/traccar/helper/Parser.java +++ b/src/main/java/org/traccar/helper/Parser.java @@ -48,13 +48,25 @@ public class Parser { } public boolean hasNext(int number) { - String value = matcher.group(position); - if (value != null && !value.isEmpty()) { - return true; - } else { - position += number; - return false; + for (int i = position; i < position + number; i++) { + String value = matcher.group(i); + if (value == null || value.isEmpty()) { + position += number; + return false; + } + } + return true; + } + + public boolean hasNextAny(int number) { + for (int i = position; i < position + number; i++) { + String value = matcher.group(i); + if (value != null && !value.isEmpty()) { + return true; + } } + position += number; + return false; } public String next() { @@ -155,6 +167,7 @@ public class Parser { public enum CoordinateFormat { DEG_DEG, + DEG_DEG_HEM, DEG_HEM, DEG_MIN_MIN, DEG_MIN_HEM, @@ -173,6 +186,10 @@ public class Parser { case DEG_DEG: coordinate = Double.parseDouble(next() + '.' + next()); break; + case DEG_DEG_HEM: + coordinate = Double.parseDouble(next() + '.' + next()); + hemisphere = next(); + break; case DEG_HEM: coordinate = nextDouble(0); hemisphere = next(); diff --git a/src/main/java/org/traccar/helper/PatternUtil.java b/src/main/java/org/traccar/helper/PatternUtil.java index 74813e1d9..a46c7b7b4 100644 --- a/src/main/java/org/traccar/helper/PatternUtil.java +++ b/src/main/java/org/traccar/helper/PatternUtil.java @@ -63,7 +63,7 @@ public final class PatternUtil { for (int i = 0; i < pattern.length(); i++) { try { - Matcher matcher = Pattern.compile("(" + pattern.substring(0, i) + ").*").matcher(input); + Matcher matcher = Pattern.compile("(" + pattern.substring(0, i) + ")[\\s\\S]*").matcher(input); if (matcher.matches()) { result.patternMatch = pattern.substring(0, i); result.patternTail = pattern.substring(i); diff --git a/src/main/java/org/traccar/helper/StringUtil.java b/src/main/java/org/traccar/helper/StringUtil.java new file mode 100644 index 000000000..9b4d717f4 --- /dev/null +++ b/src/main/java/org/traccar/helper/StringUtil.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper; + +public final class StringUtil { + + private StringUtil() { + } + + public static boolean containsHex(String value) { + for (char c : value.toCharArray()) { + if (c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F') { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/org/traccar/helper/ServletHelper.java b/src/main/java/org/traccar/helper/WebHelper.java index b6c587ec3..9533fe84b 100644 --- a/src/main/java/org/traccar/helper/ServletHelper.java +++ b/src/main/java/org/traccar/helper/WebHelper.java @@ -15,11 +15,18 @@ */ package org.traccar.helper; -import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; -public final class ServletHelper { +import jakarta.servlet.http.HttpServletRequest; - private ServletHelper() { +import org.eclipse.jetty.util.URIUtil; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +public final class WebHelper { + + private WebHelper() { } public static String retrieveRemoteAddress(HttpServletRequest request) { @@ -42,4 +49,17 @@ public final class ServletHelper { } } + public static String retrieveWebUrl(Config config) { + if (config.hasKey(Keys.WEB_URL)) { + return config.getString(Keys.WEB_URL).replaceAll("/$", ""); + } else { + String address; + try { + address = config.getString(Keys.WEB_ADDRESS, InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException e) { + address = "localhost"; + } + return URIUtil.newURI("http", address, config.getInteger(Keys.WEB_PORT), "", ""); + } + } } diff --git a/src/main/java/org/traccar/helper/model/AttributeUtil.java b/src/main/java/org/traccar/helper/model/AttributeUtil.java new file mode 100644 index 000000000..2630f64f0 --- /dev/null +++ b/src/main/java/org/traccar/helper/model/AttributeUtil.java @@ -0,0 +1,188 @@ +/* + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper.model; + +import org.traccar.api.security.PermissionsService; +import org.traccar.config.Config; +import org.traccar.config.ConfigKey; +import org.traccar.config.KeyType; +import org.traccar.config.Keys; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.Server; +import org.traccar.session.cache.CacheManager; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +public final class AttributeUtil { + + private AttributeUtil() { + } + + public interface Provider { + Device getDevice(); + Group getGroup(long groupId); + Server getServer(); + Config getConfig(); + } + + public static <T> T lookup(CacheManager cacheManager, ConfigKey<T> key, long deviceId) { + return lookup(new CacheProvider(cacheManager, deviceId), key); + } + + @SuppressWarnings({ "deprecation", "unchecked" }) + public static <T> T lookup(Provider provider, ConfigKey<T> key) { + Device device = provider.getDevice(); + Object result = device.getAttributes().get(key.getKey()); + long groupId = device.getGroupId(); + while (result == null && groupId > 0) { + Group group = provider.getGroup(groupId); + if (group != null) { + result = group.getAttributes().get(key.getKey()); + groupId = group.getGroupId(); + } else { + groupId = 0; + } + } + if (result == null && key.hasType(KeyType.SERVER)) { + result = provider.getServer().getAttributes().get(key.getKey()); + } + if (result == null && key.hasType(KeyType.CONFIG)) { + result = provider.getConfig().getString(key.getKey()); + } + + if (result != null) { + Class<T> valueClass = key.getValueClass(); + if (valueClass.equals(Boolean.class)) { + return (T) (result instanceof String + ? Boolean.parseBoolean((String) result) + : result); + } else if (valueClass.equals(Integer.class)) { + return (T) (Object) (result instanceof String + ? Integer.parseInt((String) result) + : ((Number) result).intValue()); + } else if (valueClass.equals(Long.class)) { + return (T) (Object) (result instanceof String + ? Long.parseLong((String) result) + : ((Number) result).longValue()); + } else if (valueClass.equals(Double.class)) { + return (T) (Object) (result instanceof String + ? Double.parseDouble((String) result) + : ((Number) result).doubleValue()); + } else { + return (T) result; + } + } + return key.getDefaultValue(); + } + + public static String getDevicePassword( + CacheManager cacheManager, long deviceId, String protocol, String defaultPassword) { + + String password = lookup(cacheManager, Keys.DEVICE_PASSWORD, deviceId); + if (password != null) { + return password; + } + + if (protocol != null) { + password = cacheManager.getConfig().getString(Keys.PROTOCOL_DEVICE_PASSWORD.withPrefix(protocol)); + if (password != null) { + return password; + } + } + + return defaultPassword; + } + + public static class CacheProvider implements Provider { + + private final CacheManager cacheManager; + private final long deviceId; + + public CacheProvider(CacheManager cacheManager, long deviceId) { + this.cacheManager = cacheManager; + this.deviceId = deviceId; + } + + @Override + public Device getDevice() { + return cacheManager.getObject(Device.class, deviceId); + } + + @Override + public Group getGroup(long groupId) { + return cacheManager.getObject(Group.class, groupId); + } + + @Override + public Server getServer() { + return cacheManager.getServer(); + } + + @Override + public Config getConfig() { + return cacheManager.getConfig(); + } + } + + public static class StorageProvider implements Provider { + + private final Config config; + private final Storage storage; + private final PermissionsService permissionsService; + private final Device device; + + public StorageProvider(Config config, Storage storage, PermissionsService permissionsService, Device device) { + this.config = config; + this.storage = storage; + this.permissionsService = permissionsService; + this.device = device; + } + + @Override + public Device getDevice() { + return device; + } + + @Override + public Group getGroup(long groupId) { + try { + return storage.getObject( + Group.class, new Request(new Columns.All(), new Condition.Equals("id", groupId))); + } catch (StorageException e) { + throw new RuntimeException(e); + } + } + + @Override + public Server getServer() { + try { + return permissionsService.getServer(); + } catch (StorageException e) { + throw new RuntimeException(e); + } + } + + @Override + public Config getConfig() { + return config; + } + } + +} diff --git a/src/main/java/org/traccar/helper/model/DeviceUtil.java b/src/main/java/org/traccar/helper/model/DeviceUtil.java new file mode 100644 index 000000000..5d8cb5f25 --- /dev/null +++ b/src/main/java/org/traccar/helper/model/DeviceUtil.java @@ -0,0 +1,79 @@ +/* + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper.model; + +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.User; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Objects; +import java.util.stream.Collectors; + +public final class DeviceUtil { + + private DeviceUtil() { + } + + public static void resetStatus(Storage storage) throws StorageException { + storage.updateObject(new Device(), new Request(new Columns.Include("status"))); + } + + + public static Collection<Device> getAccessibleDevices( + Storage storage, long userId, + Collection<Long> deviceIds, Collection<Long> groupIds) throws StorageException { + + var devices = storage.getObjects(Device.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, Device.class))); + var deviceById = devices.stream() + .collect(Collectors.toUnmodifiableMap(Device::getId, x -> x)); + var devicesByGroup = devices.stream() + .filter(x -> x.getGroupId() > 0) + .collect(Collectors.groupingBy(Device::getGroupId)); + + var groups = storage.getObjects(Group.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, Group.class))); + var groupsByGroup = groups.stream() + .filter(x -> x.getGroupId() > 0) + .collect(Collectors.groupingBy(Group::getGroupId)); + + var results = deviceIds.stream() + .map(deviceById::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + var groupQueue = new LinkedList<>(groupIds); + while (!groupQueue.isEmpty()) { + long groupId = groupQueue.pop(); + results.addAll(devicesByGroup.getOrDefault(groupId, Collections.emptyList())); + groupQueue.addAll(groupsByGroup.getOrDefault(groupId, Collections.emptyList()) + .stream().map(Group::getId).collect(Collectors.toUnmodifiableList())); + } + + return results; + } + +} diff --git a/src/main/java/org/traccar/helper/model/GeofenceUtil.java b/src/main/java/org/traccar/helper/model/GeofenceUtil.java new file mode 100644 index 000000000..9f063a8b4 --- /dev/null +++ b/src/main/java/org/traccar/helper/model/GeofenceUtil.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper.model; + +import org.traccar.config.Config; +import org.traccar.model.Geofence; +import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; + +import java.util.ArrayList; +import java.util.List; + +public final class GeofenceUtil { + + private GeofenceUtil() { + } + + public static List<Long> getCurrentGeofences(Config config, CacheManager cacheManager, Position position) { + List<Long> result = new ArrayList<>(); + for (Geofence geofence : cacheManager.getDeviceObjects(position.getDeviceId(), Geofence.class)) { + if (geofence.getGeometry().containsPoint( + config, geofence, position.getLatitude(), position.getLongitude())) { + result.add(geofence.getId()); + } + } + return result; + } + +} diff --git a/src/main/java/org/traccar/helper/model/PositionUtil.java b/src/main/java/org/traccar/helper/model/PositionUtil.java new file mode 100644 index 000000000..6c380b81a --- /dev/null +++ b/src/main/java/org/traccar/helper/model/PositionUtil.java @@ -0,0 +1,80 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper.model; + +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Position; +import org.traccar.model.User; +import org.traccar.session.cache.CacheManager; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Order; +import org.traccar.storage.query.Request; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +public final class PositionUtil { + + private PositionUtil() { + } + + public static boolean isLatest(CacheManager cacheManager, Position position) { + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + return lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) >= 0; + } + + public static double calculateDistance(Position first, Position last, boolean useOdometer) { + double distance; + double firstOdometer = first.getDouble(Position.KEY_ODOMETER); + double lastOdometer = last.getDouble(Position.KEY_ODOMETER); + + if (useOdometer && firstOdometer != 0.0 && lastOdometer != 0.0) { + distance = lastOdometer - firstOdometer; + } else { + distance = last.getDouble(Position.KEY_TOTAL_DISTANCE) - first.getDouble(Position.KEY_TOTAL_DISTANCE); + } + return distance; + } + + public static List<Position> getPositions( + Storage storage, long deviceId, Date from, Date to) throws StorageException { + return storage.getObjects(Position.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", deviceId), + new Condition.Between("fixTime", "from", from, "to", to)), + new Order("fixTime"))); + } + + public static List<Position> getLatestPositions(Storage storage, long userId) throws StorageException { + var devices = storage.getObjects(Device.class, new Request( + new Columns.Include("id"), + new Condition.Permission(User.class, userId, Device.class))); + var deviceIds = devices.stream().map(BaseModel::getId).collect(Collectors.toUnmodifiableSet()); + + var positions = storage.getObjects(Position.class, new Request( + new Columns.All(), new Condition.LatestPositions())); + return positions.stream() + .filter(position -> deviceIds.contains(position.getDeviceId())) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/org/traccar/helper/model/UserUtil.java b/src/main/java/org/traccar/helper/model/UserUtil.java new file mode 100644 index 000000000..4b1c404f9 --- /dev/null +++ b/src/main/java/org/traccar/helper/model/UserUtil.java @@ -0,0 +1,78 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper.model; + +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.model.Server; +import org.traccar.model.User; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Order; +import org.traccar.storage.query.Request; + +import java.util.Date; +import java.util.TimeZone; + +public final class UserUtil { + + private UserUtil() { + } + + public static boolean isEmpty(Storage storage) throws StorageException { + return storage.getObjects(User.class, new Request( + new Columns.Include("id"), + new Order("id", false, 1))).isEmpty(); + } + + public static String getDistanceUnit(Server server, User user) { + return lookupStringAttribute(server, user, "distanceUnit", "km"); + } + + public static String getSpeedUnit(Server server, User user) { + return lookupStringAttribute(server, user, "speedUnit", "kn"); + } + + public static String getVolumeUnit(Server server, User user) { + return lookupStringAttribute(server, user, "volumeUnit", "ltr"); + } + + public static TimeZone getTimezone(Server server, User user) { + String timezone = lookupStringAttribute(server, user, "timezone", null); + return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault(); + } + + private static String lookupStringAttribute(Server server, User user, String key, String defaultValue) { + String preference; + String serverPreference = server.getString(key); + String userPreference = user.getString(key); + if (server.getForceSettings()) { + preference = serverPreference != null ? serverPreference : userPreference; + } else { + preference = userPreference != null ? userPreference : serverPreference; + } + return preference != null ? preference : defaultValue; + } + + public static void setUserDefaults(User user, Config config) { + user.setDeviceLimit(config.getInteger(Keys.USERS_DEFAULT_DEVICE_LIMIT)); + int expirationDays = config.getInteger(Keys.USERS_DEFAULT_EXPIRATION_DAYS); + if (expirationDays > 0) { + user.setExpirationTime(new Date(System.currentTimeMillis() + expirationDays * 86400000L)); + } + } +} |