ruby-net-text 0.0.3 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc4467da1a2124222650203e4d1f3bcbf377b2c8178d6d7a9690c1fb0003ac8a
4
- data.tar.gz: a04be63fe05981caf42cd4c64f615c06dfd9ee6352b464fa80fadb7dd87b3ecb
3
+ metadata.gz: de26ac5c1f8127c9d08a5430adc0cff34d266847327c6864baa903b5352ac041
4
+ data.tar.gz: d2725dab71f60b4d12be9ff0d948ae1a3f1bc6de9858499258aeedc11c801ae0
5
5
  SHA512:
6
- metadata.gz: ef9cab28b37134208fc2e24563e8f5138ca118d1df47c76e2ac8272dc688ee3f19f5ad6f343a1e08fc3f1359742f999d9216a802ce08d4b4acd213c906bcd5dc
7
- data.tar.gz: d142b3651f551721b022746e32c8aee4355075dd0bbd8cdb5e897444d42d695b8576561e6068547e85b435d738f14b449c28c0991c7719d0c0890c4d18ace61f
6
+ metadata.gz: 79defc399e2b1e1631e153c5994ef1547d0eb9732f0153a641ba9dbf98d35ea03c176723b7bb7052ac08a61b2069eac37e158d04e213dd4c778ecb0c39af00b4
7
+ data.tar.gz: 050fecb1c2fda499404207e5cb3d49048ff5fcb9695497f35b257dc40c02e3e71ba648ffe575e044a57e282ac43f62590d147c65404005427ae8bfeeb66848d9
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Gemini, Gopher, and Finger support for Net::* and URI::*
2
+
3
+ [![Support using Liberapay](https://img.shields.io/badge/Liberapay-Support_me-yellow?logo=liberapay)](https://liberapay.com/milouse/donate)
4
+ [![Support using Flattr](https://img.shields.io/badge/Flattr-Support_me-brightgreen?logo=flattr)](https://flattr.com/@milouse)
5
+ [![Support using Paypal](https://img.shields.io/badge/Paypal-Support_me-00457C?logo=paypal&labelColor=lightgray)](https://paypal.me/milouse)
6
+
7
+ [![Gem](https://img.shields.io/gem/v/ruby-net-text)](https://rubygems.org/gems/ruby-net-text)
8
+ [![Documentation](https://img.shields.io/badge/Documentation-ruby--net--text-CC342D?logo=rubygems)](https://www.rubydoc.info/gems/ruby-net-text/Net/Gemini)
9
+
10
+ This project aims to add connectors to well known internet text protocols
11
+ through the standard `Net::*` and `URI::*` ruby module namespaces.
12
+
13
+ ## Documentation
14
+
15
+ The code is self-documented and you can browse it on rubydoc.info:
16
+
17
+ ### Gemini
18
+
19
+ - [URI::Gemini](https://www.rubydoc.info/gems/ruby-net-text/URI/Gemini)
20
+ - [Net::Gemini](https://www.rubydoc.info/gems/ruby-net-text/Net/Gemini)
21
+
22
+ ### Gopher
23
+
24
+ - [URI::Gopher](https://www.rubydoc.info/gems/ruby-net-text/URI/Gopher)
25
+ - [Net::Gopher](https://www.rubydoc.info/gems/ruby-net-text/Net/Gopher)
26
+
27
+ ### Finger
28
+
29
+ - [URI::Finger](https://www.rubydoc.info/gems/ruby-net-text/URI/Finger)
30
+ - [Net::Finger](https://www.rubydoc.info/gems/ruby-net-text/Net/Finger)
31
+
32
+ ## Helpers
33
+
34
+ This repository also includes 2 little helpers:
35
+
36
+ - `bin/heraut`: a toy client for Gemini, Gopher and Finger. Give it a URI and
37
+ it will output the remote file.
38
+ - `bin/test_thread.rb`: a toy performance test script to run against a Gemini
39
+ server
data/lib/net/finger.rb ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../uri/finger'
4
+ require_relative 'generic'
5
+
6
+ module Net
7
+ # == A Finger client API for Ruby.
8
+ #
9
+ # Net::Finger provides a library which can be used to build Finger
10
+ # user-agents.
11
+ #
12
+ # Net::Finger is designed to work closely with URI.
13
+ #
14
+ # == Simple Examples
15
+ #
16
+ # All examples assume you have loaded Net::Finger with:
17
+ #
18
+ # require 'net/finger'
19
+ #
20
+ # This will also require 'uri' so you don't need to require it
21
+ # separately.
22
+ #
23
+ # The Net::Finger methods in the following section do not persist
24
+ # connections.
25
+ #
26
+ # === GET by URI
27
+ #
28
+ # uri = URI('finger://skyjake.fi/jaakko')
29
+ # Net::Finger.get(uri) # => String
30
+ #
31
+ class Finger
32
+ extend TextGeneric
33
+
34
+ def self.get(string_or_uri)
35
+ uri = build_uri string_or_uri, URI::Finger
36
+ request uri, uri.name.to_s
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gemini
4
+ # Contains specific method to parse text/gemini documents.
5
+ module GmiParser
6
+ private
7
+
8
+ def parse_meta
9
+ header = { status: @status, meta: @meta, mimetype: nil }
10
+ return header unless body_permitted?
11
+ mime = { lang: nil, charset: 'utf-8', format: nil }
12
+ raw_meta = meta.split(';').map(&:strip)
13
+ header[:mimetype] = raw_meta.shift
14
+ return header unless raw_meta.any?
15
+ raw_meta.map { |m| m.split('=') }.each do |opt|
16
+ key = opt[0].downcase.to_sym
17
+ next unless mime.has_key? key
18
+ mime[key] = opt[1].downcase
19
+ end
20
+ header.merge(mime)
21
+ end
22
+
23
+ def parse_preformatted_block(line, buf)
24
+ cur_block = { meta: line[3..].chomp, content: '' }
25
+ while (line = buf.gets)
26
+ if line.start_with?('```')
27
+ @preformatted_blocks << cur_block
28
+ break
29
+ end
30
+ cur_block[:content] += line
31
+ end
32
+ end
33
+
34
+ def parse_link(line)
35
+ m = line.strip.match(/\A=>\s*([^\s]+)(?:\s*(.+))?\z/)
36
+ return if m.nil?
37
+ begin
38
+ uri = URI(m[1])
39
+ rescue URI::InvalidURIError
40
+ return
41
+ end
42
+ uri = @uri.merge(uri) if @uri && uri.is_a?(URI::Generic)
43
+ @links << { uri: uri, label: m[2]&.chomp }
44
+ end
45
+
46
+ def parse_body
47
+ buf = StringIO.new(@body)
48
+ while (line = buf.gets)
49
+ if line.start_with?('```')
50
+ parse_preformatted_block(line, buf)
51
+ elsif line.start_with?('=>')
52
+ parse_link(line)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gemini
4
+ # Contains specific method to correctly display Gemini texts
5
+ module ReflowText
6
+ private
7
+
8
+ def reflow_line_cut_index(line)
9
+ possible_cut = [
10
+ line.rindex(' ') || 0,
11
+ line.rindex('­') || 0,
12
+ line.rindex('-') || 0
13
+ ].sort
14
+ possible_cut.reverse!
15
+ possible_cut[0]
16
+ end
17
+
18
+ def reflow_line_prefix(line)
19
+ m = line.match(/\A([*#>]+ )/)
20
+ return '' unless m
21
+ # Each quote line should begin with the quote mark
22
+ return m[1] if m[1].start_with?('>')
23
+ ' ' * m[1].length
24
+ end
25
+
26
+ def reflow_text_line(line, mono_block_open, length)
27
+ line.strip!
28
+ if mono_block_open || line.start_with?('=>') || line.length < length
29
+ return [line]
30
+ end
31
+ output = []
32
+ prefix = reflow_line_prefix(line)
33
+ while line.length > length
34
+ cut_line = line[0...length]
35
+ cut_index = reflow_line_cut_index(cut_line)
36
+ break if cut_index.zero? # Better do nothing for now
37
+ output << line[0...cut_index]
38
+ line = prefix + line[cut_index + 1..]
39
+ end
40
+ output << line
41
+ end
42
+
43
+ def reformat_body(length)
44
+ unless length.is_a? Integer
45
+ raise ArgumentError, "Length must be Integer, #{length} given"
46
+ end
47
+ return @body if length.zero?
48
+ new_body = []
49
+ mono_block_open = false
50
+ buf = StringIO.new(@body)
51
+ while (line = buf.gets)
52
+ if line.start_with?('```')
53
+ mono_block_open = !mono_block_open
54
+ # Don't include code block toggle lines
55
+ next
56
+ end
57
+ new_body += reflow_text_line(line, mono_block_open, length)
58
+ end
59
+ new_body.join("\n")
60
+ end
61
+ end
62
+ end
@@ -42,7 +42,7 @@ module Net
42
42
  # - 2 bytes for <CR><LF>
43
43
  str = sock.gets($INPUT_RECORD_SEPARATOR, 1026)
44
44
  m = /\A(.*)\r\n\z/.match(str)
45
- raise GeminiBadRequest, "Malformed request: #{str.dump}" if m.nil?
45
+ raise GeminiBadRequest, "Malformed request: #{str&.dump}" if m.nil?
46
46
  new(m[1])
47
47
  end
48
48
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gemini
4
+ # Contains specific method to handle SSL connection
5
+ module SSL
6
+ private
7
+
8
+ def ssl_check_existing(new_cert, cert_file)
9
+ raw = File.read cert_file
10
+ saved_one = OpenSSL::X509::Certificate.new raw
11
+ return true if saved_one == new_cert
12
+ # TODO: offer some kind of recuperation
13
+ warn "#{cert_file} does not match the current host cert!"
14
+ false
15
+ end
16
+
17
+ def ssl_verify_cb(cert)
18
+ return false unless OpenSSL::SSL.verify_certificate_identity(cert, @host)
19
+ cert_file = File.expand_path("#{@certs_path}/#{@host}.pem")
20
+ return ssl_check_existing(cert, cert_file) if File.exist?(cert_file)
21
+ FileUtils.mkdir_p(File.expand_path(@certs_path))
22
+ File.open(cert_file, 'wb') { |f| f.print cert.to_pem }
23
+ true
24
+ end
25
+
26
+ def ssl_context
27
+ ssl_context = OpenSSL::SSL::SSLContext.new
28
+ ssl_context.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER)
29
+ ssl_context.min_version = OpenSSL::SSL::TLS1_2_VERSION
30
+ ssl_context.verify_hostname = true
31
+ ssl_context.ca_file = '/etc/ssl/certs/ca-certificates.crt'
32
+ ssl_context.verify_callback = lambda do |preverify_ok, store_context|
33
+ return true if preverify_ok
34
+ ssl_verify_cb store_context.current_cert
35
+ end
36
+ ssl_context
37
+ end
38
+
39
+ def init_sockets
40
+ socket = TCPSocket.new(@host, @port)
41
+ @ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
42
+ # Close underlying TCP socket with SSL socket
43
+ @ssl_socket.sync_close = true
44
+ @ssl_socket.hostname = @host # SNI
45
+ @ssl_socket.connect
46
+ end
47
+
48
+ # Closes the SSL and TCP connections.
49
+ def finish
50
+ @ssl_socket.close
51
+ end
52
+ end
53
+ end
data/lib/net/gemini.rb CHANGED
@@ -16,8 +16,8 @@ module Net
16
16
 
17
17
  # == A Gemini client API for Ruby.
18
18
  #
19
- # Net::Gemini provides a rich library which can be used to build
20
- # Gemini user-agents.
19
+ # Net::Gemini provides a library which can be used to build Gemini
20
+ # user-agents.
21
21
  # @see https://gemini.circumlunar.space/docs/specification.html
22
22
  #
23
23
  # Net::Gemini is designed to work closely with URI.
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+
5
+ # Generic interface to be used by specific network protocol implementation.
6
+ module TextGeneric
7
+ private
8
+
9
+ def build_uri(string_or_uri, uri_class)
10
+ string_or_uri = URI(string_or_uri) if string_or_uri.is_a?(String)
11
+ return string_or_uri if string_or_uri.is_a?(uri_class)
12
+ raise ArgumentError, "uri is not a String, nor an #{uri_class.name}"
13
+ end
14
+
15
+ def request(uri, query)
16
+ sock = TCPSocket.new(uri.host, uri.port)
17
+ sock.puts query
18
+ sock.read
19
+ ensure
20
+ # Stop remaining connection, even if they should be already cut
21
+ # by the server
22
+ sock&.close
23
+ end
24
+ end
data/lib/net/gopher.rb ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../uri/gopher'
4
+ require_relative 'generic'
5
+
6
+ module Net
7
+ # == A Gopher client API for Ruby.
8
+ #
9
+ # Net::Gopher provides a library which can be used to build Gopher
10
+ # user-agents.
11
+ #
12
+ # Net::Gopher is designed to work closely with URI.
13
+ #
14
+ # == Simple Examples
15
+ #
16
+ # All examples assume you have loaded Net::Gopher with:
17
+ #
18
+ # require 'net/gopher'
19
+ #
20
+ # This will also require 'uri' so you don't need to require it
21
+ # separately.
22
+ #
23
+ # The Net::Gopher methods in the following section do not persist
24
+ # connections.
25
+ #
26
+ # === GET by URI
27
+ #
28
+ # uri = URI('gopher://thelambdalab.xyz/1/projects/elpher/')
29
+ # Net::Gopher.get(uri) # => String
30
+ #
31
+ class Gopher
32
+ extend TextGeneric
33
+
34
+ def self.get(string_or_uri)
35
+ uri = build_uri string_or_uri, URI::Gopher
36
+ request uri, "#{uri.selector}\r\n"
37
+ end
38
+ end
39
+ end
data/lib/uri/finger.rb CHANGED
@@ -14,8 +14,22 @@ module URI # :nodoc:
14
14
  DEFAULT_PORT = 79
15
15
 
16
16
  # An Array of the available components for URI::Finger.
17
- COMPONENT = [:scheme, :userinfo, :host, :port].freeze
17
+ COMPONENT = [:scheme, :userinfo, :host, :port, :path].freeze
18
+
19
+ def name
20
+ # Utilitary method to extract a name to query, either from the userinfo
21
+ # part or from the path.
22
+ return @user if @user
23
+ return '' unless @path
24
+ # Remove leading /
25
+ @path[1..] || ''
26
+ end
18
27
  end
19
28
 
20
- @@schemes['FINGER'] = Finger
29
+ if respond_to? :register_scheme
30
+ # Introduced somewhere in ruby 3.0.x
31
+ register_scheme 'FINGER', Finger
32
+ else
33
+ @@schemes['FINGER'] = Finger
34
+ end
21
35
  end
data/lib/uri/gemini.rb CHANGED
@@ -18,5 +18,10 @@ module URI # :nodoc:
18
18
  :path, :query, :fragment].freeze
19
19
  end
20
20
 
21
- @@schemes['GEMINI'] = Gemini
21
+ if respond_to? :register_scheme
22
+ # Introduced somewhere in ruby 3.0.x
23
+ register_scheme 'GEMINI', Gemini
24
+ else
25
+ @@schemes['GEMINI'] = Gemini
26
+ end
22
27
  end
data/lib/uri/gopher.rb CHANGED
@@ -15,7 +15,29 @@ module URI # :nodoc:
15
15
 
16
16
  # An Array of the available components for URI::Gopher.
17
17
  COMPONENT = [:scheme, :host, :port, :path].freeze
18
+
19
+ def selector
20
+ return @selector if defined? @selector
21
+ @selector = extract_gopher_path_elements[1]
22
+ end
23
+
24
+ def item_type
25
+ return @item_type if defined? @item_type
26
+ @item_type = extract_gopher_path_elements[0]
27
+ end
28
+
29
+ private
30
+
31
+ def extract_gopher_path_elements
32
+ m = /\A\/([0-9+dfghisA-LT])(\/.*)?\Z/.match(@path)
33
+ m.captures
34
+ end
18
35
  end
19
36
 
20
- @@schemes['GOPHER'] = Gopher
37
+ if respond_to? :register_scheme
38
+ # Introduced somewhere in ruby 3.0.x
39
+ register_scheme 'GOPHER', Gopher
40
+ else
41
+ @@schemes['GOPHER'] = Gopher
42
+ end
21
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-net-text
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Étienne Deparis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-12 00:00:00.000000000 Z
11
+ date: 2022-02-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: etienne@depar.is
@@ -17,16 +17,27 @@ extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
19
  - LICENSE
20
+ - README.md
21
+ - lib/net/finger.rb
20
22
  - lib/net/gemini.rb
23
+ - lib/net/gemini/gmi_parser.rb
24
+ - lib/net/gemini/reflow_text.rb
21
25
  - lib/net/gemini/request.rb
22
26
  - lib/net/gemini/response.rb
27
+ - lib/net/gemini/ssl.rb
28
+ - lib/net/generic.rb
29
+ - lib/net/gopher.rb
23
30
  - lib/uri/finger.rb
24
31
  - lib/uri/gemini.rb
25
32
  - lib/uri/gopher.rb
26
33
  homepage: https://git.umaneti.net/ruby-net-text/
27
34
  licenses:
28
35
  - MIT
29
- metadata: {}
36
+ metadata:
37
+ rubygems_mfa_required: 'true'
38
+ source_code_uri: https://git.umaneti.net/ruby-net-text/
39
+ documentation_uri: https://www.rubydoc.info/gems/ruby-net-text
40
+ funding_uri: https://liberapay.com/milouse
30
41
  post_install_message:
31
42
  rdoc_options: []
32
43
  require_paths:
@@ -42,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
42
53
  - !ruby/object:Gem::Version
43
54
  version: '0'
44
55
  requirements: []
45
- rubygems_version: 3.1.4
56
+ rubygems_version: 3.3.7
46
57
  signing_key:
47
58
  specification_version: 4
48
59
  summary: Gemini, Gopher, and Finger support for Net::* and URI::*