diff --git a/src/main/java/me/legrange/mikrotik/ApiCommandException.java b/src/main/java/me/legrange/mikrotik/ApiCommandException.java index 1c4a63d..6580c78 100644 --- a/src/main/java/me/legrange/mikrotik/ApiCommandException.java +++ b/src/main/java/me/legrange/mikrotik/ApiCommandException.java @@ -1,4 +1,7 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; + +import me.legrange.mikrotik.MikrotikApiException; +import me.legrange.mikrotik.impl.Error; /** * Thrown when the Mikrotik returns an error when receiving our command. diff --git a/src/main/java/me/legrange/mikrotik/ApiConnection.java b/src/main/java/me/legrange/mikrotik/ApiConnection.java index d2c2d67..b3c49ca 100644 --- a/src/main/java/me/legrange/mikrotik/ApiConnection.java +++ b/src/main/java/me/legrange/mikrotik/ApiConnection.java @@ -1,18 +1,8 @@ package me.legrange.mikrotik; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.LinkedBlockingQueue; +import me.legrange.mikrotik.impl.ApiConnectionImpl; /** * The Mikrotik API connection. This is the class used to connect to a remote @@ -20,7 +10,10 @@ import java.util.concurrent.LinkedBlockingQueue; * * @author GideonLeGrange */ -public class ApiConnection { +public abstract class ApiConnection { + + public static final int DEFAULT_PORT = 8728; + /** * Create a new API connection to the give device on the supplied port @@ -28,10 +21,8 @@ public class ApiConnection { * @param port The TCP port to use. * @return The ApiConnection */ - public static ApiConnection connect(String host, int port) throws ApiConnectionException { - ApiConnection con = new ApiConnection(); - con.open(host, port); - return con; + public static ApiConnection connect(String host, int port) throws MikrotikApiException { + return ApiConnectionImpl.connect(host, port); } /** @@ -39,7 +30,7 @@ public class ApiConnection { * @param host The host to which to connect. * @return The ApiConnection */ - public static ApiConnection connect(String host) throws ApiConnectionException { + public static ApiConnection connect(String host) throws MikrotikApiException { return connect(host, DEFAULT_PORT); } @@ -48,25 +39,12 @@ public class ApiConnection { * * @return if connection is established to router it returns true. */ - public boolean isConnected() { - return connected; - } + public abstract boolean isConnected(); /** * Disconnect from the remote API */ - public void disconnect() throws ApiConnectionException { - if (!connected) { - throw new ApiConnectionException(("Not/no longer connected to remote Mikrotik")); - } - connected = false; - reader.interrupt(); - try { - sock.close(); - } catch (IOException ex) { - throw new ApiConnectionException(String.format("Error closing socket: %s", ex.getMessage()), ex); - } - } + public abstract void disconnect() throws MikrotikApiException; /** * Log in to the remote router. @@ -74,22 +52,13 @@ public class ApiConnection { * @param username - username of the user on the router * @param password - password for the user */ - public void login(String username, String password) throws MikrotikApiException, ApiCommandException, InterruptedException { - List> list = execute("/login"); - Map res = list.get(0); - String hash = res.get("ret"); - String chal = Util.hexStrToStr("00") + new String(makePass(password)) + Util.hexStrToStr(hash); - chal = Util.hashMD5(chal); - execute("/login name=" + username + " response=00" + chal); - } + public abstract void login(String username, String password) throws MikrotikApiException, InterruptedException; /** execute a command and return a list of results. * @param cmd Command to execute * @return The list of results */ - public List> execute(String cmd) throws MikrotikApiException { - return execute(Parser.parse(cmd)); - } + public abstract List> execute(String cmd) throws MikrotikApiException; /** execute a command and attach a result listener to receive it's results. * @@ -98,359 +67,9 @@ public class ApiConnection { * @return A command object that can be used to cancel the command. * @throws MikrotikApiException */ - public String execute(String cmd, ResultListener lis) throws MikrotikApiException { - return execute(Parser.parse(cmd), lis); - } + public abstract String execute(String cmd, ResultListener lis) throws MikrotikApiException; /** cancel a command */ - public void cancel(String tag) throws MikrotikApiException { - execute(String.format("/cancel tag=%s", tag)) ; - } + public abstract void cancel(String tag) throws MikrotikApiException; - private List> execute(Command cmd) throws MikrotikApiException { - SyncListener l = new SyncListener(); - execute(cmd, l); - return l.getResults(); - } - - - private String execute(Command cmd, ResultListener lis) throws MikrotikApiException { - String tag = nextTag(); - cmd.setTag(tag); - listeners.put(tag, lis); - try { - Util.write(cmd, out); - } catch (UnsupportedEncodingException ex) { - throw new ApiDataException(ex.getMessage(), ex); - } catch (IOException ex) { - throw new ApiConnectionException(ex.getMessage(), ex); - } - return tag; - } - - private ApiConnection() { - } - - /** - * Start the API. Connects to the Mikrotik - */ - private void open(String host, int port) throws ApiConnectionException { - try { - InetAddress ia = InetAddress.getByName(host); - if (ia.isReachable(1000)) { - sock = new Socket(ia, port); - in = new DataInputStream(sock.getInputStream()); - out = new DataOutputStream(sock.getOutputStream()); - connected = true; - reader = new Reader(); - reader.setDaemon(true); - reader.start(); - processor = new Processor(); - processor.setDaemon(true); - processor.start(); - } else { - throw new ApiConnectionException(String.format("Host '%s' port %d is uncreachable", host, port)); - } - } catch (UnknownHostException ex) { - connected = false; - throw new ApiConnectionException(String.format("Unknown host '%s'", host), ex); - } catch (IOException ex) { - connected = false; - throw new ApiConnectionException(String.format("Error connecting to '%s': %s", host, ex.getMessage()), ex); - } - } - - private char[] makePass(String pass) { - if (true) { - return pass.toCharArray(); - } - char[] res = new char[pass.length() + 1]; - System.arraycopy(pass.toCharArray(), 0, res, 0, pass.length()); - res[pass.length()] = 0x0; - return res; - } - - private synchronized String nextTag() { - _tag++; - return Integer.toHexString(_tag); - } - - private static final int DEFAULT_PORT = 8728; - private Socket sock = null; - private DataOutputStream out = null; - private DataInputStream in = null; - private boolean connected = false; - private Reader reader; - private Processor processor; - private final Map listeners = new HashMap(); - private Integer _tag = 0; - - /** - * thread to read data from the socket and process it into Strings - */ - private class Reader extends Thread { - - private String take() throws ApiConnectionException, ApiDataException { - Object val = null; - try { - val = queue.take(); - } catch (InterruptedException ex) { - throw new ApiConnectionException("Interrupted while reading data from queue.", ex); - } - if (val instanceof ApiConnectionException) { - throw (ApiConnectionException) val; - } else if (val instanceof ApiDataException) { - throw (ApiDataException) val; - } - return (String) val; - } - - private boolean isEmpty() { - return queue.isEmpty(); - } - - @Override - public void run() { - while (connected) { - try { - String s = Util.decode(in); - if (s != null) { - queue.put(s); - } - } catch (ApiDataException ex) { - try { - queue.put(ex); - } catch (InterruptedException ex2) { - } - } catch (ApiConnectionException ex) { - } catch (InterruptedException ex1) { - } - } - } - private LinkedBlockingQueue queue = new LinkedBlockingQueue(40); - } - - /** - * Thread to take the received strings and process it into Result objects - */ - private class Processor extends Thread { - - @Override - public void run() { - while (connected) { - Response res; - try { - res = unpack(); - } catch (ApiCommandException ex) { - String tag = ex.getTag(); - if (tag != null) { - res = new Error(tag, ex.getMessage()); - } else { - continue; - } - } catch (MikrotikApiException ex) { - ex.printStackTrace(); - continue; - } - ResultListener l = listeners.get(res.getTag()); - if (l != null) { - if (res instanceof Result) { - l.receive((Result) res); - } else { - if (res instanceof Done) { - listeners.remove(res.getTag()); - } - if (l instanceof ResponseListener) { - ResponseListener rl = (ResponseListener) l; - if (res instanceof Done) { - if (rl instanceof SyncListener) { - ((SyncListener)rl).completed((Done)res); - } - else { - rl.completed(); - } - } else if (res instanceof Error) { - rl.error(new ApiCommandException((Error) res)); - } - } - } - } - } - } - - private void nextLine() throws ApiConnectionException, ApiDataException { - if (lines.isEmpty()) { - String block = reader.take(); - String parts[] = block.split("\n"); - lines.addAll(Arrays.asList(parts)); - } - line = lines.remove(0); - } - - private boolean hasNextLine() { - return !lines.isEmpty() || !reader.isEmpty(); - } - - private Response unpack() throws MikrotikApiException { - if (line == null) { - nextLine(); - } - if (line.equals("!re")) { - return unpackRe(); - } else if (line.equals("!done")) { - return unpackDone(); - } else if (line.equals("!trap")) { - return unpackError(); - } else if (line.equals("!halt")) { - return unpackError(); - } else { - throw new ApiDataException(String.format("Unexpected line '%s'", line)); - } - } - - private Result unpackRe() throws ApiDataException, ApiConnectionException { - nextLine(); - int l = 0; - Result res = new Result(); - while (!line.startsWith(("!"))) { - l++; - if (line.startsWith(("="))) { - String parts[] = line.split("=", 3); - if (parts.length == 3) { - res.put(parts[1], parts[2]); - } else { - throw new ApiDataException(String.format("Malformed line '%s'", line)); - } - } else if (line.startsWith(".tag=")) { - String parts[] = line.split("=", 2); - if (parts.length == 2) { - res.setTag(parts[1]); - } - } else { - throw new ApiDataException(String.format("Unexpected line '%s'", line)); - } - if (hasNextLine()) { - nextLine(); - } else { - line = null; - break; - } - } - return res; - } - - private Done unpackDone() throws MikrotikApiException { - Done done = new Done(null); - if (hasNextLine()) { - nextLine(); - - while (!line.startsWith("!")) { - if (line.startsWith(".tag=")) { - String parts[] = line.split("=", 2); - if (parts.length == 2) { - done.setTag(parts[1]); - } - } else if (line.startsWith(("=ret"))) { - String parts[] = line.split("=", 3); - if (parts.length == 3) { - done.setHash(parts[2]); - } else { - throw new ApiDataException(String.format("Malformed line '%s'", line)); - } - } - if (hasNextLine()) { - nextLine(); - } else { - line = null; - break; - } - } - } - return done; - } - - private Error unpackError() throws MikrotikApiException { - nextLine(); - Error err = new Error(); - if (hasNextLine()) { - while (!line.startsWith("!")) { - if (line.startsWith(".tag=")) { - String parts[] = line.split("=", 2); - if (parts.length == 2) { - err.setTag(parts[1]); - } - } else if (line.startsWith("=message=")) { - err.setMessage(line.split("=", 3)[2]); - } - if (hasNextLine()) { - nextLine(); - } else { - line = null; - break; - } - } - } - return err; - } - - private void queue(Response res) { - String tag = res.getTag(); - if (tag != null) { - ResultListener rl = listeners.get(tag); - if (rl != null) { - if (res instanceof Result) { - rl.receive((Result) res); - } else { - // rl.error((Error)res); - } - } - } - } - private List lines = new LinkedList(); - private String line; - } - - private class SyncListener implements ResponseListener { - - public synchronized void error(MikrotikApiException ex) { - this.err = ex; - notify(); - } - - public synchronized void completed() { - notify(); - } - - synchronized void completed(Done done) { - if (done.getHash() != null) { - Result res = new Result(); - res.put("ret", done.getHash()); - results.add(res); - } - notify(); - } - - public void receive(Map result) { - results.add(result); - } - - private List> getResults() throws MikrotikApiException { - try { - synchronized (this) { // don't wait if we already have a result. - if ((err == null) && results.isEmpty()) { - wait(); - } - } - } catch (InterruptedException ex) { - throw new ApiConnectionException(ex.getMessage(), ex); - } - if (err != null) { - throw err; - } - return results; - } - - private List> results = new LinkedList>(); - private MikrotikApiException err; - } } \ No newline at end of file diff --git a/src/main/java/me/legrange/mikrotik/ApiConnectionException.java b/src/main/java/me/legrange/mikrotik/ApiConnectionException.java index 10f4e35..a7add55 100644 --- a/src/main/java/me/legrange/mikrotik/ApiConnectionException.java +++ b/src/main/java/me/legrange/mikrotik/ApiConnectionException.java @@ -6,11 +6,11 @@ package me.legrange.mikrotik; */ public class ApiConnectionException extends MikrotikApiException { - ApiConnectionException(String msg) { + public ApiConnectionException(String msg) { super(msg); } - ApiConnectionException(String msg, Throwable err) { + public ApiConnectionException(String msg, Throwable err) { super(msg, err); } diff --git a/src/main/java/me/legrange/mikrotik/MikrotikApiException.java b/src/main/java/me/legrange/mikrotik/MikrotikApiException.java index cd39367..3a559ab 100644 --- a/src/main/java/me/legrange/mikrotik/MikrotikApiException.java +++ b/src/main/java/me/legrange/mikrotik/MikrotikApiException.java @@ -7,11 +7,11 @@ package me.legrange.mikrotik; */ public class MikrotikApiException extends Exception { - MikrotikApiException(String msg) { + public MikrotikApiException(String msg) { super(msg); } - MikrotikApiException(String msg, Throwable err) { + public MikrotikApiException(String msg, Throwable err) { super(msg, err); } } diff --git a/src/main/java/me/legrange/mikrotik/impl/ApiConnectionImpl.java b/src/main/java/me/legrange/mikrotik/impl/ApiConnectionImpl.java index d2c2d67..d3a0619 100644 --- a/src/main/java/me/legrange/mikrotik/impl/ApiConnectionImpl.java +++ b/src/main/java/me/legrange/mikrotik/impl/ApiConnectionImpl.java @@ -1,5 +1,6 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; +import me.legrange.mikrotik.*; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -15,12 +16,12 @@ import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; /** - * The Mikrotik API connection. This is the class used to connect to a remote + * The Mikrotik API connection implementation. This is the class used to connect to a remote * Mikrotik and send commands to it. * * @author GideonLeGrange */ -public class ApiConnection { +public final class ApiConnectionImpl extends ApiConnection { /** * Create a new API connection to the give device on the supplied port @@ -29,20 +30,11 @@ public class ApiConnection { * @return The ApiConnection */ public static ApiConnection connect(String host, int port) throws ApiConnectionException { - ApiConnection con = new ApiConnection(); + ApiConnectionImpl con = new ApiConnectionImpl(); con.open(host, port); return con; } - /** - * Create a new API connection to the give device on the default API port.. - * @param host The host to which to connect. - * @return The ApiConnection - */ - public static ApiConnection connect(String host) throws ApiConnectionException { - return connect(host, DEFAULT_PORT); - } - /** * Check the state of connection. * @@ -74,7 +66,7 @@ public class ApiConnection { * @param username - username of the user on the router * @param password - password for the user */ - public void login(String username, String password) throws MikrotikApiException, ApiCommandException, InterruptedException { + public void login(String username, String password) throws MikrotikApiException, InterruptedException { List> list = execute("/login"); Map res = list.get(0); String hash = res.get("ret"); @@ -128,7 +120,7 @@ public class ApiConnection { return tag; } - private ApiConnection() { + private ApiConnectionImpl() { } /** diff --git a/src/main/java/me/legrange/mikrotik/impl/ApiDataException.java b/src/main/java/me/legrange/mikrotik/impl/ApiDataException.java index e2b9de7..0d1e597 100644 --- a/src/main/java/me/legrange/mikrotik/impl/ApiDataException.java +++ b/src/main/java/me/legrange/mikrotik/impl/ApiDataException.java @@ -1,4 +1,6 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; + +import me.legrange.mikrotik.MikrotikApiException; /** * Thrown if there is a problem unpacking data from the Api. diff --git a/src/main/java/me/legrange/mikrotik/impl/Command.java b/src/main/java/me/legrange/mikrotik/impl/Command.java index a49c24d..72ec06b 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Command.java +++ b/src/main/java/me/legrange/mikrotik/impl/Command.java @@ -1,4 +1,4 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; import java.util.Arrays; import java.util.LinkedList; diff --git a/src/main/java/me/legrange/mikrotik/impl/Done.java b/src/main/java/me/legrange/mikrotik/impl/Done.java index 9105f4a..0948dea 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Done.java +++ b/src/main/java/me/legrange/mikrotik/impl/Done.java @@ -1,4 +1,4 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; /** * Internal representation of !done diff --git a/src/main/java/me/legrange/mikrotik/impl/Error.java b/src/main/java/me/legrange/mikrotik/impl/Error.java index 2998741..6e7b8df 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Error.java +++ b/src/main/java/me/legrange/mikrotik/impl/Error.java @@ -1,4 +1,4 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; /** * Used to encapsulate API error information. We need to pass both the message and the tag (if one was used). diff --git a/src/main/java/me/legrange/mikrotik/impl/Parameter.java b/src/main/java/me/legrange/mikrotik/impl/Parameter.java index 0937e93..8ebe57e 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parameter.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parameter.java @@ -1,4 +1,4 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; /** * A command parameter diff --git a/src/main/java/me/legrange/mikrotik/impl/ParseException.java b/src/main/java/me/legrange/mikrotik/impl/ParseException.java index 5cb8185..b784856 100644 --- a/src/main/java/me/legrange/mikrotik/impl/ParseException.java +++ b/src/main/java/me/legrange/mikrotik/impl/ParseException.java @@ -1,4 +1,6 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; + +import me.legrange.mikrotik.MikrotikApiException; /** * Exception thrown if the parser encounters an error while parsing a command line. diff --git a/src/main/java/me/legrange/mikrotik/impl/Parser.java b/src/main/java/me/legrange/mikrotik/impl/Parser.java index fc95692..26e7d2b 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parser.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parser.java @@ -1,4 +1,4 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; import java.util.Arrays; import java.util.HashMap; diff --git a/src/main/java/me/legrange/mikrotik/impl/Response.java b/src/main/java/me/legrange/mikrotik/impl/Response.java index 5e05a02..6e9b592 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Response.java +++ b/src/main/java/me/legrange/mikrotik/impl/Response.java @@ -1,4 +1,4 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; /** * Super type of possible API responses diff --git a/src/main/java/me/legrange/mikrotik/impl/Result.java b/src/main/java/me/legrange/mikrotik/impl/Result.java index 1b6a741..847c9b3 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Result.java +++ b/src/main/java/me/legrange/mikrotik/impl/Result.java @@ -1,4 +1,4 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; import java.util.Collection; import java.util.HashMap; diff --git a/src/main/java/me/legrange/mikrotik/impl/Util.java b/src/main/java/me/legrange/mikrotik/impl/Util.java index d34c0d0..606273c 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Util.java +++ b/src/main/java/me/legrange/mikrotik/impl/Util.java @@ -1,4 +1,4 @@ -package me.legrange.mikrotik; +package me.legrange.mikrotik.impl; import java.io.IOException; import java.io.InputStream; @@ -7,17 +7,23 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.List; +import me.legrange.mikrotik.ApiConnectionException; /** - * Utility library that handles the low level encoding required by the Mikrotik API. + * Utility library that handles the low level encoding required by the Mikrotik + * API. + * * @author GideonLeGrange. Possibly some code by janisk left. */ -class Util { - +final class Util { + + /** + * write a command to the output stream + */ static void write(Command cmd, OutputStream out) throws UnsupportedEncodingException, IOException { encode(cmd.getCommand(), out); for (Parameter param : cmd.getParameters()) { - encode(String.format("=%s=%s", param.getName(), param.hasValue() ? param.getValue() : "" ), out); + encode(String.format("=%s=%s", param.getName(), param.hasValue() ? param.getValue() : ""), out); } String tag = cmd.getTag(); if ((tag != null) && !tag.equals("")) { @@ -27,57 +33,32 @@ class Util { if (!props.isEmpty()) { StringBuilder buf = new StringBuilder("=.proplist="); for (int i = 0; i < props.size(); ++i) { - if (i > 0) + if (i > 0) { buf.append(","); + } buf.append(props.get(i)); } encode(buf.toString(), out); } - for (String query : cmd.getQueries()) { + for (String query : cmd.getQueries()) { encode(query, out); } out.write(0); } - - /** encode text using Mikrotik's encoding scheme and write it to an output stream. */ - private static void encode(String word, OutputStream out) throws UnsupportedEncodingException, IOException { - byte bytes[] = word.getBytes("US-ASCII"); - int len = bytes.length; - if (len < 0x80) { - out.write(len); - } else if (len < 0x4000) { - len = len | 0x8000; - out.write(len >> 8); - out.write(len); - } else if (len < 0x20000) { - len = len | 0xC00000; - out.write(len >> 16); - out.write(len >> 8); - out.write(len); - } else if (len < 0x10000000) { - len = len | 0xE0000000; - out.write(len >> 24); - out.write(len >> 16); - out.write(len >> 8); - out.write(len); - } else { - out.write(0xF0); - out.write(len >> 24); - out.write(len >> 16); - out.write(len >> 8); - out.write(len); - } - out.write(bytes); - } - - /** decode bytes from an input stream of Mikrotik protocol sentences into text */ + + /** + * decode bytes from an input stream of Mikrotik protocol sentences into + * text + */ static String decode(InputStream in) throws ApiDataException, ApiConnectionException { StringBuilder res = new StringBuilder(); decode(in, res); return res.toString(); } - - /** decode bytes from an input stream into Mikrotik protocol sentences */ + + /** + * decode bytes from an input stream into Mikrotik protocol sentences + */ private static void decode(InputStream in, StringBuilder result) throws ApiDataException, ApiConnectionException { try { int len = readLen(in); @@ -91,44 +72,17 @@ class Util { buf[i] = (byte) (c & 0xFF); } String res = new String(buf); - if (result.length() > 0) + if (result.length() > 0) { result.append("\n"); + } result.append(res); decode(in, result); - } + } } catch (IOException ex) { throw new ApiConnectionException(ex.getMessage(), ex); } } - /** read length bytes from stream and return length of coming word */ - static private int readLen(InputStream in) throws IOException { - int c = in.read(); - if (c > 0) { - if ((c & 0x80) == 0) { - } else if ((c & 0xC0) == 0x80) { - c = c & ~0xC0; - c = (c << 8) | in.read(); - } else if ((c & 0xE0) == 0xC0) { - c = c & ~0xE0; - c = (c << 8) | in.read(); - c = (c << 8) | in.read(); - } else if ((c & 0xF0) == 0xE0) { - c = c & ~0xF0; - c = (c << 8) | in.read(); - c = (c << 8) | in.read(); - c = (c << 8) | in.read(); - } else if ((c & 0xF8) == 0xF0) { - c = in.read(); - c = (c << 8) | in.read(); - c = (c << 8) | in.read(); - c = (c << 8) | in.read(); - c = (c << 8) | in.read(); - } - } - return c; - } - /** * makes MD5 hash of string for use with RouterOS API * @@ -173,4 +127,68 @@ class Util { } return ret; } + + /** + * encode text using Mikrotik's encoding scheme and write it to an output + * stream. + */ + private static void encode(String word, OutputStream out) throws UnsupportedEncodingException, IOException { + byte bytes[] = word.getBytes("US-ASCII"); + int len = bytes.length; + if (len < 0x80) { + out.write(len); + } else if (len < 0x4000) { + len = len | 0x8000; + out.write(len >> 8); + out.write(len); + } else if (len < 0x20000) { + len = len | 0xC00000; + out.write(len >> 16); + out.write(len >> 8); + out.write(len); + } else if (len < 0x10000000) { + len = len | 0xE0000000; + out.write(len >> 24); + out.write(len >> 16); + out.write(len >> 8); + out.write(len); + } else { + out.write(0xF0); + out.write(len >> 24); + out.write(len >> 16); + out.write(len >> 8); + out.write(len); + } + out.write(bytes); + } + + /** + * read length bytes from stream and return length of coming word + */ + private static int readLen(InputStream in) throws IOException { + int c = in.read(); + if (c > 0) { + if ((c & 0x80) == 0) { + } else if ((c & 0xC0) == 0x80) { + c = c & ~0xC0; + c = (c << 8) | in.read(); + } else if ((c & 0xE0) == 0xC0) { + c = c & ~0xE0; + c = (c << 8) | in.read(); + c = (c << 8) | in.read(); + } else if ((c & 0xF0) == 0xE0) { + c = c & ~0xF0; + c = (c << 8) | in.read(); + c = (c << 8) | in.read(); + c = (c << 8) | in.read(); + } else if ((c & 0xF8) == 0xF0) { + c = in.read(); + c = (c << 8) | in.read(); + c = (c << 8) | in.read(); + c = (c << 8) | in.read(); + c = (c << 8) | in.read(); + } + } + return c; + } } \ No newline at end of file