mtik 4.0.0 → 4.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []