mtik 4.0.3 → 4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.txt +54 -14
- data/LICENSE.txt +3 -3
- data/README.txt +19 -19
- data/Rakefile +1 -32
- data/VERSION.txt +1 -1
- data/bin/tikcli +46 -6
- data/bin/tikcommand +50 -12
- data/bin/tikfetch +50 -13
- data/bin/tikjson +119 -0
- data/lib/mtik.rb +40 -24
- data/lib/mtik/connection.rb +111 -44
- data/lib/mtik/error.rb +2 -2
- data/lib/mtik/fatalerror.rb +2 -2
- data/lib/mtik/reply.rb +2 -2
- data/lib/mtik/request.rb +2 -2
- data/lib/mtik/timeouterror.rb +2 -2
- metadata +11 -11
- data/examples/tikjson.rb +0 -72
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c0fcd5a05cdbbb232cc76a9aa321be06965df29d767051feb038d9707b2f75e5
|
|
4
|
+
data.tar.gz: bc38cf94a3d8e7d14fbce5dfda7092f317c7720133a8e74a9a823a3658abbdf5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dddba29fb56df7da8b2d4d31e51f7bf5bab698d2731662832547b754b6908f7fc101ae404cc23dcaf065fec868c3aaa0eecafe9b0bfd041f268d47b7fcf1f13c
|
|
7
|
+
data.tar.gz: f461371288c57a6cbe97e29f646193ec0cf9d8f2f0404f6acf5318c67e2fbadbcb0f6e8bb2f3ca3a9dc55ed5cc2d74c0208f3deec3d5dcb022ae2d8ae12396c2
|
data/CHANGELOG.txt
CHANGED
|
@@ -1,18 +1,58 @@
|
|
|
1
|
-
|
|
1
|
+
2020-08-24 (24 AUG 2020) VERSION 4.1.2
|
|
2
|
+
* Added tikjson to the executables list in mtik.gemspec
|
|
3
|
+
(Yes, I totally forgot to do this for 4.1.1. Oops!)
|
|
4
|
+
|
|
5
|
+
2020-08-24 (24 AUG 2020) VERSION 4.1.1
|
|
6
|
+
* Version bump to update copyright notices and author's URL
|
|
7
|
+
* Moved the tikjson script to the bin subdirectory and updated
|
|
8
|
+
it to include CLI options like tikcommand and added a
|
|
9
|
+
--pretty CLI option.
|
|
10
|
+
* Updated README to use HTTPS URLs where releant AND fixed the
|
|
11
|
+
examples section to correctly note that example scripts now
|
|
12
|
+
reside in the bin subdirectory.
|
|
13
|
+
* Tweaked varous bin subdirectory scripts to add a --verbose
|
|
14
|
+
CLI option (except for tikcli) and matching MTIK_VERBOSE
|
|
15
|
+
environment variable.
|
|
16
|
+
|
|
17
|
+
2020-08-23 (23 AUG 2020) VERSION 4.1.0
|
|
18
|
+
* Minor version bump due to changing argument passing for the call to
|
|
19
|
+
MTik.interactive_client()
|
|
20
|
+
* Updated tikcli, tikcommand, and tikfetch commands to add options for
|
|
21
|
+
enabling SSL and/or to use unencrypted plaintext logins (newer API
|
|
22
|
+
login style). Also one can set environment variables MTIK_SSL to
|
|
23
|
+
specify SSL use, or MTIK_UNENCRYPTED_PLAINTEXT to enable unencrypted
|
|
24
|
+
plaintext logins if SSL is NOT used for compatibility with cleartext
|
|
25
|
+
API usage on RouterOS versions 6.43+
|
|
26
|
+
* THANKS to Zdenek Crha (zdenek-crha on github) for pointing out that
|
|
27
|
+
the binary commands were lacking proper argument passing to allow
|
|
28
|
+
for SSL and/or unencrypted plaintext options, and for suggesting the
|
|
29
|
+
use of environment variables as an alterative to CLI options for
|
|
30
|
+
enabling such.
|
|
31
|
+
|
|
32
|
+
2020-08-22 (22 AUG 2020) VERSION 4.0.5
|
|
33
|
+
* This is a cosmetic version bump for the purpose of updating the gem for wider
|
|
34
|
+
availability via rubygems in addition to directly from github prior to some
|
|
35
|
+
coming feature updates and fixes for newer versions of RouterOS
|
|
36
|
+
|
|
37
|
+
2019-07-26 (26 JUL 2020) VERSION 4.0.4 Adam Kubica (github user xcr)
|
|
38
|
+
* Adam bumped the version number and separated out a gemspec file
|
|
39
|
+
* Jiacheng (github user krhougs) had submitted a similar update as Adam
|
|
40
|
+
|
|
41
|
+
2014-02-14 (14 FEB 2014) VERSION 4.0.3 Aaron D. Gifford (https://aarongifford.com)
|
|
2
42
|
* Update to fetch() utility, along with some very minor some cosmetic changes
|
|
3
43
|
|
|
4
|
-
2013-06-06 (06 JUN 2013) VERSION 4.0.2 Aaron D. Gifford (
|
|
44
|
+
2013-06-06 (06 JUN 2013) VERSION 4.0.2 Aaron D. Gifford (https://aarongifford.com)
|
|
5
45
|
Bart Braem (http://www.lalunerouge.net/)
|
|
6
46
|
* Merged Bart Braem's implementation of timeouts and bumped up the version. Thanks, Bart!
|
|
7
47
|
* Updated Rakefile to remove a bit of obsolescence
|
|
8
48
|
|
|
9
|
-
2012-02-09 (09 FEB 2012) VERSION 4.0.1 Aaron D. Gifford (
|
|
49
|
+
2012-02-09 (09 FEB 2012) VERSION 4.0.1 Aaron D. Gifford (https://aarongifford.com)
|
|
10
50
|
* Added os_version to connections. Upon successful connect and login, the RouterOS
|
|
11
51
|
version is fetched and stored. This will allow future updates to better support
|
|
12
52
|
some commands that differ (like fetch) depending on which RouterOS version is
|
|
13
53
|
installed on the device.
|
|
14
54
|
|
|
15
|
-
2011-03-25 (25 MAR 2011) VERSION 4.0.0 Aaron D. Gifford (
|
|
55
|
+
2011-03-25 (25 MAR 2011) VERSION 4.0.0 Aaron D. Gifford (https://aarongifford.com)
|
|
16
56
|
* Per user suggestion, added a new optional cancel parameter to the MTik#command()
|
|
17
57
|
method that will auto-cancel the supplied command after receiving the specified
|
|
18
58
|
number of '!re' reply sentences. This is usful for executing a command that otherwise
|
|
@@ -28,7 +68,7 @@
|
|
|
28
68
|
eliminated a few redundant key?() calls, and fixed a replycounter initialization
|
|
29
69
|
typo (had set it to 1 instead of 0).
|
|
30
70
|
|
|
31
|
-
2011-01-11 (11 JAN 2011) VERSION 3.1.2 Aaron D. Gifford (
|
|
71
|
+
2011-01-11 (11 JAN 2011) VERSION 3.1.2 Aaron D. Gifford (https://aarongifford.com)
|
|
32
72
|
* Added source file encoding comments and updated the copyright notices
|
|
33
73
|
* Fixed a tiny bug in lib/mtik/connection.rb
|
|
34
74
|
* Changed MTik::Request@state member in lib/mtik/request.rb to Symbol instead of String
|
|
@@ -37,12 +77,12 @@
|
|
|
37
77
|
* Renamed a few variables that, with warnings enabled, Ruby complained were overlapping
|
|
38
78
|
or hiding outer variables of the same name to avoid the warning.
|
|
39
79
|
|
|
40
|
-
2010-12-30 (30 DEC 2011) VERSION 3.1.1 Aaron D. Gifford (
|
|
80
|
+
2010-12-30 (30 DEC 2011) VERSION 3.1.1 Aaron D. Gifford (https://aarongifford.com)
|
|
41
81
|
* Changed the tikfetch.rb utility so it no longer requires a destination filename
|
|
42
82
|
by default--it will use the supplied URL's final path element as a filename. Also
|
|
43
83
|
updated the stats output a bit.
|
|
44
84
|
|
|
45
|
-
2010-04-24 (24 APR 2010) VERSION 3.1.0 Aaron D. Gifford (
|
|
85
|
+
2010-04-24 (24 APR 2010) VERSION 3.1.0 Aaron D. Gifford (https://aarongifford.com)
|
|
46
86
|
* Added find_sentences() method to MTik::Reply -- just sugar to Array.select()
|
|
47
87
|
* Changed MTik::Connection.fetch() method to add an optional timeout parameter
|
|
48
88
|
which should not affect the API and should be backward compatible. By default,
|
|
@@ -56,28 +96,28 @@
|
|
|
56
96
|
command if needed. Due to this change, I decided to bump the version to 3.1.0.
|
|
57
97
|
* Fixed RDoc formatting in several files, and added an RDocTask to the Rakefile
|
|
58
98
|
|
|
59
|
-
2010-04-23 (23 APR 2010) VERSION 3.0.5 Aaron D. Gifford (
|
|
60
|
-
* Double bug-fix (typo fix and logic fix) to request.rb thanks to Allan Eising and
|
|
99
|
+
2010-04-23 (23 APR 2010) VERSION 3.0.5 Aaron D. Gifford (https://aarongifford.com)
|
|
100
|
+
* Double bug-fix (typo fix and logic fix) to request.rb thanks to Allan Eising and
|
|
61
101
|
Søren Daugaard. Thank you both for the patch!
|
|
62
102
|
* Added a brief sanity-check in request.rb to help spotlight logic errors.
|
|
63
103
|
|
|
64
|
-
2010-04-09 (09 APR 2010) VERSION 3.0.4 Aaron D. Gifford (
|
|
104
|
+
2010-04-09 (09 APR 2010) VERSION 3.0.4 Aaron D. Gifford (https://aarongifford.com)
|
|
65
105
|
* Bug fix to lib/mtik.rb thanks to Allan Eising to the command validation regular
|
|
66
106
|
expression to permit the '-' character in a command. Thanks!
|
|
67
107
|
|
|
68
|
-
2010-03-11 (11 MAR 2010) VERSION 3.0.3 Aaron D. Gifford (
|
|
108
|
+
2010-03-11 (11 MAR 2010) VERSION 3.0.3 Aaron D. Gifford (https://aarongifford.com)
|
|
69
109
|
* Bug fix to lib/mtik.rb command() method so when executing multiple commands
|
|
70
110
|
response array order matches command array order.
|
|
71
111
|
* Cosmetic change for hex encoding with a little simplification, and one very small
|
|
72
112
|
readability change in lib/mtik/connection.rb
|
|
73
113
|
|
|
74
|
-
2010-02-05 (05 FEB 2010) VERSION 3.0.2 Aaron D. Gifford (
|
|
114
|
+
2010-02-05 (05 FEB 2010) VERSION 3.0.2 Aaron D. Gifford (https://aarongifford.com)
|
|
75
115
|
* Typo fixes to example tikfetch.rb
|
|
76
116
|
* Multi-command functionality added to interactive client and to tikcommand.rb example
|
|
77
117
|
* Removed kludge path stuff for author's broken FreeBSD Ruby 1.9.1 gem system
|
|
78
118
|
* Removed lame JSON-ifier from example and switched to require 'json' instead
|
|
79
119
|
|
|
80
|
-
2010-01-19 (19 JAN 2010) VERSION 3.0.1 Aaron D. Gifford (
|
|
120
|
+
2010-01-19 (19 JAN 2010) VERSION 3.0.1 Aaron D. Gifford (https://aarongifford.com/)
|
|
81
121
|
* Added support for '/tool/fetch' 'requesting' state
|
|
82
122
|
* Added auto-cancel of finished '/tool/fetch' command in MTik#command()
|
|
83
123
|
* Added MTik::Request#cancel() method
|
|
@@ -90,7 +130,7 @@
|
|
|
90
130
|
messed-up FreeBSD Ruby 1.9 installation
|
|
91
131
|
* Renamed examples with a 'tik' prefix
|
|
92
132
|
|
|
93
|
-
2010-01-15 (15 JAN 2010) VERSION 3.0.0 Aaron D. Gifford (
|
|
133
|
+
2010-01-15 (15 JAN 2010) VERSION 3.0.0 Aaron D. Gifford (https://aarongifford.com/)
|
|
94
134
|
* Bumped the version to 3.0.0 due to modularization and gemification changes
|
|
95
135
|
that break any software using the older Ruby API classes.
|
|
96
136
|
* Split out (as separate files) and added a few more examples:
|
data/LICENSE.txt
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
########################################################################
|
|
2
2
|
#--
|
|
3
3
|
#
|
|
4
|
-
# GEM:
|
|
4
|
+
# GEM: MTik -- A Ruby gem implementing the MikroTik RouterOS API
|
|
5
5
|
#
|
|
6
6
|
#++
|
|
7
|
-
# Author:: Aaron D. Gifford -
|
|
8
|
-
# Copyright:: Copyright (c) 2009-
|
|
7
|
+
# Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
8
|
+
# Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
9
9
|
# License:: BSD license
|
|
10
10
|
#--
|
|
11
11
|
# Redistribution and use in source and binary forms, with or without
|
data/README.txt
CHANGED
|
@@ -6,48 +6,48 @@ devices running RouterOS (with the API enabled) over the network.
|
|
|
6
6
|
|
|
7
7
|
== Examples
|
|
8
8
|
|
|
9
|
-
Included in the
|
|
9
|
+
Included in the bin subdirectory are four example ruby scripts
|
|
10
10
|
that use the Ruby API implementation:
|
|
11
11
|
|
|
12
|
-
tikcli
|
|
13
|
-
tikcommand
|
|
14
|
-
|
|
15
|
-
tikfetch
|
|
16
|
-
|
|
17
|
-
tikjson
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
tikcli:: A command-line-like API interface
|
|
13
|
+
tikcommand:: A tool for executing a single API command from
|
|
14
|
+
a unix command-line
|
|
15
|
+
tikfetch:: A tool for instructing devices to download files
|
|
16
|
+
from specified URLs (unix command-line tool)
|
|
17
|
+
tikjson:: Like 'tikcommand' a tool for executing a single
|
|
18
|
+
API command, except the output is formatted in
|
|
19
|
+
JSON-style
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
== Download
|
|
23
23
|
|
|
24
24
|
The latest version of MTik can be found at
|
|
25
25
|
|
|
26
|
-
*
|
|
26
|
+
* https://aarongifford.com/computers/mtik/
|
|
27
27
|
|
|
28
28
|
Ruby RDoc documentation can be found online at
|
|
29
29
|
|
|
30
|
-
*
|
|
30
|
+
* https://aarongifford.com/computers/mtik/doc/
|
|
31
31
|
|
|
32
32
|
Additional documentation is available at
|
|
33
33
|
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* https://aarongifford.com/computers/mtik/
|
|
35
|
+
* https://wiki.mikrotik.com/wiki/API_Ruby_class
|
|
36
36
|
|
|
37
37
|
Source code is available on GitHub:
|
|
38
38
|
|
|
39
|
-
*
|
|
39
|
+
* https://www.github.com/astounding/mtik/
|
|
40
40
|
|
|
41
|
-
For documentation on the MikroTik RouterOS
|
|
41
|
+
For documentation on the MikroTik RouterOS API, see
|
|
42
42
|
|
|
43
|
-
*
|
|
43
|
+
* https://wiki.mikrotik.com/wiki/API
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
== Installation
|
|
47
47
|
|
|
48
48
|
To install MTik is through its GEM file:
|
|
49
49
|
|
|
50
|
-
% [sudo] gem install mtik-4.
|
|
50
|
+
% [sudo] gem install mtik-4.1.1.gem
|
|
51
51
|
|
|
52
52
|
== License
|
|
53
53
|
|
|
@@ -59,13 +59,13 @@ under a BSD-style license. (See the LICENSE.txt file.)
|
|
|
59
59
|
|
|
60
60
|
Bugs/issues may also be reported via GitHub:
|
|
61
61
|
|
|
62
|
-
*
|
|
62
|
+
* https://www.github.com/astounding/mtik/issues/
|
|
63
63
|
|
|
64
64
|
ALSO, please send the author a note regarding the issue by clicking on the
|
|
65
65
|
"Contact Me" link in the left-hand menu of the author's web site.
|
|
66
66
|
The direct URL is:
|
|
67
67
|
|
|
68
|
-
*
|
|
68
|
+
* https://aarongifford.com/leaveanote.html
|
|
69
69
|
|
|
70
70
|
Thank you!
|
|
71
71
|
|
data/Rakefile
CHANGED
|
@@ -2,38 +2,7 @@ require 'rubygems'
|
|
|
2
2
|
require 'rubygems/package_task'
|
|
3
3
|
require 'rdoc/task'
|
|
4
4
|
|
|
5
|
-
gemspec = Gem::Specification.
|
|
6
|
-
spec.name = 'mtik'
|
|
7
|
-
spec.version = File.open('VERSION.txt','r').to_a.join.strip
|
|
8
|
-
spec.date = File.mtime('VERSION.txt')
|
|
9
|
-
spec.author = 'Aaron D. Gifford'
|
|
10
|
-
spec.email = 'email_not_accepted@aarongifford.com'
|
|
11
|
-
spec.homepage = 'http://www.aarongifford.com/computers/mtik/'
|
|
12
|
-
spec.summary = 'MTik implements the MikroTik RouterOS API for use in Ruby.'
|
|
13
|
-
spec.description = 'MTik implements the MikroTik RouterOS API for use in Ruby.'
|
|
14
|
-
spec.rubyforge_project = 'mtik'
|
|
15
|
-
spec.extra_rdoc_files = [ 'README.txt' ]
|
|
16
|
-
spec.require_paths = [ 'lib' ]
|
|
17
|
-
spec.files = [
|
|
18
|
-
'CHANGELOG.txt',
|
|
19
|
-
'LICENSE.txt',
|
|
20
|
-
'README.txt',
|
|
21
|
-
'VERSION.txt',
|
|
22
|
-
'Rakefile',
|
|
23
|
-
'examples/tikjson.rb',
|
|
24
|
-
'bin/tikcli',
|
|
25
|
-
'bin/tikcommand',
|
|
26
|
-
'bin/tikfetch',
|
|
27
|
-
'lib/mtik.rb',
|
|
28
|
-
'lib/mtik/connection.rb',
|
|
29
|
-
'lib/mtik/error.rb',
|
|
30
|
-
'lib/mtik/fatalerror.rb',
|
|
31
|
-
'lib/mtik/reply.rb',
|
|
32
|
-
'lib/mtik/request.rb',
|
|
33
|
-
'lib/mtik/timeouterror.rb'
|
|
34
|
-
]
|
|
35
|
-
spec.executables = [ 'tikcli', 'tikcommand', 'tikfetch' ]
|
|
36
|
-
end
|
|
5
|
+
gemspec = Gem::Specification.load('mtik.gemspec')
|
|
37
6
|
|
|
38
7
|
Gem::PackageTask.new(gemspec) do |pkg|
|
|
39
8
|
pkg.need_zip = true
|
data/VERSION.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.1.2
|
data/bin/tikcli
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
########################################################################
|
|
3
3
|
#--
|
|
4
4
|
#
|
|
5
|
-
# FILE:
|
|
5
|
+
# FILE: tikcli -- Example of using the Ruby MikroTik API in Ruby
|
|
6
6
|
#
|
|
7
7
|
#++
|
|
8
|
-
# Author:: Aaron D. Gifford -
|
|
9
|
-
# Copyright:: Copyright (c) 2009-
|
|
8
|
+
# Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
9
|
+
# Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
10
10
|
# License:: BSD license
|
|
11
11
|
#--
|
|
12
12
|
# Redistribution and use in source and binary forms, with or without
|
|
@@ -42,10 +42,50 @@ $LOAD_PATH.unshift(File.dirname(__FILE__)+'/../lib')
|
|
|
42
42
|
require 'rubygems'
|
|
43
43
|
require 'mtik'
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
|
|
46
|
+
def usage(msg='')
|
|
47
|
+
STDERR.print(
|
|
48
|
+
(msg.size > 0 ? msg + "\n\n" : '') +
|
|
49
|
+
"Usage: #{$0} [-s|--ssl] [-u|--unencrypted_plaintext] <host> <user> <pass>\n" +
|
|
50
|
+
" --ssl OR -s - Use SSL for the API connection.\n" +
|
|
51
|
+
" --unencrypted_plaintext OR -u - Use the 6.43+ login API even if NOT\n" +
|
|
52
|
+
" using SSL.\n"
|
|
53
|
+
)
|
|
47
54
|
exit(-1)
|
|
48
55
|
end
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
use_ssl = unencrypted_plaintext = false
|
|
58
|
+
while !ARGV[0].nil? && ARGV[0][0] == '-'
|
|
59
|
+
arg = ARGV.shift
|
|
60
|
+
case arg
|
|
61
|
+
when '--ssl', '-s'
|
|
62
|
+
usage("Please do not repeat the --ssl (or -s) parameter") if use_ssl
|
|
63
|
+
use_ssl = true
|
|
64
|
+
when '--unencrypted_plaintext', '-u'
|
|
65
|
+
usage("Please do not repeat the --unencrypted_plaintext (or -u) parameter") if unencrypted_plaintext
|
|
66
|
+
unencrypted_plaintext = true
|
|
67
|
+
else
|
|
68
|
+
usage("Unknown argument #{arg.inspect}")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
usage("Too many arguments.") if ARGV.size > 3
|
|
72
|
+
usage("Insufficient arguments.") if ARGV.size < 3
|
|
73
|
+
|
|
74
|
+
## Permit setting use_ssl and unencrypted_plaintext via environment variables:
|
|
75
|
+
use_ssl = true if ENV['MTIK_SSL']
|
|
76
|
+
unencrypted_plaintext = true if ENV['MTIK_UNENCRYPTED_PLAINTEXT']
|
|
77
|
+
|
|
78
|
+
## NOTE: We IGNORE environment variable MTIK_VERBOSE mode as this script
|
|
79
|
+
## ALWAYS uses verbose mode.
|
|
80
|
+
|
|
81
|
+
args = {
|
|
82
|
+
:host => ARGV[0],
|
|
83
|
+
:user => ARGV[1],
|
|
84
|
+
:pass => ARGV[2],
|
|
85
|
+
:ssl => use_ssl,
|
|
86
|
+
:unencrypted_plaintext => unencrypted_plaintext
|
|
87
|
+
}
|
|
88
|
+
p args
|
|
89
|
+
|
|
90
|
+
MTik::interactive_client(args)
|
|
51
91
|
|
data/bin/tikcommand
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
########################################################################
|
|
3
3
|
#--
|
|
4
4
|
#
|
|
5
|
-
# FILE:
|
|
5
|
+
# FILE: tikcommand -- Example of using the Ruby MikroTik API in Ruby
|
|
6
6
|
#
|
|
7
7
|
#++
|
|
8
|
-
# Author:: Aaron D. Gifford -
|
|
9
|
-
# Copyright:: Copyright (c) 2009-
|
|
8
|
+
# Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
9
|
+
# Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
10
10
|
# License:: BSD license
|
|
11
11
|
#--
|
|
12
12
|
# Redistribution and use in source and binary forms, with or without
|
|
@@ -42,12 +42,46 @@ $LOAD_PATH.unshift(File.dirname(__FILE__)+'/../lib')
|
|
|
42
42
|
require 'rubygems'
|
|
43
43
|
require 'mtik'
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
|
|
46
|
+
def usage(msg='')
|
|
47
|
+
STDERR.print(
|
|
48
|
+
(msg.size > 0 ? msg + "\n\n" : '') +
|
|
49
|
+
"Usage: #{$0} [-s|--ssl] [-u|--unencrypted_plaintext] [-v|--verbose] <host> <user> <pass> <command> [<args>...] [<command> [<args> ...]]\n" +
|
|
50
|
+
" --unencrypted_plaintext OR -u - Use the 6.43+ login API even if NOT\n" +
|
|
51
|
+
" using SSL.\n" +
|
|
52
|
+
" --ssl OR -s - Use SSL for the API connection.\n" +
|
|
53
|
+
" --verbose OR -v - Enable verbose output via STDERR.\n"
|
|
54
|
+
)
|
|
47
55
|
exit(-1)
|
|
48
56
|
end
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
|
|
59
|
+
use_ssl = unencrypted_plaintext = verbose = false
|
|
60
|
+
while !ARGV[0].nil? && ARGV[0][0] == '-'
|
|
61
|
+
arg = ARGV.shift
|
|
62
|
+
case arg
|
|
63
|
+
when '--ssl', '-s'
|
|
64
|
+
usage("Please do not repeat the --ssl (or -s) parameter") if use_ssl
|
|
65
|
+
use_ssl = true
|
|
66
|
+
when '--unencrypted_plaintext', '-u'
|
|
67
|
+
usage("Please do not repeat the --unencrypted_plaintext (or -u) parameter") if unencrypted_plaintext
|
|
68
|
+
unencrypted_plaintext = true
|
|
69
|
+
when '--verbose', '-v'
|
|
70
|
+
usage("Please do not repeat the --verbose (or -v) parameter") if use_ssl
|
|
71
|
+
verbose = true
|
|
72
|
+
else
|
|
73
|
+
usage("Unknown argument #{arg.inspect}")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
usage("Too few arguments.") if ARGV.size < 4
|
|
77
|
+
usage("First command must start with a slash '/' character. #{ARGV[3].inspect}") if ARGV[3].nil? || ARGV[3][0] != '/'
|
|
78
|
+
|
|
79
|
+
## Permit setting use_ssl and unencrypted_plaintext via environment variables:
|
|
80
|
+
use_ssl = true if ENV['MTIK_SSL']
|
|
81
|
+
unencrypted_plaintext = true if ENV['MTIK_UNENCRYPTED_PLAINTEXT']
|
|
82
|
+
verbose = true if ENV['MTIK_VERBOSE']
|
|
83
|
+
|
|
84
|
+
MTik::verbose = verbose
|
|
51
85
|
|
|
52
86
|
## Detect multiple command sequences and build an array of arrays
|
|
53
87
|
## where each outer array element is a command plus arguments:
|
|
@@ -62,10 +96,14 @@ while i < ARGV.length
|
|
|
62
96
|
i += 1
|
|
63
97
|
end
|
|
64
98
|
|
|
65
|
-
|
|
66
|
-
:host=>ARGV[0],
|
|
67
|
-
:user=>ARGV[1],
|
|
68
|
-
:pass=>ARGV[2],
|
|
69
|
-
:command=>command
|
|
70
|
-
|
|
99
|
+
args = {
|
|
100
|
+
:host => ARGV[0],
|
|
101
|
+
:user => ARGV[1],
|
|
102
|
+
:pass => ARGV[2],
|
|
103
|
+
:command => command,
|
|
104
|
+
:ssl => use_ssl,
|
|
105
|
+
:unencrypted_plaintext => unencrypted_plaintext
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
p MTik::command(args)
|
|
71
109
|
|
data/bin/tikfetch
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
########################################################################
|
|
3
3
|
#--
|
|
4
4
|
#
|
|
5
|
-
# FILE:
|
|
5
|
+
# FILE: tikfetch -- Example of using the Ruby MikroTik API in Ruby
|
|
6
6
|
#
|
|
7
7
|
#++
|
|
8
|
-
# Author:: Aaron D. Gifford -
|
|
9
|
-
# Copyright:: Copyright (c) 2009-
|
|
8
|
+
# Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
9
|
+
# Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
10
10
|
# License:: BSD license
|
|
11
11
|
#--
|
|
12
12
|
# Redistribution and use in source and binary forms, with or without
|
|
@@ -42,20 +42,56 @@ $LOAD_PATH.unshift(File.dirname(__FILE__)+'/../lib')
|
|
|
42
42
|
require 'rubygems'
|
|
43
43
|
require 'mtik'
|
|
44
44
|
|
|
45
|
-
## Uncomment this (set $VERBOSE to true) if you want crazy verbose
|
|
46
|
-
## output of all API interactions:
|
|
47
|
-
#MTik::verbose = true
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
print
|
|
51
|
-
|
|
46
|
+
def usage(msg='')
|
|
47
|
+
STDERR.print(
|
|
48
|
+
(msg.size > 0 ? msg + "\n\n" : '') +
|
|
49
|
+
"Usage: #{$0} [-s|--ssl] [-u|--unencrypted_plaintext] [-v|--verbose] <device> <user> <pass> <url> [<localfilename> [<url> [<localfilename> ... ]]]\n" +
|
|
50
|
+
" --unencrypted_plaintext OR -u - Use the 6.43+ login API even if NOT\n" +
|
|
51
|
+
" using SSL.\n" +
|
|
52
|
+
" --ssl OR -s - Use SSL for the API connection.\n" +
|
|
53
|
+
" --verbose OR -v - Enable verbose output via STDOUT.\n"
|
|
54
|
+
)
|
|
55
|
+
exit(-1)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
use_ssl = unencrypted_plaintext = verbose = false
|
|
59
|
+
while !ARGV[0].nil? && ARGV[0][0] == '-'
|
|
60
|
+
arg = ARGV.shift
|
|
61
|
+
case arg
|
|
62
|
+
when '--ssl', '-s'
|
|
63
|
+
usage("Please do not repeat the --ssl (or -s) parameter") if use_ssl
|
|
64
|
+
use_ssl = true
|
|
65
|
+
when '--unencrypted_plaintext', '-u'
|
|
66
|
+
usage("Please do not repeat the --unencrypted_plaintext (or -u) parameter") if unencrypted_plaintext
|
|
67
|
+
unencrypted_plaintext = true
|
|
68
|
+
when '--verbose', '-v'
|
|
69
|
+
usage("Please do not repeat the --verbose (or -v) parameter") if verbose
|
|
70
|
+
verbose = true
|
|
71
|
+
else
|
|
72
|
+
usage("Unknown argument #{arg.inspect}")
|
|
73
|
+
end
|
|
52
74
|
end
|
|
75
|
+
usage("Too few arguments.") if ARGV.size < 4
|
|
76
|
+
|
|
77
|
+
## Permit setting use_ssl and unencrypted_plaintext via environment variables:
|
|
78
|
+
use_ssl = true if ENV['MTIK_SSL']
|
|
79
|
+
unencrypted_plaintext = true if ENV['MTIK_UNENCRYPTED_PLAINTEXT']
|
|
80
|
+
verbose = true if ENV['MTIK_VERBOSE']
|
|
81
|
+
|
|
82
|
+
MTik::verbose = verbose
|
|
83
|
+
|
|
84
|
+
args = {
|
|
85
|
+
:host => ARGV.shift,
|
|
86
|
+
:user => ARGV.shift,
|
|
87
|
+
:pass => ARGV.shift,
|
|
88
|
+
:ssl => use_ssl,
|
|
89
|
+
:unencrypted_plaintext => unencrypted_plaintext
|
|
90
|
+
}
|
|
91
|
+
|
|
53
92
|
|
|
54
|
-
host = ARGV.shift
|
|
55
|
-
user = ARGV.shift
|
|
56
|
-
pass = ARGV.shift
|
|
57
93
|
begin
|
|
58
|
-
mt = MTik::Connection.new(
|
|
94
|
+
mt = MTik::Connection.new(args)
|
|
59
95
|
rescue Errno::ETIMEDOUT, Errno::ENETUNREACH, Errno::EHOSTUNREACH, MTik::Error => e
|
|
60
96
|
print ">>> ERROR CONNECTING: #{e}\n"
|
|
61
97
|
exit
|
|
@@ -129,6 +165,7 @@ print "SIZE CREATED FILENAME\n"
|
|
|
129
165
|
print "====================================================================\n"
|
|
130
166
|
mt.get_reply_each('/file/getall') do |req, s|
|
|
131
167
|
unless s.key?('!done')
|
|
168
|
+
s['size'] = 'directory' if s['type'] == 'directory'
|
|
132
169
|
print "#{(s['size']+' ')[0,10]} #{s['creation-time']} #{s['name']}\n"
|
|
133
170
|
end
|
|
134
171
|
end
|
data/bin/tikjson
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
########################################################################
|
|
3
|
+
#--
|
|
4
|
+
#
|
|
5
|
+
# FILE: tikjson -- Example of using the Ruby MikroTik API in Ruby
|
|
6
|
+
# to execute an API command and retrieve results
|
|
7
|
+
# in JSON format
|
|
8
|
+
#
|
|
9
|
+
#++
|
|
10
|
+
# Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
11
|
+
# Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
12
|
+
# License:: BSD license
|
|
13
|
+
#--
|
|
14
|
+
# Redistribution and use in source and binary forms, with or without
|
|
15
|
+
# modification, are permitted provided that the following conditions
|
|
16
|
+
# are met:
|
|
17
|
+
# 1. Redistributions of source code must retain the above copyright
|
|
18
|
+
# notice, the above list of authors and contributors, this list of
|
|
19
|
+
# conditions and the following disclaimer.
|
|
20
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
|
21
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
22
|
+
# documentation and/or other materials provided with the distribution.
|
|
23
|
+
# 3. Neither the name of the author(s) or copyright holder(s) nor the
|
|
24
|
+
# names of any contributors may be used to endorse or promote products
|
|
25
|
+
# derived from this software without specific prior written permission.
|
|
26
|
+
#
|
|
27
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S), AUTHOR(S) AND
|
|
28
|
+
# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
|
29
|
+
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
30
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
31
|
+
# IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), AUTHOR(S), OR CONTRIBUTORS BE
|
|
32
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
33
|
+
# DCONSEQUENTIAL AMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
34
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
35
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
36
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
37
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
38
|
+
# THE POSSIBILITY OF SUCH DAMAGE.
|
|
39
|
+
########################################################################
|
|
40
|
+
# encoding: ASCII-8BIT
|
|
41
|
+
|
|
42
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)+'/../lib')
|
|
43
|
+
|
|
44
|
+
require 'rubygems'
|
|
45
|
+
require 'mtik'
|
|
46
|
+
require 'json'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def usage(msg='')
|
|
50
|
+
STDERR.print(
|
|
51
|
+
(msg.size > 0 ? msg + "\n\n" : '') +
|
|
52
|
+
"Usage: #{$0} [-p|--pretty] [-s|--ssl] [-u|--unencrypted_plaintext] [-v|--verbose] <host> <user> <pass> <command> [<args>...] [<command> [<args> ...]]\n" +
|
|
53
|
+
" --pretty OR -p - Output JSON using JSON.pretty_generate()\n" +
|
|
54
|
+
" --ssl OR -s - Use SSL for the API connection.\n" +
|
|
55
|
+
" --unencrypted_plaintext OR -u - Use the 6.43+ login API even if NOT\n" +
|
|
56
|
+
" using SSL.\n" +
|
|
57
|
+
" --verbose OR -v - Enable verbose output to STDERR.\n"
|
|
58
|
+
)
|
|
59
|
+
exit(-1)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
use_ssl = unencrypted_plaintext = pretty = verbose = false
|
|
63
|
+
while !ARGV[0].nil? && ARGV[0][0] == '-'
|
|
64
|
+
arg = ARGV.shift
|
|
65
|
+
case arg
|
|
66
|
+
when '--pretty', '-p'
|
|
67
|
+
usage("Please do not repeat the --pretty (or -p) parameter") if pretty
|
|
68
|
+
pretty = true
|
|
69
|
+
when '--ssl', '-s'
|
|
70
|
+
usage("Please do not repeat the --ssl (or -s) parameter") if use_ssl
|
|
71
|
+
use_ssl = true
|
|
72
|
+
when '--unencrypted_plaintext', '-u'
|
|
73
|
+
usage("Please do not repeat the --unencrypted_plaintext (or -u) parameter") if unencrypted_plaintext
|
|
74
|
+
unencrypted_plaintext = true
|
|
75
|
+
when '--verbose', '-v'
|
|
76
|
+
usage("Please do not repeat the --verbose (or -v) parameter") if verbose
|
|
77
|
+
verbose = true
|
|
78
|
+
else
|
|
79
|
+
usage("Unknown argument #{arg.inspect}")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
usage("Too few arguments.") if ARGV.size < 4
|
|
83
|
+
usage("First command must start with a slash '/' character. #{ARGV[3].inspect}") if ARGV[3].nil? || ARGV[3][0] != '/'
|
|
84
|
+
|
|
85
|
+
## Permit setting use_ssl and unencrypted_plaintext via environment variables:
|
|
86
|
+
use_ssl = true if ENV['MTIK_SSL']
|
|
87
|
+
unencrypted_plaintext = true if ENV['MTIK_UNENCRYPTED_PLAINTEXT']
|
|
88
|
+
verbose = true if ENV['MTIK_VERBOSE']
|
|
89
|
+
|
|
90
|
+
MTik::verbose = verbose
|
|
91
|
+
|
|
92
|
+
## Detect multiple command sequences and build an array of arrays
|
|
93
|
+
## where each outer array element is a command plus arguments:
|
|
94
|
+
command = Array.new
|
|
95
|
+
i = 3
|
|
96
|
+
while i < ARGV.length
|
|
97
|
+
if ARGV[i][0,1] == '/' ## Command detected...
|
|
98
|
+
command << [ ARGV[i] ]
|
|
99
|
+
else
|
|
100
|
+
command[command.length-1] << ARGV[i]
|
|
101
|
+
end
|
|
102
|
+
i += 1
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
args = {
|
|
106
|
+
:host => ARGV[0],
|
|
107
|
+
:user => ARGV[1],
|
|
108
|
+
:pass => ARGV[2],
|
|
109
|
+
:command => command,
|
|
110
|
+
:ssl => use_ssl,
|
|
111
|
+
:unencrypted_plaintext => unencrypted_plaintext
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if pretty
|
|
115
|
+
puts JSON.pretty_generate(MTik::command(args))
|
|
116
|
+
else
|
|
117
|
+
print MTik::command(args).to_json + "\n"
|
|
118
|
+
end
|
|
119
|
+
|
data/lib/mtik.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
############################################################################
|
|
2
2
|
## A Ruby library implementing the Ruby MikroTik API
|
|
3
3
|
############################################################################
|
|
4
|
-
## Author:: Aaron D. Gifford -
|
|
5
|
-
## Copyright:: Copyright (c) 2009-
|
|
4
|
+
## Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
5
|
+
## Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
6
6
|
## License:: BSD license
|
|
7
7
|
##
|
|
8
8
|
## Redistribution and use in source and binary forms, with or without
|
|
@@ -34,20 +34,22 @@
|
|
|
34
34
|
# encoding: ASCII-8BIT
|
|
35
35
|
|
|
36
36
|
module MTik
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
require_relative 'mtik/error.rb'
|
|
38
|
+
require_relative 'mtik/fatalerror.rb'
|
|
39
|
+
require_relative 'mtik/timeouterror.rb'
|
|
40
|
+
require_relative 'mtik/request.rb'
|
|
41
|
+
require_relative 'mtik/reply.rb'
|
|
42
|
+
require_relative 'mtik/connection.rb'
|
|
43
43
|
|
|
44
44
|
## Default MikroTik RouterOS API TCP port:
|
|
45
45
|
PORT = 8728
|
|
46
|
+
## Default MikroTik RouterOS API-SSL TCP port:
|
|
47
|
+
PORT_SSL = 8729
|
|
46
48
|
## Default username to use if none is specified:
|
|
47
49
|
USER = 'admin'
|
|
48
50
|
## Default password to use if none is specified:
|
|
49
51
|
PASS = ''
|
|
50
|
-
## Connection timeout default -- *NOT USED*
|
|
52
|
+
## Connection timeout default -- *NOT USED*
|
|
51
53
|
CONN_TIMEOUT = 60
|
|
52
54
|
## Command timeout -- The maximum number of seconds to wait for more
|
|
53
55
|
## API data when expecting one or more command responses.
|
|
@@ -56,6 +58,9 @@ module MTik
|
|
|
56
58
|
## Maximum number of replies before a command is auto-canceled:
|
|
57
59
|
MAXREPLIES = 1000
|
|
58
60
|
|
|
61
|
+
## SSL is set to false by default
|
|
62
|
+
USE_SSL = false
|
|
63
|
+
|
|
59
64
|
@verbose = false
|
|
60
65
|
@debug = false
|
|
61
66
|
|
|
@@ -81,12 +86,30 @@ module MTik
|
|
|
81
86
|
|
|
82
87
|
|
|
83
88
|
## Act as an interactive client with the device, accepting user
|
|
84
|
-
## input from STDIN.
|
|
85
|
-
|
|
89
|
+
## input from STDIN. Arguments are key/value pairs, and are
|
|
90
|
+
## simply passed directly to MTik::Connection(). The below
|
|
91
|
+
## documentation is taken directly from MTik::Connection. One
|
|
92
|
+
## more ## key/value pair style arguments must be specified.
|
|
93
|
+
## The one ## required argument is the host or IP of the device
|
|
94
|
+
## to connect to.
|
|
95
|
+
## +host+:: This is the only _required_ argument. Example:
|
|
96
|
+
## <i> :host => "rb411.example.org" </i>
|
|
97
|
+
## +ssl+:: Use SSL to encrypt communications
|
|
98
|
+
## +port+:: Override the default API port (8728/8729)
|
|
99
|
+
## +user+:: Override the default API username ('admin')
|
|
100
|
+
## +pass+:: Override the default API password (blank)
|
|
101
|
+
## +conn_timeout+:: Override the default connection
|
|
102
|
+
## timeout (60 seconds)
|
|
103
|
+
## +cmd_timeout+:: Override the default command timeout
|
|
104
|
+
## (60 seconds) -- the number of seconds
|
|
105
|
+
## to wait for additional API input.
|
|
106
|
+
## +unencrypted_plaintext+:: Attempt to use the 6.43+ login API
|
|
107
|
+
## even without SSL
|
|
108
|
+
def self.interactive_client(args)
|
|
86
109
|
old_verbose = MTik::verbose
|
|
87
110
|
MTik::verbose = true
|
|
88
111
|
begin
|
|
89
|
-
tk = MTik::Connection.new(
|
|
112
|
+
tk = MTik::Connection.new(args)
|
|
90
113
|
rescue MTik::Error, Errno::ECONNREFUSED => e
|
|
91
114
|
print "=== LOGIN ERROR: #{e.message}\n"
|
|
92
115
|
exit
|
|
@@ -127,7 +150,7 @@ module MTik
|
|
|
127
150
|
cmd == '/tool/fetch' && sentence['status'] == 'finished'
|
|
128
151
|
) || (maxreply > 0 && count == maxreply)
|
|
129
152
|
state = 2
|
|
130
|
-
req.cancel do |r, s|
|
|
153
|
+
req.cancel do |r, s|
|
|
131
154
|
state = 1
|
|
132
155
|
end
|
|
133
156
|
end
|
|
@@ -154,7 +177,7 @@ module MTik
|
|
|
154
177
|
end
|
|
155
178
|
end
|
|
156
179
|
end
|
|
157
|
-
|
|
180
|
+
|
|
158
181
|
reply = tk.get_reply('/quit')
|
|
159
182
|
unless reply[0].key?('!fatal')
|
|
160
183
|
raise MTik::Error.new("Unexpected response to '/quit' command.")
|
|
@@ -240,16 +263,9 @@ module MTik
|
|
|
240
263
|
## Remember that the limit applies separately to each API command
|
|
241
264
|
## executed.
|
|
242
265
|
def self.command(args)
|
|
243
|
-
tk
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
:pass => args[:pass],
|
|
247
|
-
:port => args[:port],
|
|
248
|
-
:conn_timeout => args[:conn_timeout],
|
|
249
|
-
:cmd_timeout => args[:cmd_timeout]
|
|
250
|
-
)
|
|
251
|
-
limit = args[:limit] ## Optional reply limit
|
|
252
|
-
cmd = args[:command]
|
|
266
|
+
tk = MTik::Connection.new(args)
|
|
267
|
+
limit = args[:limit] ## Optional reply limit
|
|
268
|
+
cmd = args[:command]
|
|
253
269
|
replies = Array.new
|
|
254
270
|
if cmd.is_a?(String)
|
|
255
271
|
## Single command, no arguments
|
data/lib/mtik/connection.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
############################################################################
|
|
2
2
|
## A Ruby library implementing the Ruby MikroTik API
|
|
3
3
|
############################################################################
|
|
4
|
-
## Author:: Aaron D. Gifford -
|
|
5
|
-
## Copyright:: Copyright (c) 2009-
|
|
4
|
+
## Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
5
|
+
## Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
6
6
|
## License:: BSD license
|
|
7
7
|
##
|
|
8
8
|
## Redistribution and use in source and binary forms, with or without
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
class MTik::Connection
|
|
40
40
|
require 'socket'
|
|
41
41
|
require 'digest/md5'
|
|
42
|
+
require 'openssl'
|
|
42
43
|
|
|
43
44
|
## Initialize/construct the new _MTik_ object. One or more
|
|
44
45
|
## key/value pair style arguments must be specified. The one
|
|
@@ -46,7 +47,8 @@ class MTik::Connection
|
|
|
46
47
|
## to.
|
|
47
48
|
## +host+:: This is the only _required_ argument. Example:
|
|
48
49
|
## <i> :host => "rb411.example.org" </i>
|
|
49
|
-
## +
|
|
50
|
+
## +ssl+:: Use SSL to encrypt communications
|
|
51
|
+
## +port+:: Override the default API port (8728/8729)
|
|
50
52
|
## +user+:: Override the default API username ('admin')
|
|
51
53
|
## +pass+:: Override the default API password (blank)
|
|
52
54
|
## +conn_timeout+:: Override the default connection
|
|
@@ -54,18 +56,22 @@ class MTik::Connection
|
|
|
54
56
|
## +cmd_timeout+:: Override the default command timeout
|
|
55
57
|
## (60 seconds) -- the number of seconds
|
|
56
58
|
## to wait for additional API input.
|
|
59
|
+
## +unencrypted_plaintext+:: Attempt to use the 6.43+ login API even without SSL
|
|
57
60
|
def initialize(args)
|
|
58
|
-
@sock
|
|
59
|
-
@
|
|
60
|
-
@
|
|
61
|
-
@
|
|
62
|
-
@
|
|
63
|
-
@
|
|
64
|
-
@
|
|
65
|
-
@
|
|
66
|
-
@
|
|
67
|
-
@
|
|
68
|
-
@
|
|
61
|
+
@sock = nil
|
|
62
|
+
@ssl_sock = nil
|
|
63
|
+
@requests = Hash.new
|
|
64
|
+
@use_ssl = args[:ssl] || MTik::USE_SSL
|
|
65
|
+
@unencrypted_plaintext = args[:unencrypted_plaintext]
|
|
66
|
+
@host = args[:host]
|
|
67
|
+
@port = args[:port] || (@use_ssl ? MTik::PORT_SSL : MTik::PORT)
|
|
68
|
+
@user = args[:user] || MTik::USER
|
|
69
|
+
@pass = args[:pass] || MTik::PASS
|
|
70
|
+
@conn_timeout = args[:conn_timeout] || MTik::CONN_TIMEOUT
|
|
71
|
+
@cmd_timeout = args[:cmd_timeout] || MTik::CMD_TIMEOUT
|
|
72
|
+
@data = ''
|
|
73
|
+
@parsing = false ## Recursion flag
|
|
74
|
+
@os_version = nil
|
|
69
75
|
|
|
70
76
|
## Initiate connection and immediately login to device:
|
|
71
77
|
login
|
|
@@ -95,28 +101,41 @@ class MTik::Connection
|
|
|
95
101
|
raise MTik::Error.new("Login failed: Unable to connect to device.")
|
|
96
102
|
end
|
|
97
103
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
# Try using the the post-6.43 login API; on older routers this still initiates
|
|
105
|
+
# a regular challenge-response cycle.
|
|
106
|
+
if @use_ssl || @unencrypted_plaintext
|
|
107
|
+
warn("SENDING PLAINTEXT PASSWORD OVER UNENCRYPTED CONNECTION") unless @use_ssl
|
|
108
|
+
reply = get_reply('/login',["=name=#{@user}","=password=#{@pass}"])
|
|
109
|
+
if reply.length == 1 && reply[0].length == 2 && reply[0].key?('!done')
|
|
110
|
+
v_6_43_login_successful = true
|
|
111
|
+
end
|
|
112
|
+
else
|
|
113
|
+
## Just send first /login command to obtain the challenge, if not using SSL
|
|
114
|
+
reply = get_reply('/login')
|
|
103
115
|
end
|
|
104
116
|
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
unless v_6_43_login_successful
|
|
118
|
+
## Make sure the reply has the info we expect for challenge-response authentication:
|
|
119
|
+
if reply.length != 1 || reply[0].length != 3 || !reply[0].key?('ret')
|
|
120
|
+
raise MTik::Error.new("Login failed: unexpected reply to login attempt.")
|
|
121
|
+
end
|
|
107
122
|
|
|
108
|
-
|
|
109
|
-
|
|
123
|
+
## Grab the challenge from first (only) sentence in the reply:
|
|
124
|
+
challenge = hex2bin(reply[0]['ret'])
|
|
110
125
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
## Generate reply MD5 hash and convert binary hash to hex string:
|
|
127
|
+
response = Digest::MD5.hexdigest(0.chr + @pass + challenge)
|
|
128
|
+
|
|
129
|
+
## Send second /login command with our response:
|
|
130
|
+
reply = get_reply('/login', '=name=' + @user, '=response=00' + response)
|
|
131
|
+
if reply[0].key?('!trap')
|
|
132
|
+
raise MTik::Error.new("Login failed: " + (reply[0].key?('message') ? reply[0]['message'] : 'Unknown error.'))
|
|
133
|
+
end
|
|
134
|
+
unless reply.length == 1 && reply[0].length == 2 && reply[0].key?('!done')
|
|
135
|
+
@sock.close
|
|
136
|
+
@sock = nil
|
|
137
|
+
raise MTik::Error.new('Login failed: Unknown response to login.')
|
|
138
|
+
end
|
|
120
139
|
end
|
|
121
140
|
|
|
122
141
|
## Request the RouterOS version of the device as different versions
|
|
@@ -132,27 +151,37 @@ class MTik::Connection
|
|
|
132
151
|
return unless @sock.nil?
|
|
133
152
|
## TODO: Perhaps catch more errors
|
|
134
153
|
begin
|
|
135
|
-
addr
|
|
154
|
+
addr = Socket.getaddrinfo(@host, nil)
|
|
136
155
|
@sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
|
137
156
|
|
|
138
157
|
begin
|
|
139
158
|
@sock.connect_nonblock(Socket.pack_sockaddr_in(@port, addr[0][3]))
|
|
140
159
|
rescue Errno::EINPROGRESS
|
|
141
160
|
ready = IO.select([@sock], [@sock], [], @conn_timeout)
|
|
142
|
-
|
|
143
|
-
@sock
|
|
144
|
-
else
|
|
145
|
-
raise Errno::ETIMEDOUT
|
|
161
|
+
raise Errno::ETIMEDOUT unless ready
|
|
146
162
|
end
|
|
147
|
-
end
|
|
148
163
|
|
|
149
|
-
|
|
164
|
+
connect_ssl(@sock) if @use_ssl
|
|
165
|
+
rescue Errno::ECONNREFUSED,
|
|
166
|
+
Errno::ETIMEDOUT,
|
|
167
|
+
Errno::ENETUNREACH,
|
|
150
168
|
Errno::EHOSTUNREACH => e
|
|
151
169
|
@sock = nil
|
|
152
170
|
raise e ## Re-raise the exception
|
|
153
171
|
end
|
|
154
172
|
end
|
|
155
173
|
|
|
174
|
+
def connect_ssl(sock)
|
|
175
|
+
ssl_context = OpenSSL::SSL::SSLContext.new()
|
|
176
|
+
ssl_context.ciphers = ['HIGH']
|
|
177
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
|
|
178
|
+
ssl_socket.sync_close = true
|
|
179
|
+
unless ssl_socket.connect
|
|
180
|
+
raise MTik::Error.new("Cannot establish SSL connection.")
|
|
181
|
+
end
|
|
182
|
+
@ssl_sock = ssl_socket
|
|
183
|
+
end
|
|
184
|
+
|
|
156
185
|
## Wait for and read exactly one sentence, regardless of content:
|
|
157
186
|
def get_sentence
|
|
158
187
|
## TODO: Implement timeouts, detect disconnection, maybe do auto-reconnect
|
|
@@ -206,7 +235,8 @@ class MTik::Connection
|
|
|
206
235
|
end
|
|
207
236
|
oldlen = @data.length
|
|
208
237
|
## Read some more data IF any is available:
|
|
209
|
-
|
|
238
|
+
sock = @ssl_sock || @sock
|
|
239
|
+
sel = IO.select([sock],nil,[sock], @cmd_timeout)
|
|
210
240
|
if sel.nil?
|
|
211
241
|
raise MTik::TimeoutError.new(
|
|
212
242
|
"Time-out while awaiting data with #{outstanding} pending " +
|
|
@@ -214,7 +244,7 @@ class MTik::Connection
|
|
|
214
244
|
)
|
|
215
245
|
end
|
|
216
246
|
if sel[0].length == 1
|
|
217
|
-
@data +=
|
|
247
|
+
@data += recv(8192)
|
|
218
248
|
elsif sel[2].length == 1
|
|
219
249
|
raise MTik::Error.new(
|
|
220
250
|
"I/O (select) error while awaiting data with #{outstanding} pending " +
|
|
@@ -384,10 +414,45 @@ class MTik::Connection
|
|
|
384
414
|
|
|
385
415
|
## Send the request object over the socket
|
|
386
416
|
def xmit(req)
|
|
387
|
-
|
|
417
|
+
begin
|
|
418
|
+
if @ssl_sock
|
|
419
|
+
@ssl_sock.write(req.request)
|
|
420
|
+
else
|
|
421
|
+
@sock.send(req.request, 0)
|
|
422
|
+
end
|
|
423
|
+
rescue Errno::EPIPE => e
|
|
424
|
+
@sock = @ssl_sock = nil
|
|
425
|
+
raise e ## Re-raise the exception
|
|
426
|
+
end
|
|
388
427
|
return req
|
|
389
428
|
end
|
|
390
429
|
|
|
430
|
+
def recv(buffer_size)
|
|
431
|
+
if @ssl_sock
|
|
432
|
+
recv_openssl(buffer_size)
|
|
433
|
+
else
|
|
434
|
+
@sock.recv(buffer_size)
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# 2 cases for backwards compatibility
|
|
439
|
+
def recv_openssl(buffer_size)
|
|
440
|
+
if OpenSSL::SSL.const_defined? 'SSLErrorWaitReadable'.freeze
|
|
441
|
+
begin
|
|
442
|
+
@ssl_sock.read_nonblock(buffer_size)
|
|
443
|
+
rescue OpenSSL::SSL::SSLErrorWaitReadable
|
|
444
|
+
''
|
|
445
|
+
end
|
|
446
|
+
else
|
|
447
|
+
begin
|
|
448
|
+
@ssl_sock.read_nonblock(buffer_size)
|
|
449
|
+
rescue OpenSSL::SSL::SSLError => e
|
|
450
|
+
return '' if e.message == 'read would block'.freeze
|
|
451
|
+
raise e
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
391
456
|
## Send a command, then wait for the command to complete, then return
|
|
392
457
|
## the completed reply.
|
|
393
458
|
##
|
|
@@ -420,8 +485,10 @@ class MTik::Connection
|
|
|
420
485
|
|
|
421
486
|
## Close the connection.
|
|
422
487
|
def close
|
|
423
|
-
return if @sock.nil?
|
|
424
|
-
@
|
|
488
|
+
return if @sock.nil? and @ssl_sock.nil?
|
|
489
|
+
@ssl_sock.close if @ssl_sock and !@ssl_sock.closed?
|
|
490
|
+
@sock.close if @sock and !@sock.closed?
|
|
491
|
+
@ssl_sock = nil
|
|
425
492
|
@sock = nil
|
|
426
493
|
end
|
|
427
494
|
|
data/lib/mtik/error.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
############################################################################
|
|
2
2
|
## A Ruby library implementing the Ruby MikroTik API
|
|
3
3
|
############################################################################
|
|
4
|
-
## Author:: Aaron D. Gifford -
|
|
5
|
-
## Copyright:: Copyright (c) 2009-
|
|
4
|
+
## Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
5
|
+
## Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
6
6
|
## License:: BSD license
|
|
7
7
|
##
|
|
8
8
|
## Redistribution and use in source and binary forms, with or without
|
data/lib/mtik/fatalerror.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
############################################################################
|
|
2
2
|
## A Ruby library implementing the Ruby MikroTik API
|
|
3
3
|
############################################################################
|
|
4
|
-
## Author:: Aaron D. Gifford -
|
|
5
|
-
## Copyright:: Copyright (c) 2009-
|
|
4
|
+
## Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
5
|
+
## Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
6
6
|
## License:: BSD license
|
|
7
7
|
##
|
|
8
8
|
## Redistribution and use in source and binary forms, with or without
|
data/lib/mtik/reply.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
############################################################################
|
|
2
2
|
## A Ruby library implementing the Ruby MikroTik API
|
|
3
3
|
############################################################################
|
|
4
|
-
## Author:: Aaron D. Gifford -
|
|
5
|
-
## Copyright:: Copyright (c) 2009-
|
|
4
|
+
## Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
5
|
+
## Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
6
6
|
## License:: BSD license
|
|
7
7
|
##
|
|
8
8
|
## Redistribution and use in source and binary forms, with or without
|
data/lib/mtik/request.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
############################################################################
|
|
2
2
|
## A Ruby library implementing the Ruby MikroTik API
|
|
3
3
|
############################################################################
|
|
4
|
-
## Author:: Aaron D. Gifford -
|
|
5
|
-
## Copyright:: Copyright (c) 2009-
|
|
4
|
+
## Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
5
|
+
## Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
6
6
|
## License:: BSD license
|
|
7
7
|
##
|
|
8
8
|
## Redistribution and use in source and binary forms, with or without
|
data/lib/mtik/timeouterror.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
############################################################################
|
|
2
2
|
## A Ruby library implementing the Ruby MikroTik API
|
|
3
3
|
############################################################################
|
|
4
|
-
## Author:: Aaron D. Gifford -
|
|
5
|
-
## Copyright:: Copyright (c) 2009-
|
|
4
|
+
## Author:: Aaron D. Gifford - https://aarongifford.com/
|
|
5
|
+
## Copyright:: Copyright (c) 2009-2020, InfoWest, Inc.
|
|
6
6
|
## License:: BSD license
|
|
7
7
|
##
|
|
8
8
|
## Redistribution and use in source and binary forms, with or without
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mtik
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aaron D. Gifford
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-08-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: MTik implements the MikroTik RouterOS API for use in Ruby.
|
|
14
14
|
email: email_not_accepted@aarongifford.com
|
|
@@ -16,6 +16,7 @@ executables:
|
|
|
16
16
|
- tikcli
|
|
17
17
|
- tikcommand
|
|
18
18
|
- tikfetch
|
|
19
|
+
- tikjson
|
|
19
20
|
extensions: []
|
|
20
21
|
extra_rdoc_files:
|
|
21
22
|
- README.txt
|
|
@@ -28,7 +29,7 @@ files:
|
|
|
28
29
|
- bin/tikcli
|
|
29
30
|
- bin/tikcommand
|
|
30
31
|
- bin/tikfetch
|
|
31
|
-
-
|
|
32
|
+
- bin/tikjson
|
|
32
33
|
- lib/mtik.rb
|
|
33
34
|
- lib/mtik/connection.rb
|
|
34
35
|
- lib/mtik/error.rb
|
|
@@ -36,27 +37,26 @@ files:
|
|
|
36
37
|
- lib/mtik/reply.rb
|
|
37
38
|
- lib/mtik/request.rb
|
|
38
39
|
- lib/mtik/timeouterror.rb
|
|
39
|
-
homepage:
|
|
40
|
+
homepage: https://aarongifford.com/computers/mtik/
|
|
40
41
|
licenses: []
|
|
41
42
|
metadata: {}
|
|
42
|
-
post_install_message:
|
|
43
|
+
post_install_message:
|
|
43
44
|
rdoc_options: []
|
|
44
45
|
require_paths:
|
|
45
46
|
- lib
|
|
46
47
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
47
48
|
requirements:
|
|
48
|
-
- -
|
|
49
|
+
- - ">="
|
|
49
50
|
- !ruby/object:Gem::Version
|
|
50
51
|
version: '0'
|
|
51
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
53
|
requirements:
|
|
53
|
-
- -
|
|
54
|
+
- - ">="
|
|
54
55
|
- !ruby/object:Gem::Version
|
|
55
56
|
version: '0'
|
|
56
57
|
requirements: []
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
signing_key:
|
|
58
|
+
rubygems_version: 3.0.6
|
|
59
|
+
signing_key:
|
|
60
60
|
specification_version: 4
|
|
61
61
|
summary: MTik implements the MikroTik RouterOS API for use in Ruby.
|
|
62
62
|
test_files: []
|
data/examples/tikjson.rb
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
########################################################################
|
|
3
|
-
#--
|
|
4
|
-
#
|
|
5
|
-
# FILE: tikjson.rb -- Example of using the Ruby MikroTik API in Ruby
|
|
6
|
-
# to execute an API command and retrieve results
|
|
7
|
-
# in JSON format
|
|
8
|
-
#
|
|
9
|
-
#++
|
|
10
|
-
# Author:: Aaron D. Gifford - http://www.aarongifford.com/
|
|
11
|
-
# Copyright:: Copyright (c) 2009-2014, InfoWest, Inc.
|
|
12
|
-
# License:: BSD license
|
|
13
|
-
#--
|
|
14
|
-
# Redistribution and use in source and binary forms, with or without
|
|
15
|
-
# modification, are permitted provided that the following conditions
|
|
16
|
-
# are met:
|
|
17
|
-
# 1. Redistributions of source code must retain the above copyright
|
|
18
|
-
# notice, the above list of authors and contributors, this list of
|
|
19
|
-
# conditions and the following disclaimer.
|
|
20
|
-
# 2. Redistributions in binary form must reproduce the above copyright
|
|
21
|
-
# notice, this list of conditions and the following disclaimer in the
|
|
22
|
-
# documentation and/or other materials provided with the distribution.
|
|
23
|
-
# 3. Neither the name of the author(s) or copyright holder(s) nor the
|
|
24
|
-
# names of any contributors may be used to endorse or promote products
|
|
25
|
-
# derived from this software without specific prior written permission.
|
|
26
|
-
#
|
|
27
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S), AUTHOR(S) AND
|
|
28
|
-
# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
|
29
|
-
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
30
|
-
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
31
|
-
# IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), AUTHOR(S), OR CONTRIBUTORS BE
|
|
32
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
33
|
-
# DCONSEQUENTIAL AMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
34
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
35
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
36
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
37
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
38
|
-
# THE POSSIBILITY OF SUCH DAMAGE.
|
|
39
|
-
########################################################################
|
|
40
|
-
# encoding: ASCII-8BIT
|
|
41
|
-
|
|
42
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__)+'/../lib')
|
|
43
|
-
|
|
44
|
-
require 'rubygems'
|
|
45
|
-
require 'mtik'
|
|
46
|
-
require 'json'
|
|
47
|
-
|
|
48
|
-
unless ARGV.length > 3
|
|
49
|
-
STDERR.print("Usage: #{$0} <host> <user> <pass> <command> [<args>...] [<command> [<args> ...]]\n")
|
|
50
|
-
exit(-1)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
## Detect multiple command sequences and build an array of arrays
|
|
54
|
-
## where each outer array element is a command plus arguments:
|
|
55
|
-
command = Array.new
|
|
56
|
-
i = 3
|
|
57
|
-
while i < ARGV.length
|
|
58
|
-
if ARGV[i][0,1] == '/' ## Command detected...
|
|
59
|
-
command << [ ARGV[i] ]
|
|
60
|
-
else
|
|
61
|
-
command[command.length-1] << ARGV[i]
|
|
62
|
-
end
|
|
63
|
-
i += 1
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
print MTik::command(
|
|
67
|
-
:host=>ARGV[0],
|
|
68
|
-
:user=>ARGV[1],
|
|
69
|
-
:pass=>ARGV[2],
|
|
70
|
-
:command=>command
|
|
71
|
-
).to_json + "\n"
|
|
72
|
-
|