From c4f2b9ae653a8a4cb5e87965a21f8450671404e7 Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Thu, 10 Apr 2014 16:35:18 +0200 Subject: [PATCH 01/12] Switched to ANTLR parser branch --- pom.xml | 28 +++++++++++++++++++ .../legrange/mikrotik/impl/parser/Command.g4 | 23 +++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 diff --git a/pom.xml b/pom.xml index 1ab9cb6..0470de5 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,20 @@ example;*.impl + + + org.antlr + antlr4-maven-plugin + 4.2.2 + + + + antlr4 + + + + + @@ -81,6 +95,20 @@ 3.8.1 test + + org.antlr + antlr4-runtime + 4.2.1 + complete + runtime + + + org.antlr + antlr4 + 4.2.1 + complete + compile + diff --git a/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 b/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 new file mode 100644 index 0000000..6dda077 --- /dev/null +++ b/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 @@ -0,0 +1,23 @@ +/** A Mikrotik API Command grammar */ + +grammar Command; +line: command param* where? retrn? ; +command: CMD ; +param: NAME ('=' text )? ; +text: NO_SPACE | QUOTED ; +where: 'where' expr ; +expr: ( eqExpr | hasExpr | moreExpr | lessExpr ) binExpr? ; +binExpr: andExpr | orExpr ; +eqExpr: NAME '=' text ; +hasExpr: NAME ; +moreExpr: NAME '>' text; +lessExpr: NAME '<' text; +andExpr: expr 'and' expr ; +orExpr: expr 'or' expr ; +retrn: 'returns' NAME (',' NAME )* ; + +NAME : ('a'..'z'|'A'..'Z'|'-')+ ; +CMD_PART : ('/' NAME)+ ; +CMD : CMD_PART+ ; +NO_SPACE : [\\S]+ ; +QUOTED : ('"' [.]* '"') ; \ No newline at end of file From 2633088303a7f2f785b069e6a8b87fd49322a423 Mon Sep 17 00:00:00 2001 From: Gideon le Grange Date: Fri, 11 Apr 2014 09:01:02 +0200 Subject: [PATCH 02/12] Working out ANTLR grammar --- .../me/legrange/mikrotik/impl/parser/Command.g4 | 10 +++++----- src/main/java/examples/Config.java | 2 +- src/main/java/examples/Example6.java | 3 ++- .../mikrotik/impl/ApiConnectionImpl.java | 2 +- .../java/me/legrange/mikrotik/impl/Parser.java | 16 ++++++++++++---- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 b/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 index 6dda077..b485e7d 100644 --- a/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 +++ b/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 @@ -4,7 +4,7 @@ grammar Command; line: command param* where? retrn? ; command: CMD ; param: NAME ('=' text )? ; -text: NO_SPACE | QUOTED ; +text: (QUOTED|VALUE) ; where: 'where' expr ; expr: ( eqExpr | hasExpr | moreExpr | lessExpr ) binExpr? ; binExpr: andExpr | orExpr ; @@ -17,7 +17,7 @@ orExpr: expr 'or' expr ; retrn: 'returns' NAME (',' NAME )* ; NAME : ('a'..'z'|'A'..'Z'|'-')+ ; -CMD_PART : ('/' NAME)+ ; -CMD : CMD_PART+ ; -NO_SPACE : [\\S]+ ; -QUOTED : ('"' [.]* '"') ; \ No newline at end of file +CMD : (('/')(NAME))+ ; +QUOTED : ('"')(.*?)('"') ; +VALUE : (~[ \t\r\n="])+ ; +WS: [ \n\t\r]+ -> skip; \ No newline at end of file diff --git a/src/main/java/examples/Config.java b/src/main/java/examples/Config.java index 899e76d..67dcab9 100644 --- a/src/main/java/examples/Config.java +++ b/src/main/java/examples/Config.java @@ -7,7 +7,7 @@ package examples; public class Config { - public static final String HOST = "10.0.1.3"; + public static final String HOST = "10.0.1.134"; public static final String USERNAME = "admin"; public static final String PASSWORD = ""; diff --git a/src/main/java/examples/Example6.java b/src/main/java/examples/Example6.java index 7c5c203..1f9655b 100644 --- a/src/main/java/examples/Example6.java +++ b/src/main/java/examples/Example6.java @@ -17,7 +17,8 @@ public class Example6 extends Example { } private void test() throws MikrotikApiException, InterruptedException { - con.execute("/interface/gre/add remote-address=10.0.1.1 name=gre1 keepalive=10"); +// con.execute("/interface/gre/add remote-address=1.2.3.4 name=gre1 keepalive=10 comment='test comment'"); + con.execute("/ip/firewall/filter/add action=drop chain=forward in-interface=gre1 protocol=udp dst-port=78,80");//,80,32"); Thread.sleep(10000); // 10 seconds for the user to look on the router to see the interface with /interface gre print con.execute("/interface/gre/set remote-address=172.16.1.1 .id=gre1"); // now look again and the IP has changed diff --git a/src/main/java/me/legrange/mikrotik/impl/ApiConnectionImpl.java b/src/main/java/me/legrange/mikrotik/impl/ApiConnectionImpl.java index fc48278..6b24da6 100644 --- a/src/main/java/me/legrange/mikrotik/impl/ApiConnectionImpl.java +++ b/src/main/java/me/legrange/mikrotik/impl/ApiConnectionImpl.java @@ -486,7 +486,7 @@ public final class ApiConnectionImpl extends ApiConnection { throw new ApiConnectionException(ex.getMessage(), ex); } if (err != null) { - throw err; + throw new MikrotikApiException(err.getMessage(), err); } return results; } diff --git a/src/main/java/me/legrange/mikrotik/impl/Parser.java b/src/main/java/me/legrange/mikrotik/impl/Parser.java index 26e7d2b..dc6caf8 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parser.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parser.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Scanner; import java.util.StringTokenizer; /** @@ -44,8 +45,13 @@ class Parser { if (token == Token.EQUALS) { next(); expect(Token.NAME); - cmd.addParameter(new Parameter(name, text)); + String val = text; next(); + while (token == Token.COMMA) { + next(); + val = val + "," + text; + } + cmd.addParameter(new Parameter(name, val)); } else { cmd.addParameter(new Parameter(name)); @@ -173,9 +179,10 @@ class Parser { // System.out.printf("%s: %s\n", token, text); } - private Parser(String text) { - text = text.trim(); - StringTokenizer st = new StringTokenizer(text, " \t,=", true); + private Parser(String line) { + line = line.trim(); + StringTokenizer st = new StringTokenizer(line, " \t,=", true); + boolean quote = false; while (st.hasMoreElements()) { String t = st.nextToken().trim(); if (!t.equals("")) { @@ -183,6 +190,7 @@ class Parser { } } } + private final List words = new LinkedList(); private String text; private Token token; From 7c73559bb107023ab45d56328bda824ecc6e681f Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Mon, 14 Apr 2014 14:42:51 +0200 Subject: [PATCH 03/12] Re-wrote scanning and parsing to handle problems from issues #3 and #4 --- .../legrange/mikrotik/impl/parser/Command.g4 | 23 ---- .../me/legrange/mikrotik/impl/Parser.java | 118 ++++++++---------- .../legrange/mikrotik/impl/ScanException.java | 21 ++++ 3 files changed, 74 insertions(+), 88 deletions(-) delete mode 100644 src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 create mode 100644 src/main/java/me/legrange/mikrotik/impl/ScanException.java diff --git a/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 b/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 deleted file mode 100644 index b485e7d..0000000 --- a/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 +++ /dev/null @@ -1,23 +0,0 @@ -/** A Mikrotik API Command grammar */ - -grammar Command; -line: command param* where? retrn? ; -command: CMD ; -param: NAME ('=' text )? ; -text: (QUOTED|VALUE) ; -where: 'where' expr ; -expr: ( eqExpr | hasExpr | moreExpr | lessExpr ) binExpr? ; -binExpr: andExpr | orExpr ; -eqExpr: NAME '=' text ; -hasExpr: NAME ; -moreExpr: NAME '>' text; -lessExpr: NAME '<' text; -andExpr: expr 'and' expr ; -orExpr: expr 'or' expr ; -retrn: 'returns' NAME (',' NAME )* ; - -NAME : ('a'..'z'|'A'..'Z'|'-')+ ; -CMD : (('/')(NAME))+ ; -QUOTED : ('"')(.*?)('"') ; -VALUE : (~[ \t\r\n="])+ ; -WS: [ \n\t\r]+ -> skip; \ No newline at end of file diff --git a/src/main/java/me/legrange/mikrotik/impl/Parser.java b/src/main/java/me/legrange/mikrotik/impl/Parser.java index dc6caf8..ea2185a 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parser.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parser.java @@ -1,12 +1,9 @@ package me.legrange.mikrotik.impl; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.StringTokenizer; +import me.legrange.mikrotik.impl.Scanner.Token; /** * Parse the pseudo-command line into command objects. @@ -14,6 +11,15 @@ import java.util.StringTokenizer; */ class Parser { + public static void main(String...args) throws Exception { + /* System.out.println(parse("/system/reboot")); + System.out.println(parse("/interface/print where type=ether")); + System.out.println(parse("/interface/wireless/monitor .id=wlan1")); + System.out.println(parse("/ip/firewall/filter/add action=drop chain=forward in-interface=gre1 protocol=udp dst-port=78,80")); + System.out.println(parse("/file/print detail where name=conf.rsc")); + System.out.println(parse("/ip/firewall/filter/add action=drop chain=forward in-interface=aps_hs protocol=udp dst-port=78,80,32")); */ + System.out.println(parse("/interface/gre/set .id=gre1 comment=\"test coment\"")); + } /** parse the given bit of text into a Command object */ static Command parse(String text) throws ParseException { Parser parser = new Parser(text); @@ -22,10 +28,7 @@ class Parser { /** run parse on the internal data and return the command object */ private Command parse() throws ParseException { - next(); - expect(Token.COMMAND); - cmd = new Command(text); - next(); + command(); while (!((token == Token.WHERE) || (token == Token.RETURN) || (token == Token.EOL))) { param(); } @@ -38,20 +41,36 @@ class Parser { expect(Token.EOL); return cmd; } + + private void command() throws ParseException { + StringBuilder path = new StringBuilder(); + do { + expect(Token.SLASH); + path.append("/"); + next(); + expect(Token.TEXT); + path.append(text); + next(); + } while (token == Token.SLASH); + cmd = new Command(path.toString()); + } private void param() throws ParseException { String name = text; next(); if (token == Token.EQUALS) { next(); - expect(Token.NAME); - String val = text; + expect(Token.TEXT); + StringBuilder val = new StringBuilder(text); next(); while (token == Token.COMMA) { + val.append(","); + next(); + expect(Token.TEXT); + val.append(text); next(); - val = val + "," + text; } - cmd.addParameter(new Parameter(name, val)); + cmd.addParameter(new Parameter(name, val.toString())); } else { cmd.addParameter(new Parameter(name)); @@ -64,12 +83,12 @@ class Parser { } private void expr() throws ParseException { - expect(Token.NOT, Token.NAME); + expect(Token.NOT, Token.TEXT); switch (token) { case NOT: notExpr(); break; - case NAME: { + case TEXT: { String name = text; next(); expect(Token.EQUALS, Token.LESS, Token.MORE); @@ -116,19 +135,20 @@ class Parser { cmd.addQuery("?#!"); } - private void eqExpr(String name) { + private void eqExpr(String name) throws ParseException { next(); // eat = + expect(Token.TEXT); cmd.addQuery(String.format("?%s=%s", name, text)); next(); } - private void lessExpr(String name) { + private void lessExpr(String name) throws ScanException { next(); // eat < cmd.addQuery(String.format("?<%s=%s", name, text)); next(); } - private void moreExpr(String name) { + private void moreExpr(String name) throws ScanException { next(); // eat > cmd.addQuery(String.format("?>%s=%s", name, text)); next(); @@ -140,8 +160,8 @@ class Parser { private void returns() throws ParseException { next(); - expect(Token.NAME); - List props = new LinkedList(); + expect(Token.TEXT); + List props = new LinkedList<>(); while (!(token == Token.EOL)) { if (token != Token.COMMA) { props.add(text); @@ -158,59 +178,27 @@ class Parser { throw new ParseException(String.format("Expected %s but found %s", Arrays.asList(tokens), this.token)); } - private void next() { - if (!words.isEmpty()) { - text = words.remove(0); - if (text.startsWith("/")) { - token = Token.COMMAND; - } else { - Token t = lookup.get(text); - if (t != null) { - token = t; - text = ""; - } else { - token = Token.NAME; - } - } - } else { - token = Token.EOL; - text = ""; + private void next() throws ScanException { +// Token was = token; + token = scanner.next(); + // System.out.printf("'%s' => '%s'\n", was, token); + while (token == Token.WS) { + token = scanner.next(); } - // System.out.printf("%s: %s\n", token, text); + text = scanner.text(); } - private Parser(String line) { + private Parser(String line) throws ScanException { line = line.trim(); - StringTokenizer st = new StringTokenizer(line, " \t,=", true); - boolean quote = false; - while (st.hasMoreElements()) { - String t = st.nextToken().trim(); - if (!t.equals("")) { - words.add(t); - } - } + scanner = new Scanner(line); + next(); } - private final List words = new LinkedList(); - private String text; + + + private final Scanner scanner; private Token token; + private String text; private Command cmd; - private static final Map lookup = new HashMap(); - private enum Token { - - COMMAND, WHERE, RETURN, EOL, NOT, AND, OR, NAME, EQUALS, MORE, LESS, COMMA; - } - - static { - lookup.put("where", Token.WHERE); - lookup.put("return", Token.RETURN); - lookup.put("not", Token.NOT); - lookup.put("and", Token.AND); - lookup.put("or", Token.OR); - lookup.put("=", Token.EQUALS); - lookup.put(">", Token.MORE); - lookup.put("<", Token.LESS); - lookup.put(",", Token.COMMA); - } } diff --git a/src/main/java/me/legrange/mikrotik/impl/ScanException.java b/src/main/java/me/legrange/mikrotik/impl/ScanException.java new file mode 100644 index 0000000..1463f2d --- /dev/null +++ b/src/main/java/me/legrange/mikrotik/impl/ScanException.java @@ -0,0 +1,21 @@ +package me.legrange.mikrotik.impl; + +import me.legrange.mikrotik.MikrotikApiException; + +/** + * Exception thrown if the scanner encounters an error while scanning a command line. + * @author GideonLeGrange + */ +public class ScanException extends ParseException { + + ScanException(String msg) { + super(msg); + } + + ScanException(String msg, Throwable err) { + super(msg, err); + } + + + +} From 99fa3149fd888e82e36870b81ce7df4cd12df22d Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Mon, 14 Apr 2014 14:43:03 +0200 Subject: [PATCH 04/12] Re-wrote scanning and parsing to handle problems from issues #3 and #4 --- .../me/legrange/mikrotik/impl/Scanner.java | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/main/java/me/legrange/mikrotik/impl/Scanner.java diff --git a/src/main/java/me/legrange/mikrotik/impl/Scanner.java b/src/main/java/me/legrange/mikrotik/impl/Scanner.java new file mode 100644 index 0000000..11f9fb8 --- /dev/null +++ b/src/main/java/me/legrange/mikrotik/impl/Scanner.java @@ -0,0 +1,145 @@ +/* + * Copyright 2014 GideonLeGrange. + * + * 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 me.legrange.mikrotik.impl; + +/** + * A simple scanner. + * @author gideon + */ +class Scanner { + + enum Token { + SLASH("/"), COMMA(","), EOL(), WS, TEXT, + LESS("<"), MORE(">"), EQUALS("="), + WHERE, NOT, AND, OR, RETURN; + + @Override + public String toString() { + return (symb == null) ? name() : symb; + } + + private Token(String symb) { + this.symb = symb; + } + + private Token() { + symb = null; + } + + + private final String symb; + } + + Scanner(String line) { + this.line = line; + nextChar(); + } + + Token next() throws ScanException { + text = null; + switch (c) { + case '\n' : return Token.EOL; + case ' ' : + case '\t' : + return whiteSpace(); + case ',' : + nextChar(); + return Token.COMMA; + case '/' : + nextChar(); + return Token.SLASH; + case '<' : + nextChar(); + return Token.LESS; + case '>' : + nextChar(); + return Token.MORE; + case '=' : + nextChar(); + return Token.EQUALS; + case '"' : + return quotedText(); + default : + return name(); + } + + } + + String text() { + if (text != null) return text.toString(); + return ""; + } + + private Token name() throws ScanException { + text = new StringBuilder(); + while (in(c,"[A-Za-z0-9-\\.]")) { + text.append(c); + nextChar(); + } + String val = text.toString(); + switch (val) { + case "where" : return Token.WHERE; + case "not" : return Token.NOT; + case "and" : return Token.AND; + case "or" : return Token.OR; + case "return" : return Token.RETURN; + } + return Token.TEXT; + } + + /** process quoted text */ + private Token quotedText() throws ScanException { + nextChar(); // eat the '"' + text = new StringBuilder(); + while (c != '"') { + if (c == '\n') { + throw new ScanException("Unclosed quoted text"); + } + text.append(c); + nextChar(); + } + nextChar(); // eat the '"' + return Token.TEXT; + } + + /** process white space */ + private Token whiteSpace() { + while ((c == ' ') || (c == '\t')) { + nextChar(); + } + return Token.WS; + } + + private void nextChar() { + if (pos < line.length()) { + c = line.charAt(pos); + pos ++; + } + else { + c = '\n'; + } + } + + private boolean in(char c, String cs) { + return ("" + c).matches(cs); + } + + private final String line; + private int pos = 0; + private char c; + private StringBuilder text; +} From 3d7fee5a4b91269362ef163cdbfcfd7abef3d7ff Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Mon, 14 Apr 2014 14:44:32 +0200 Subject: [PATCH 05/12] Removed stray antlr --- pom.xml | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/pom.xml b/pom.xml index 0470de5..1ab9cb6 100644 --- a/pom.xml +++ b/pom.xml @@ -68,20 +68,6 @@ example;*.impl - - - org.antlr - antlr4-maven-plugin - 4.2.2 - - - - antlr4 - - - - - @@ -95,20 +81,6 @@ 3.8.1 test - - org.antlr - antlr4-runtime - 4.2.1 - complete - runtime - - - org.antlr - antlr4 - 4.2.1 - complete - compile - From b5eb058f2507e6f2a4d9be983c9f2d3296641f67 Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Mon, 14 Apr 2014 14:52:47 +0200 Subject: [PATCH 06/12] Slight scanner and parser error handling improvements --- src/main/java/me/legrange/mikrotik/impl/Parser.java | 5 ++--- .../java/me/legrange/mikrotik/impl/Scanner.java | 13 +++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/me/legrange/mikrotik/impl/Parser.java b/src/main/java/me/legrange/mikrotik/impl/Parser.java index ea2185a..519d786 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parser.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parser.java @@ -175,13 +175,12 @@ class Parser { for (Token want : tokens) { if (this.token == want) return; } - throw new ParseException(String.format("Expected %s but found %s", Arrays.asList(tokens), this.token)); + throw new ParseException(String.format("Expected %s but found %s at position %d", Arrays.asList(tokens), this.token, scanner.pos())); } + /** move to the next token returned by the scanner */ private void next() throws ScanException { -// Token was = token; token = scanner.next(); - // System.out.printf("'%s' => '%s'\n", was, token); while (token == Token.WS) { token = scanner.next(); } diff --git a/src/main/java/me/legrange/mikrotik/impl/Scanner.java b/src/main/java/me/legrange/mikrotik/impl/Scanner.java index 11f9fb8..49b445b 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Scanner.java +++ b/src/main/java/me/legrange/mikrotik/impl/Scanner.java @@ -44,11 +44,13 @@ class Scanner { private final String symb; } + /** create a scanner for the given line of text */ Scanner(String line) { this.line = line; nextChar(); } + /** return the next token from the text */ Token next() throws ScanException { text = null; switch (c) { @@ -79,18 +81,23 @@ class Scanner { } + /** return the text associated with the last token returned */ String text() { if (text != null) return text.toString(); return ""; } + /** return the position of the scanner */ + int pos() { return pos; } + + /** process 'name' tokens which could be key words or text */ private Token name() throws ScanException { text = new StringBuilder(); while (in(c,"[A-Za-z0-9-\\.]")) { text.append(c); nextChar(); } - String val = text.toString(); + String val = text.toString().toLowerCase(); switch (val) { case "where" : return Token.WHERE; case "not" : return Token.NOT; @@ -107,7 +114,7 @@ class Scanner { text = new StringBuilder(); while (c != '"') { if (c == '\n') { - throw new ScanException("Unclosed quoted text"); + throw new ScanException("Unclosed quoted text, reached end of line."); } text.append(c); nextChar(); @@ -124,6 +131,7 @@ class Scanner { return Token.WS; } + /** return the next character from the line of text */ private void nextChar() { if (pos < line.length()) { c = line.charAt(pos); @@ -134,6 +142,7 @@ class Scanner { } } + /** check if the character matches the give expression */ private boolean in(char c, String cs) { return ("" + c).matches(cs); } From c44c02294b85353ef2ed48395b0847f21a6f8abf Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Mon, 14 Apr 2014 16:01:52 +0200 Subject: [PATCH 07/12] More parser fixes and testing --- src/main/java/examples/Example6.java | 6 +++++- src/main/java/me/legrange/mikrotik/impl/Parser.java | 9 --------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/examples/Example6.java b/src/main/java/examples/Example6.java index 1f9655b..a410a25 100644 --- a/src/main/java/examples/Example6.java +++ b/src/main/java/examples/Example6.java @@ -17,9 +17,13 @@ public class Example6 extends Example { } private void test() throws MikrotikApiException, InterruptedException { -// con.execute("/interface/gre/add remote-address=1.2.3.4 name=gre1 keepalive=10 comment='test comment'"); + System.out.println("Creating interface gre1"); + con.execute("/interface/gre/add remote-address=1.2.3.4 name=gre1 keepalive=10 comment=\"test comment\""); + System.out.println("Adding firewall rule for interface gre1"); con.execute("/ip/firewall/filter/add action=drop chain=forward in-interface=gre1 protocol=udp dst-port=78,80");//,80,32"); + System.out.println("Waiting 10 seconds"); Thread.sleep(10000); // 10 seconds for the user to look on the router to see the interface with /interface gre print + System.out.println("Changing IP for interface gre1"); con.execute("/interface/gre/set remote-address=172.16.1.1 .id=gre1"); // now look again and the IP has changed } diff --git a/src/main/java/me/legrange/mikrotik/impl/Parser.java b/src/main/java/me/legrange/mikrotik/impl/Parser.java index 519d786..a2f47d2 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parser.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parser.java @@ -11,15 +11,6 @@ import me.legrange.mikrotik.impl.Scanner.Token; */ class Parser { - public static void main(String...args) throws Exception { - /* System.out.println(parse("/system/reboot")); - System.out.println(parse("/interface/print where type=ether")); - System.out.println(parse("/interface/wireless/monitor .id=wlan1")); - System.out.println(parse("/ip/firewall/filter/add action=drop chain=forward in-interface=gre1 protocol=udp dst-port=78,80")); - System.out.println(parse("/file/print detail where name=conf.rsc")); - System.out.println(parse("/ip/firewall/filter/add action=drop chain=forward in-interface=aps_hs protocol=udp dst-port=78,80,32")); */ - System.out.println(parse("/interface/gre/set .id=gre1 comment=\"test coment\"")); - } /** parse the given bit of text into a Command object */ static Command parse(String text) throws ParseException { Parser parser = new Parser(text); From 10f88ad9f0daddb3b542decf9bed0a380b901382 Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Mon, 14 Apr 2014 16:19:03 +0200 Subject: [PATCH 08/12] Slight scanner and parser error handling improvements --- .../java/me/legrange/mikrotik/impl/Parser.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/me/legrange/mikrotik/impl/Parser.java b/src/main/java/me/legrange/mikrotik/impl/Parser.java index a2f47d2..e4fa3a0 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parser.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parser.java @@ -20,7 +20,7 @@ class Parser { /** run parse on the internal data and return the command object */ private Command parse() throws ParseException { command(); - while (!((token == Token.WHERE) || (token == Token.RETURN) || (token == Token.EOL))) { + while (!is(Token.WHERE, Token.RETURN, Token.EOL)) { param(); } if (token == Token.WHERE) { @@ -54,8 +54,8 @@ class Parser { expect(Token.TEXT); StringBuilder val = new StringBuilder(text); next(); - while (token == Token.COMMA) { - val.append(","); + while (is(Token.COMMA, Token.SLASH)) { + val.append(token); next(); expect(Token.TEXT); val.append(text); @@ -163,10 +163,15 @@ class Parser { } private void expect(Token...tokens) throws ParseException { + if (!is(tokens)) + throw new ParseException(String.format("Expected %s but found %s at position %d", Arrays.asList(tokens), this.token, scanner.pos())); + } + + private boolean is(Token...tokens) { for (Token want : tokens) { - if (this.token == want) return; + if (this.token == want) return true; } - throw new ParseException(String.format("Expected %s but found %s at position %d", Arrays.asList(tokens), this.token, scanner.pos())); + return false; } /** move to the next token returned by the scanner */ From a78ad48d39e694d948477128de26d28ddb396e18 Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Mon, 14 Apr 2014 16:19:20 +0200 Subject: [PATCH 09/12] Slight scanner and parser error handling improvements --- src/main/java/me/legrange/mikrotik/impl/Parser.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/me/legrange/mikrotik/impl/Parser.java b/src/main/java/me/legrange/mikrotik/impl/Parser.java index e4fa3a0..e623361 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parser.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parser.java @@ -189,8 +189,6 @@ class Parser { next(); } - - private final Scanner scanner; private Token token; private String text; From 351f233def6a174d388fc2ce4692d8ed62f4fa37 Mon Sep 17 00:00:00 2001 From: Gideon le Grange Date: Mon, 14 Apr 2014 16:25:33 +0200 Subject: [PATCH 10/12] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0d0bfce..bf58f5d 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,10 @@ This project provides a Java client to manipulate Mikrotik routers using the rem Versions -------- -The current stable version 1.1.2, which adds support for handling multi-line results, like for example /file print. +The current stable version 1.1.3, which fixes two severe command line parsing bugs, #3 and #4. -Version 1.1 added TLS (SSL) support to encrypt API traffic. +* 1.1.2 added support for handling multi-line results, like for example /file print. +* 1.1 added TLS (SSL) support to encrypt API traffic. Examples From 11e9a5805e34d72b5f813ef2cd2c10edb147fbac Mon Sep 17 00:00:00 2001 From: Gideon le Grange Date: Mon, 14 Apr 2014 16:31:13 +0200 Subject: [PATCH 11/12] Update README.md Explained about bugs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf58f5d..e9a0136 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This project provides a Java client to manipulate Mikrotik routers using the rem Versions -------- -The current stable version 1.1.3, which fixes two severe command line parsing bugs, #3 and #4. +The current stable version 1.1.3, which fixes two severe command line parsing bugs, #3 and #4 * 1.1.2 added support for handling multi-line results, like for example /file print. * 1.1 added TLS (SSL) support to encrypt API traffic. From 15c48def68114b5bce8852381a986aeeb63e7b1b Mon Sep 17 00:00:00 2001 From: Gideon le Grange Date: Mon, 14 Apr 2014 20:16:55 +0200 Subject: [PATCH 12/12] Fixed quoting in parser --- src/main/java/examples/Example6.java | 2 +- src/main/java/me/legrange/mikrotik/impl/Scanner.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/examples/Example6.java b/src/main/java/examples/Example6.java index a410a25..f186da0 100644 --- a/src/main/java/examples/Example6.java +++ b/src/main/java/examples/Example6.java @@ -18,7 +18,7 @@ public class Example6 extends Example { private void test() throws MikrotikApiException, InterruptedException { System.out.println("Creating interface gre1"); - con.execute("/interface/gre/add remote-address=1.2.3.4 name=gre1 keepalive=10 comment=\"test comment\""); + con.execute("/interface/gre/add remote-address=1.2.3.4 name=gre1 keepalive=10 comment='test comment'"); System.out.println("Adding firewall rule for interface gre1"); con.execute("/ip/firewall/filter/add action=drop chain=forward in-interface=gre1 protocol=udp dst-port=78,80");//,80,32"); System.out.println("Waiting 10 seconds"); diff --git a/src/main/java/me/legrange/mikrotik/impl/Scanner.java b/src/main/java/me/legrange/mikrotik/impl/Scanner.java index 49b445b..b001404 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Scanner.java +++ b/src/main/java/me/legrange/mikrotik/impl/Scanner.java @@ -74,7 +74,9 @@ class Scanner { nextChar(); return Token.EQUALS; case '"' : - return quotedText(); + return quotedText('"'); + case '\'' : + return quotedText('\''); default : return name(); } @@ -109,10 +111,10 @@ class Scanner { } /** process quoted text */ - private Token quotedText() throws ScanException { + private Token quotedText(char quote) throws ScanException { nextChar(); // eat the '"' text = new StringBuilder(); - while (c != '"') { + while (c != quote) { if (c == '\n') { throw new ScanException("Unclosed quoted text, reached end of line."); }