opentofu 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1eeac402d86ae7cc5b5f2e9be3327a60f8f3132453854ea9ee5f477462e58fc7
4
+ data.tar.gz: 54f68d912adb5e14a39ddde842ad4acf5ff3664923815138e5a30fcc8ba87881
5
+ SHA512:
6
+ metadata.gz: c74b24eada5091a0d37eb1a97b871838744e2169b36974fed374900c0a64e73acafe019751341da153f6f5e59a0b6e48e2c4aa9b1674fa65aa3573a74be44528
7
+ data.tar.gz: 8c00e948a371088cd8e87698d3884fe55d7aa8b84ac65c5a5d64d6b0a53cbf505fed91010cd4d83e53f820a1b2a6dac46a9ad5947733546404e1d992d0301bf5
data/.rubocop.yml ADDED
@@ -0,0 +1,34 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Metrics/MethodLength:
5
+ Enabled: true
6
+ Max: 18
7
+ Exclude:
8
+ - "lib/net/tofu/response.rb"
9
+
10
+ Style/ClassVars:
11
+ Enabled: true
12
+ Exclude:
13
+ - "lib/net/tofu/request.rb"
14
+
15
+ Style/Semicolon:
16
+ Enabled: true
17
+ Exclude:
18
+ - "lib/net/tofu/response.rb"
19
+
20
+ Style/SingleLineMethods:
21
+ Enabled: true
22
+ Exclude:
23
+ - "lib/net/tofu/response.rb"
24
+
25
+ Style/StringLiterals:
26
+ Enabled: true
27
+ EnforcedStyle: double_quotes
28
+
29
+ Style/StringLiteralsInInterpolation:
30
+ Enabled: true
31
+ EnforcedStyle: double_quotes
32
+
33
+ Layout/LineLength:
34
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2023-07027
9
+
10
+ First release. Still lots of improvements to be made.
11
+
12
+ ### Added
13
+
14
+ - Custom error classes for the library.
15
+ - A Request model for handling client requests.
16
+ - Handles the URI parsing logic.
17
+ - Holds a Response type.
18
+ - Does some basic error checking based on the Gemini Protocol specification.
19
+ - A Response model for handling server responses.
20
+ - Holds a Socket type.
21
+ - Does some basic error checking based on the Gemini Protocol specification.
22
+ - A Socket model for handling raw socket connections, as well as TLS security settings.
23
+ - A top-level module for making get and get_response requests.
24
+ - Added a 'trust' parameter to the Net::Tofu.get and Net::Tofu.get_response methods. This will later be used for certificate management, but it isn't in use yet.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Rory Dudley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # OpenTOFU
2
+
3
+ OpenTOFU is a certificate pinning and client library for Geminispace.
4
+ It is based off of the ['trust on first use'](https://en.wikipedia.org/wiki/Trust_on_first_use) authentication scheme.
5
+
6
+ ## Installation
7
+
8
+ To install the gem standalone:
9
+
10
+ ```sh
11
+ gem install opentofu
12
+ ```
13
+
14
+ Or to use it as part of a project, place the following line in your Gemfile, then run `bundle install` in your project directory:
15
+
16
+ ```ruby
17
+ gem "opentofu", "~> 0.1.0"
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+ require "net/tofu"
24
+ ```
25
+
26
+ ## Credits
27
+
28
+ I'd like to thank Étienne Deparis, author of [ruby-net-text](https://git.umaneti.net/ruby-net-text/) for releasing their code under the MIT license so that some of it can be used in this project. In particular, `lib/ui/gemini.rb` is taken straight from that codebase. The license for it is present in the aformentioned file.
29
+
30
+ ## Development
31
+
32
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
33
+
34
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
35
+
36
+ ## Contributing
37
+
38
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pinecat/opentofu.
39
+
40
+ ## License
41
+
42
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ require "rubocop/rake_task"
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ task default: %i[test rubocop]
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ module Tofu
5
+ class Response
6
+ # Raised when a server sends an invalid header.
7
+ class InvalidHeaderError < StandardError; end
8
+
9
+ # Raised when a server sends an invalid status code.
10
+ class InvalidStatusCodeError < StandardError; end
11
+
12
+ # Raised when a server sends an invalid meta field.
13
+ class InvalidMetaError < StandardError; end
14
+
15
+ # Raised when a server sends an invalid redirect link.
16
+ class InvalidRedirectError < StandardError; end
17
+ end
18
+
19
+ class Request
20
+ # Raised when a request contains an invalid scheme.
21
+ class InvalidSchemeError < StandardError; end
22
+
23
+ # Raised when a request contains an invalid URI.
24
+ class InvalidURIError < StandardError; end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ module Tofu
5
+ # Stores a client request to a Gemini server.
6
+ class Request
7
+ SCHEME = "gemini"
8
+
9
+ MAX_URI_BYTESIZE = 1024
10
+
11
+ # @return [URI] The full URI object of the request.
12
+ attr_reader :uri
13
+
14
+ # @return [String] The request scheme (i.e. gemini://, http://).
15
+ attr_reader :scheme
16
+
17
+ # @return [String] The hostname of the request.
18
+ attr_reader :host
19
+
20
+ # @return [Integer] The server port to connect on.
21
+ attr_reader :port
22
+
23
+ # @return [String] The requested path on the host.
24
+ attr_reader :path
25
+
26
+ # @return [Array] Additional queries to send to the host.
27
+ attr_reader :queries
28
+
29
+ # @return [String] A fragment to request from the host.
30
+ attr_reader :fragment
31
+
32
+ # @return [Response] The response from the server after calling #{gets}.
33
+ attr_reader :resp
34
+
35
+ # Constructor for the request type.
36
+ # @param host [String] A host string, optionally with the gemini:// scheme.
37
+ # @param port [Integer] Optional parameter to specify the server port to connect to.
38
+ def initialize(host, port: nil)
39
+ # Keeps track of the current host for links with only paths.
40
+ @@current_host ||= ""
41
+
42
+ @host = host
43
+ @port = port unless port.nil?
44
+ determine_host
45
+ parse_head
46
+ parse_tail
47
+
48
+ puts format
49
+
50
+ # Make sure the URI isn't too large
51
+ if format.bytesize > MAX_URI_BYTESIZE
52
+ raise InvalidURIError,
53
+ "The URI is too large, should be #{MAX_URI_BYTESIZE} bytes, instead is #{format.bytesize} bytes"
54
+ end
55
+
56
+ # Create a socket
57
+ @sock = Socket.new(@host, @port)
58
+ end
59
+
60
+ # Format the URI for sending over a socket to a Gemini server.
61
+ # @return [String] The URI string appended with a carriage return and linefeed.
62
+ def format
63
+ "#{@uri}\r\n"
64
+ end
65
+
66
+ # Connect to the server and try to fetch data.
67
+ def gets
68
+ @sock.connect
69
+ @resp = @sock.gets(self)
70
+ ensure
71
+ @sock.close
72
+ end
73
+
74
+ private
75
+
76
+ # Parses the host and the path, and sets the current_host.
77
+ def determine_host
78
+ puts "current: #{@@current_host}"
79
+ @uri = URI(@host)
80
+
81
+ unless @uri.host.nil? || @uri.host.empty?
82
+ @host = @uri.host
83
+ @@current_host = @host
84
+ return
85
+ end
86
+
87
+ return if @uri.path.nil? || @uri.path.empty?
88
+ return unless @uri.host.nil? || @uri.host.empty?
89
+
90
+ if @uri.path.start_with?("/")
91
+ raise InvalidURIError, "No host specified" if @@current_host.nil? || @@current_host.empty?
92
+
93
+ unless @@current_host.nil? || @@current_host.empty?
94
+ @uri.host = @@current_host
95
+ @host = @uri.host
96
+ @@current_host = @host
97
+ end
98
+
99
+ return
100
+ end
101
+
102
+ paths = @uri.path.split("/")
103
+ puts paths
104
+
105
+ @uri.host = paths[0]
106
+ @host = @uri.host
107
+ @@current_host = @host
108
+ @uri.path = nil if paths.length == 1
109
+ return unless paths.length > 1
110
+
111
+ @uri.path = paths[1..].join("/")
112
+ @uri.path = "/#{uri.path}"
113
+ end
114
+
115
+ # Parses the scheme, the host, and the port for the request.
116
+ def parse_head
117
+ # Check if a scheme was specified, if not, default to gemini://
118
+ # Also set the port if this happens
119
+ if @uri.scheme.nil? || @uri.scheme.empty?
120
+ @uri.scheme = SCHEME
121
+ @uri.port = URI::Gemini::DEFAULT_PORT
122
+ end
123
+
124
+ # Set member parts
125
+ @scheme = @uri.scheme
126
+ @port = @uri.port
127
+
128
+ # Check if a scheme is present that isn't gemini://
129
+ return if @uri.scheme == SCHEME
130
+
131
+ raise InvalidSchemeError,
132
+ "Request uses an invalid scheme (has: #{@uri.scheme}, wants: #{SCHEME}"
133
+ end
134
+
135
+ # Parses the path, the query, and the fragment for the request.
136
+ def parse_tail
137
+ # Set path to '/' if one isn't specified
138
+ @uri.path = "/" if @uri.path.nil? || @uri.path.empty?
139
+
140
+ # Set member parts
141
+ @path = @uri.path
142
+ @queries = @uri.query.split("&") unless @uri.query.nil? || @uri.query.empty?
143
+ @fragment = @uri.fragment
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ class String # :nodoc:
4
+ def numerical?
5
+ to_i.to_s == self
6
+ end
7
+ end
8
+
9
+ module Net
10
+ module Tofu
11
+ # Stores a response from a Gemini server.
12
+ class Response
13
+ # Response types
14
+ INPUT = 1
15
+ SUCCESS = 2
16
+ REDIRECT = 3
17
+ TEMPORARY_FAILURE = 4
18
+ PERMANENT_FAILURE = 5
19
+ REQUEST_CERTIFICATE = 6
20
+
21
+ # Limits
22
+ MAX_META_BYTESIZE = 1024
23
+
24
+ # @return [String] The full response header from the server.
25
+ attr_reader :header
26
+
27
+ # @return [Integer] The 2-digit, server response status.
28
+ attr_reader :status
29
+
30
+ # @return [Integer] The first digit of the server response status.
31
+ attr_reader :status_maj
32
+
33
+ # @return [Integer] The second digit of the server response status.
34
+ attr_reader :status_min
35
+
36
+ # Dependent on the #{status} ->
37
+ # => 1x: (INPUT) A prompt line that should be displayed to the user.
38
+ # => 2x: (SUCCESS) A MIME media type.
39
+ # => 3x: (REDIRECT) A new URL for the requested resource.
40
+ # => 4x: (TEMP FAIL) Additional information regarding the temporary failure.
41
+ # => 5x: (PERM FAIL) Additional information regarding the temporary failure.
42
+ # => 6x: (RQST CERT) Additional information regarding the client certificate requirements.
43
+ #
44
+ # According to the specification for the Gemini Protocol, clients SHOULD close a connection to servers which send
45
+ # a meta over 1024 bytes. This library complies with this specification, although, it is conceivable that meta
46
+ # could be an arbitrarily long string.
47
+ #
48
+ # @return [String] The UTF-8 encoded message from the response header.
49
+ attr_reader :meta
50
+
51
+ # @return [String] The message body.
52
+ attr_reader :body
53
+
54
+ # Constructor for the response type.
55
+ # @param data [String] A raw Gemini server response.
56
+ def initialize(data)
57
+ @data = data
58
+ parse
59
+ end
60
+
61
+ # Get a human readable response type.
62
+ # @return [String] The response type as a human readable string.
63
+ def type
64
+ case @status_maj
65
+ when INPUT then return "Input"
66
+ when SUCCESS then return "Success"
67
+ when REDIRECT then return "Redirect"
68
+ when TEMPORARY_FAILURE then return "Temporary failure"
69
+ when PERMANENT_FAILURE then return "Permanent failure"
70
+ when REQUEST_CERTIFICATE then return "Request certificate"
71
+ end
72
+ "Unknown"
73
+ end
74
+
75
+ def input?; return true if @status_maj == INPUT; false; end
76
+ def success?; return true if @status_maj == SUCCESS; false; end
77
+ def redirect?; return true if @status_maj == REDIRECT; false; end
78
+ def temporary_failure?; return true if @status_maj == TEMPORARY_FAILURE; false; end
79
+ def permanent_failure?; return true if @status_maj == PERMANENT_FAILURE; false; end
80
+ def request_certificate?; return true if @status_maj == REQUEST_CERTIFICATE; false; end
81
+
82
+ private
83
+
84
+ # Splits up the responseinto a header and a body.
85
+ def parse
86
+ # Extract the header and parse it
87
+ a = @data.split("\n")
88
+ @header = a[0].strip
89
+ parse_header
90
+
91
+ # Remove the first element from the array,
92
+ # then populate body with the rest of the data
93
+ a.shift
94
+ @body = a.join("\n") if a.length.positive?
95
+ end
96
+
97
+ # Splits upt the header into a status code and a meta.
98
+ def parse_header
99
+ a = @header.split
100
+
101
+ # Make sure there are only one or two elements in the header
102
+ raise InvalidHeaderError, "The server did not send a header" unless a.length.positive?
103
+
104
+ # Parse the status code
105
+ @status = a[0]
106
+ parse_status
107
+
108
+ # Parse the meta
109
+ @meta = ""
110
+ @meta = a[1..].join(" ") if a.length >= 2
111
+ parse_meta
112
+ end
113
+
114
+ # Splits up the status into a major and minor, checks for invalid status codes.
115
+ def parse_status
116
+ # Make sure the status is numerical
117
+ unless @status.numerical?
118
+ raise InvalidStatusCodeError,
119
+ "The server sent a non-numerical status code: #{@status}"
120
+ end
121
+
122
+ # Allow status code to only be a single digit, or two digits (as the spec says it should be)
123
+ if @status.length == 1
124
+ @status_maj = Integer(@status)
125
+ @status_min = 0
126
+ elsif @status.length == 2
127
+ @status_maj = Integer(@status[0])
128
+ @status_min = Integer(@status[1])
129
+ else
130
+ raise InvalidStatusCodeError, "The server sent a status code that is longer than 2 digits: #{@status}"
131
+ end
132
+
133
+ # Make sure the major status code is between 1 and 6, including 1 and 6
134
+ unless @status_maj >= 1 && @status_maj <= 6
135
+ raise InvalidStatusCodeError,
136
+ "The server sent an invalid, major status code: #{@status_maj}"
137
+ end
138
+
139
+ # Make sure #{status} is an Integer
140
+ @status = Integer(@status)
141
+ end
142
+
143
+ # Checks the meta size, does extra checking depending on #{status} type.
144
+ def parse_meta
145
+ # Make sure the meta isn't too large
146
+ if @meta.bytesize > MAX_META_BYTESIZE
147
+ raise InvalidMetaError,
148
+ "The server sent a meta that was too large, should be #{MAX_META_BYTESIZE} bytes, instead is #{@meta.bytesize} bytes"
149
+ end
150
+
151
+ if @status_maj == TEMPORARY_FAILURE || @status_maj == PERMANENT_FAILURE || @status_maj == REQUEST_CERTIFICATE
152
+ return
153
+ end
154
+
155
+ # Make sure meta exists (i.e. has length)
156
+ # This satisfies the INPUT and SUCCESS
157
+ unless @meta.length.positive?
158
+ raise InvalidMetaError,
159
+ "The server sent an empty meta, should've sent a user prompt"
160
+ end
161
+
162
+ # TODO: Possibly check for valid MIME type for the SUCCESS response
163
+ return if @status_maj == INPUT || @status_maj == SUCCESS
164
+
165
+ # The meta needs a specific URI if @status_maj == REDIRECT
166
+ uri = URI(@meta)
167
+
168
+ # Make sure the URI has a scheme
169
+ raise InvalidRedirectError, "The redirect link does not have a scheme" if uri.scheme.nil? || uri.scheme.empty?
170
+
171
+ # Make sure the URI scheme is 'gemini'
172
+ return if uri.scheme == "gemini"
173
+
174
+ raise InvalidRedirectError,
175
+ "The redirect link has an invalid scheme (has: #{uri.scheme}, wants: gemini)"
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ module Tofu
5
+ # Stoes an SSLSocket for making requests and receiving data.
6
+ class Socket
7
+ # Constructor for the socket type.
8
+ # @param host [String] Hostname of the server to connect to.
9
+ # @param port [Integer] Server port to connect to (typically 1965).
10
+ def initialize(host, port)
11
+ @host = host
12
+ @port = port
13
+ @sock = OpenSSL::SSL::SSLSocket.open(@host, @port, context: generate_secure_context)
14
+ end
15
+
16
+ # Open a connection to the server.
17
+ def connect
18
+ @sock.hostname = @host
19
+ @sock.connect
20
+ end
21
+
22
+ # Try and retrieve data from a request.
23
+ def gets(req)
24
+ @sock.puts req.format
25
+
26
+ io = StringIO.new
27
+ while (line = @sock.gets)
28
+ io.puts line
29
+ end
30
+
31
+ Response.new(io.string)
32
+ ensure
33
+ io.close
34
+ end
35
+
36
+ # Close the connection with the server.
37
+ def close
38
+ @sock.close
39
+ end
40
+
41
+ private
42
+
43
+ # Configure the TLS security options to use on the socket.
44
+ def generate_secure_context
45
+ ctx = OpenSSL::SSL::SSLContext.new
46
+ ctx.verify_hostname = true
47
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
48
+ ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
49
+ ctx.options |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
50
+ ctx
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tofu
4
+ VERSION = "0.1.0"
5
+ end
data/lib/net/tofu.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "tofu/error"
4
+ require_relative "tofu/request"
5
+ require_relative "tofu/response"
6
+ require_relative "tofu/socket"
7
+ require_relative "tofu/version"
8
+
9
+ require "openssl"
10
+ require "stringio"
11
+ require "uri/gemini"
12
+
13
+ module Net
14
+ # Top level module for Geminispace requests.
15
+ module Tofu
16
+ def self.get(uri, trust: true)
17
+ req = Request.new(uri)
18
+ req.gets
19
+
20
+ return req.resp.body if req.resp.success?
21
+
22
+ req.resp.meta
23
+ end
24
+
25
+ def self.get_response(uri, trust: true)
26
+ req = Request.new(uri)
27
+ req.gets
28
+
29
+ req.resp
30
+ end
31
+ end
32
+ end
data/lib/uri/gemini.rb ADDED
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # MIT License
4
+ #
5
+ # Copyright (c) 2020 Étienne Deparis
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+
25
+ require "uri"
26
+
27
+ module URI # :nodoc:
28
+ #
29
+ # The syntax of Gemini URIs is defined in the Gemini specification,
30
+ # section 1.2.
31
+ #
32
+ # @see https://gemini.circumlunar.space/docs/specification.html
33
+ #
34
+ class Gemini < HTTP
35
+ # A Default port of 1965 for URI::Gemini.
36
+ DEFAULT_PORT = 1965
37
+
38
+ # An Array of the available components for URI::Gemini.
39
+ COMPONENT = %i[scheme host port
40
+ path query fragment].freeze
41
+ end
42
+
43
+ if respond_to? :register_scheme
44
+ # Introduced somewhere in ruby 3.0.x
45
+ register_scheme "GEMINI", Gemini
46
+ else
47
+ @@schemes["GEMINI"] = Gemini
48
+ end
49
+ end
data/sig/opentofu.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Opentofu
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opentofu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rory Dudley
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-07-27 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |2
14
+ OpenTOFU is a client and certificate pinning library for Geminispace, derived from
15
+ the 'trust on first use' authentication scheme (https://en.wikipedia.org/wiki/Trust_on_first_use).
16
+ email:
17
+ - rory.dudley@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - ".rubocop.yml"
23
+ - CHANGELOG.md
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - lib/net/tofu.rb
28
+ - lib/net/tofu/error.rb
29
+ - lib/net/tofu/request.rb
30
+ - lib/net/tofu/response.rb
31
+ - lib/net/tofu/socket.rb
32
+ - lib/net/tofu/version.rb
33
+ - lib/uri/gemini.rb
34
+ - sig/opentofu.rbs
35
+ homepage: https://github.com/pinecat/opentofu
36
+ licenses:
37
+ - MIT
38
+ metadata:
39
+ allowed_push_host: https://rubygems.org
40
+ homepage_uri: https://github.com/pinecat/opentofu
41
+ source_code_uri: https://github.com/pinecat/opentofu
42
+ changelog_uri: https://github.com/pinecat/opentofu
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 2.6.0
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.4.17
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: A Gemini client and certificate manager.
62
+ test_files: []