mtik 4.0.0 → 4.0.5

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0c85bb344286cb8faf3504f84bb617a6af422a5703972e8b9795601c2bc0b0ee
4
+ data.tar.gz: 9f727afa04a97fc3b366c54c637b6ba5274a057a9b460188022a2dc986ce0dca
5
+ SHA512:
6
+ metadata.gz: 0ceb27e8ea2f409882128ace3557563987f4e3a4d140d504e304ed903c51ef9025b4fd4aab6a3efbfb52063a7008a59f029e48ef8d094c7e0ceca6d153bc2c41
7
+ data.tar.gz: 9ac0bd5e67ad3004c427637edb3436f96eec96298678b440b76cdd9582bf3ca98e3dc000a1c3e3990761236b9f3858633b72f785b4a931d980d67d3fdab2353a
@@ -1,3 +1,26 @@
1
+ 2020-08-22 (22 AUG 2020) VERSION 4.0.5
2
+ * This is a cosmetic version bump for the purpose of updating the gem for wider
3
+ availability via rubygems in addition to directly from github prior to some
4
+ coming feature updates and fixes for newer versions of RouterOS
5
+
6
+ 2019-07-26 (26 JUL 2020) VERSION 4.0.4 Adam Kubica (github user xcr)
7
+ * Adam bumped the version number and separated out a gemspec file
8
+ * Jiacheng (github user krhougs) had submitted a similar update as Adam
9
+
10
+ 2014-02-14 (14 FEB 2014) VERSION 4.0.3 Aaron D. Gifford (http://www.aarongifford.com)
11
+ * Update to fetch() utility, along with some very minor some cosmetic changes
12
+
13
+ 2013-06-06 (06 JUN 2013) VERSION 4.0.2 Aaron D. Gifford (http://www.aarongifford.com)
14
+ Bart Braem (http://www.lalunerouge.net/)
15
+ * Merged Bart Braem's implementation of timeouts and bumped up the version. Thanks, Bart!
16
+ * Updated Rakefile to remove a bit of obsolescence
17
+
18
+ 2012-02-09 (09 FEB 2012) VERSION 4.0.1 Aaron D. Gifford (http://www.aarongifford.com)
19
+ * Added os_version to connections. Upon successful connect and login, the RouterOS
20
+ version is fetched and stored. This will allow future updates to better support
21
+ some commands that differ (like fetch) depending on which RouterOS version is
22
+ installed on the device.
23
+
1
24
  2011-03-25 (25 MAR 2011) VERSION 4.0.0 Aaron D. Gifford (http://www.aarongifford.com)
2
25
  * Per user suggestion, added a new optional cancel parameter to the MTik#command()
3
26
  method that will auto-cancel the supplied command after receiving the specified
@@ -13,6 +36,7 @@
13
36
  to symbol and fixed those, updated error messages to reflect state as a symbol,
14
37
  eliminated a few redundant key?() calls, and fixed a replycounter initialization
15
38
  typo (had set it to 1 instead of 0).
39
+
16
40
  2011-01-11 (11 JAN 2011) VERSION 3.1.2 Aaron D. Gifford (http://www.aarongifford.com)
17
41
  * Added source file encoding comments and updated the copyright notices
18
42
  * Fixed a tiny bug in lib/mtik/connection.rb
@@ -85,3 +109,4 @@
85
109
  fetch.rb
86
110
  * Added VERSION.txt, CHANGELOG.txt, README.txt, LICENSE.txt, and *.gemspec files, moved
87
111
  the example files into the bin subdirectory
112
+
data/README.txt CHANGED
@@ -47,7 +47,7 @@ For documentation on the MikroTik RouterOS APi, see
47
47
 
48
48
  To install MTik is through its GEM file:
49
49
 
50
- % [sudo] gem install mtik-3.1.0.gem
50
+ % [sudo] gem install mtik-4.0.0.gem
51
51
 
52
52
  == License
53
53
 
data/Rakefile CHANGED
@@ -1,46 +1,15 @@
1
1
  require 'rubygems'
2
- require 'rake/gempackagetask'
3
- require 'rake/rdoctask'
2
+ require 'rubygems/package_task'
3
+ require 'rdoc/task'
4
4
 
5
- gemspec = Gem::Specification.new do |spec|
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
- Rake::GemPackageTask.new(gemspec) do |pkg|
7
+ Gem::PackageTask.new(gemspec) do |pkg|
39
8
  pkg.need_zip = true
40
9
  pkg.need_tar = true
41
10
  end
42
11
 
43
- Rake::RDocTask.new do |rdoc|
12
+ RDoc::Task.new do |rdoc|
44
13
  rdoc.name = 'rdoc'
45
14
  rdoc.main = 'README.txt'
46
15
  rdoc.rdoc_dir = 'doc'
@@ -1 +1 @@
1
- 4.0.0
1
+ 4.0.5
@@ -2,11 +2,13 @@
2
2
  ########################################################################
3
3
  #--
4
4
  #
5
- # FILE: json.rb -- Example of using the Ruby MikroTik API in Ruby
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
6
8
  #
7
9
  #++
8
10
  # Author:: Aaron D. Gifford - http://www.aarongifford.com/
9
- # Copyright:: Copyright (c) 2009-2011, InfoWest, Inc.
11
+ # Copyright:: Copyright (c) 2009-2014, InfoWest, Inc.
10
12
  # License:: BSD license
11
13
  #--
12
14
  # Redistribution and use in source and binary forms, with or without
@@ -34,15 +34,17 @@
34
34
  # encoding: ASCII-8BIT
35
35
 
36
36
  module MTik
37
- require 'mtik/error.rb'
38
- require 'mtik/fatalerror.rb'
39
- require 'mtik/timeouterror.rb'
40
- require 'mtik/request.rb'
41
- require 'mtik/reply.rb'
42
- require 'mtik/connection.rb'
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:
@@ -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
 
@@ -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,25 +47,31 @@ 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
- ## +port+:: Override the default API port (8728)
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
53
- ## timeout (60 seconds) -- *NOT USED*
55
+ ## timeout (60 seconds)
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 = nil
59
- @requests = Hash.new
60
- @host = args[:host]
61
- @port = args[:port] || MTik::PORT
62
- @user = args[:user] || MTik::USER
63
- @pass = args[:pass] || MTik::PASS
64
- @conn_timeout = args[:conn_timeout] || MTik::CONN_TIMEOUT
65
- @cmd_timeout = args[:cmd_timeout] || MTik::CMD_TIMEOUT
66
- @data = ''
67
- @parsing = false ## Recursion flag
61
+ @sock = nil
62
+ @ssl_sock = nil
63
+ @requests = Hash.new
64
+ @use_ssl = args[:ssl] || MTik::USE_SSL
65
+ @unencrypted_plaintext = args[:unecrypted_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
68
75
 
69
76
  ## Initiate connection and immediately login to device:
70
77
  login
@@ -74,7 +81,8 @@ class MTik::Connection
74
81
  def outstanding
75
82
  return @requests.length
76
83
  end
77
- attr_reader :requests, :host, :port, :user, :pass, :conn_timeout, :cmd_timeout
84
+ attr_reader :requests, :host, :port, :user, :pass, :conn_timeout, :cmd_timeout,
85
+ :os_version
78
86
 
79
87
  ## Internal utility function:
80
88
  ## Sugar-coat ["0deadf0015"].pack('H*') so one can just do
@@ -93,37 +101,72 @@ class MTik::Connection
93
101
  raise MTik::Error.new("Login failed: Unable to connect to device.")
94
102
  end
95
103
 
96
- ## Send first /login command to obtain the challenge:
97
- reply = get_reply('/login')
98
- ## Make sure the reply has the info we expect:
99
- if reply.length != 1 || reply[0].length != 3 || !reply[0].key?('ret')
100
- raise MTik::Error.new("Login failed: unexpected reply to login attempt.")
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')
101
115
  end
102
116
 
103
- ## Grab the challenge from first (only) sentence in the reply:
104
- challenge = hex2bin(reply[0]['ret'])
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
105
122
 
106
- ## Generate reply MD5 hash and convert binary hash to hex string:
107
- response = Digest::MD5.hexdigest(0.chr + @pass + challenge)
123
+ ## Grab the challenge from first (only) sentence in the reply:
124
+ challenge = hex2bin(reply[0]['ret'])
108
125
 
109
- ## Send second /login command with our response:
110
- reply = get_reply('/login', '=name=' + @user, '=response=00' + response)
111
- if reply[0].key?('!trap')
112
- raise MTik::Error.new("Login failed: " + (reply[0].key?('message') ? reply[0]['message'] : 'Unknown error.'))
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
113
139
  end
114
- unless reply.length == 1 && reply[0].length == 2 && reply[0].key?('!done')
115
- @sock.close
116
- @sock = nil
117
- raise MTik::Error.new('Login failed: Unknown response to login.')
140
+
141
+ ## Request the RouterOS version of the device as different versions
142
+ ## sometimes use slightly different command parameters:
143
+ reply = get_reply('/system/resource/getall')
144
+ if reply.first.key?('!re') && reply.first['version']
145
+ @os_version = reply.first['version']
118
146
  end
119
147
  end
120
148
 
121
149
  ## Connect to the device
122
150
  def connect
123
151
  return unless @sock.nil?
124
- ## TODO: Perhaps catch more errors; implement connection timeout
152
+ ## TODO: Perhaps catch more errors
125
153
  begin
126
- @sock = TCPSocket::new(@host, @port)
154
+ addr = Socket.getaddrinfo(@host, nil)
155
+ @sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
156
+
157
+ begin
158
+ @sock.connect_nonblock(Socket.pack_sockaddr_in(@port, addr[0][3]))
159
+ rescue Errno::EINPROGRESS
160
+ ready = IO.select([@sock], [@sock], [], @conn_timeout)
161
+ if ready
162
+ @sock
163
+ else
164
+ raise Errno::ETIMEDOUT
165
+ end
166
+ end
167
+
168
+ connect_ssl(@sock) if @use_ssl
169
+
127
170
  rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::ENETUNREACH,
128
171
  Errno::EHOSTUNREACH => e
129
172
  @sock = nil
@@ -131,6 +174,17 @@ class MTik::Connection
131
174
  end
132
175
  end
133
176
 
177
+ def connect_ssl(sock)
178
+ ssl_context = OpenSSL::SSL::SSLContext.new()
179
+ ssl_context.ciphers = ['HIGH']
180
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
181
+ ssl_socket.sync_close = true
182
+ unless ssl_socket.connect
183
+ raise MTik::Error.new("Cannot establish SSL connection.")
184
+ end
185
+ @ssl_sock = ssl_socket
186
+ end
187
+
134
188
  ## Wait for and read exactly one sentence, regardless of content:
135
189
  def get_sentence
136
190
  ## TODO: Implement timeouts, detect disconnection, maybe do auto-reconnect
@@ -184,7 +238,8 @@ class MTik::Connection
184
238
  end
185
239
  oldlen = @data.length
186
240
  ## Read some more data IF any is available:
187
- sel = IO.select([@sock],nil,[@sock], @cmd_timeout)
241
+ sock = @ssl_sock || @sock
242
+ sel = IO.select([sock],nil,[sock], @cmd_timeout)
188
243
  if sel.nil?
189
244
  raise MTik::TimeoutError.new(
190
245
  "Time-out while awaiting data with #{outstanding} pending " +
@@ -192,7 +247,7 @@ class MTik::Connection
192
247
  )
193
248
  end
194
249
  if sel[0].length == 1
195
- @data += @sock.recv(8192)
250
+ @data += recv(8192)
196
251
  elsif sel[2].length == 1
197
252
  raise MTik::Error.new(
198
253
  "I/O (select) error while awaiting data with #{outstanding} pending " +
@@ -235,7 +290,7 @@ class MTik::Connection
235
290
  sentence = get_sentence ## This call must be ATOMIC or re-entrant safety fails
236
291
 
237
292
  ## Check for '!fatal' before checking for a tag--'!fatal'
238
- ## is never(???) tagged:
293
+ ## is never(???) tagged:
239
294
  if sentence.key?('!fatal')
240
295
  ## FATAL ERROR has occured! (Or a '/quit' command was issued...)
241
296
  if @data.length > 0
@@ -334,7 +389,7 @@ class MTik::Connection
334
389
  ## +args+:: Zero or more arguments to the command
335
390
  ## +callback+:: Proc/lambda code (or code block if not provided as
336
391
  ## an argument) to be called. (See the +await_completion+
337
- ##
392
+ ##
338
393
  def send_request(await_completion, command, *args, &callback)
339
394
  if await_completion.is_a?(MTik::Request)
340
395
  req = await_completion
@@ -362,10 +417,41 @@ class MTik::Connection
362
417
 
363
418
  ## Send the request object over the socket
364
419
  def xmit(req)
365
- @sock.send(req.request, 0)
420
+ if @ssl_sock
421
+ @ssl_sock.write(req.request)
422
+ else
423
+ @sock.send(req.request, 0)
424
+ end
425
+
366
426
  return req
367
427
  end
368
428
 
429
+ def recv(buffer_size)
430
+ if @ssl_sock
431
+ recv_openssl(buffer_size)
432
+ else
433
+ @sock.recv(buffer_size)
434
+ end
435
+ end
436
+
437
+ # 2 cases for backwards compatibility
438
+ def recv_openssl(buffer_size)
439
+ if OpenSSL::SSL.const_defined? 'SSLErrorWaitReadable'.freeze
440
+ begin
441
+ @ssl_sock.read_nonblock(buffer_size)
442
+ rescue OpenSSL::SSL::SSLErrorWaitReadable
443
+ ''
444
+ end
445
+ else
446
+ begin
447
+ @ssl_sock.read_nonblock(buffer_size)
448
+ rescue OpenSSL::SSL::SSLError => e
449
+ return '' if e.message == 'read would block'.freeze
450
+ raise e
451
+ end
452
+ end
453
+ end
454
+
369
455
  ## Send a command, then wait for the command to complete, then return
370
456
  ## the completed reply.
371
457
  ##
@@ -398,8 +484,10 @@ class MTik::Connection
398
484
 
399
485
  ## Close the connection.
400
486
  def close
401
- return if @sock.nil?
402
- @sock.close
487
+ return if @sock.nil? and @ssl_sock.nil?
488
+ @ssl_sock.close if @ssl_sock and !@ssl_sock.closed?
489
+ @sock.close if @sock and !@sock.closed?
490
+ @ssl_sock = nil
403
491
  @sock = nil
404
492
  end
405
493
 
@@ -503,16 +591,53 @@ class MTik::Connection
503
591
  ## +total+:: Final expected file size in bytes
504
592
  ## +bytes+:: Number of bytes transferred so far
505
593
  ## +request+:: The MTik::Request object
506
- def fetch(url, filename, timeout=nil, &callback)
594
+ def fetch(url, filename=nil, timeout=nil, &callback)
595
+ require 'uri'
596
+
597
+ uri = URI(url)
598
+ filename = File.basename(uri.path) if filename.nil?
599
+
507
600
  total = bytes = oldbytes = 0
508
601
  status = ''
509
602
  done = false
510
603
  lastactivity = Time.now
511
- req = get_reply_each(
512
- '/tool/fetch',
513
- '=url=' + url,
514
- '=dst-path=' + filename
515
- ) do |r, s|
604
+
605
+ ## RouterOS versions 4.9 and prior (not sure if this version cut-off
606
+ ## is exactly right) would accept the url parameter, but failed to
607
+ ## download the files. So for versions older than this, we'll use
608
+ ## the mode/src-path/port parameters instead if possible.
609
+ if !@os_version.nil? && lambda {|a,b|
610
+ sr = %r{(?:\.|rc|beta|alpha)}
611
+ a = a.split(sr).map{|i| i.to_i}
612
+ b = b.split(sr).map{|i| i.to_i}
613
+ i = 0
614
+ while i < a.size && i < b.size
615
+ return -1 if a[i] < b[i]
616
+ return 1 if a[i] > b[i]
617
+ i += 1
618
+ end
619
+ return a.size <=> b.size
620
+ }.call(@os_version, '4.9') < 1
621
+ command = [
622
+ '/tool/fetch', '=mode=' + uri.scheme,
623
+ '=src-path=' + uri.path + (uri.query.size > 0 ? '?' + uri.query : ''),
624
+ '=dst-path=' + filename
625
+ ]
626
+ case uri.scheme
627
+ when 'http'
628
+ command << '=port=80'
629
+ when 'https'
630
+ command << '=port=443'
631
+ end
632
+ else
633
+ command = [
634
+ '/tool/fetch',
635
+ '=url=' + url,
636
+ '=dst-path=' + filename
637
+ ]
638
+ end
639
+
640
+ req = get_reply_each(command[0], *command[1..-1]) do |r, s|
516
641
  if s.key?('!re') && !done
517
642
  unless s.key?('status')
518
643
  raise MTik::Error.new("Unknown response to '/tool/fetch': missing 'status' in response.")
@@ -602,7 +727,7 @@ class MTik::Connection
602
727
  else
603
728
  raise MTik::Error.new("Invalid settings match class '#{keyitem}' (expected Array, Regexp, or String)")
604
729
  end
605
-
730
+
606
731
  if s.key?(key)
607
732
  ## A key matches! && s[k] != v
608
733
  oldv = s[k]
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mtik
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
5
- prerelease:
4
+ version: 4.0.5
6
5
  platform: ruby
7
6
  authors:
8
7
  - Aaron D. Gifford
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2011-03-25 00:00:00.000000000Z
11
+ date: 2020-08-22 00:00:00.000000000 Z
13
12
  dependencies: []
14
13
  description: MTik implements the MikroTik RouterOS API for use in Ruby.
15
14
  email: email_not_accepted@aarongifford.com
@@ -24,12 +23,12 @@ files:
24
23
  - CHANGELOG.txt
25
24
  - LICENSE.txt
26
25
  - README.txt
27
- - VERSION.txt
28
26
  - Rakefile
29
- - examples/tikjson.rb
27
+ - VERSION.txt
30
28
  - bin/tikcli
31
29
  - bin/tikcommand
32
30
  - bin/tikfetch
31
+ - examples/tikjson.rb
33
32
  - lib/mtik.rb
34
33
  - lib/mtik/connection.rb
35
34
  - lib/mtik/error.rb
@@ -39,26 +38,24 @@ files:
39
38
  - lib/mtik/timeouterror.rb
40
39
  homepage: http://www.aarongifford.com/computers/mtik/
41
40
  licenses: []
42
- post_install_message:
41
+ metadata: {}
42
+ post_install_message:
43
43
  rdoc_options: []
44
44
  require_paths:
45
45
  - lib
46
46
  required_ruby_version: !ruby/object:Gem::Requirement
47
- none: false
48
47
  requirements:
49
- - - ! '>='
48
+ - - ">="
50
49
  - !ruby/object:Gem::Version
51
50
  version: '0'
52
51
  required_rubygems_version: !ruby/object:Gem::Requirement
53
- none: false
54
52
  requirements:
55
- - - ! '>='
53
+ - - ">="
56
54
  - !ruby/object:Gem::Version
57
55
  version: '0'
58
56
  requirements: []
59
- rubyforge_project: mtik
60
- rubygems_version: 1.7.2
61
- signing_key:
62
- specification_version: 3
57
+ rubygems_version: 3.0.6
58
+ signing_key:
59
+ specification_version: 4
63
60
  summary: MTik implements the MikroTik RouterOS API for use in Ruby.
64
61
  test_files: []