Try running ruby-net-nntp and tmail in 2020


I decided to communicate with NNTP because of the necessity. I could have used my own NNTP class that I created a long time ago, but I decided to use ruby-net-nntp, which can use Gemfile. This library stores articles in the TMail :: Mail class, so bundle install will also download tmail-

I summarized the use of net / nttp and the problems that occurred with tmail.

Environment & preparatory work

Library download

I prepared the following files.

source ''
gem "ruby-net-nntp"

Download the library using bundler

$ bundle install --path lib
$ ls lib/ruby/2.5.0/gems/
log4r-1.1.10/  ruby-net-nntp-1.0.0/  tmail-

Creating a sample program

When I tried to run the code, I immediately ran into an error.

Error encountered

require "net/nntp"When I run, I get the following error:

Reproduce the error from irb

$ irb
irb(main):001:0> require "rubygems"
=> false
irb(main):002:0> require "bundler/setup"
=> true
irb(main):003:0> require 'net/nntp'
Traceback (most recent call last):
       14: from /usr/bin/irb:11:in `<main>'
        2: from .../lib/ruby/2.5.0/gems/tmail- `<module:TMail>'
        1: from .../lib/ruby/2.5.0/gems/tmail- `<module:TextUtils>'
RegexpError (/.../n has a non escaped non ASCII character in non ASCII-8BIT script)

This is [ --How to deal with the problem that invalid multibyte escape error occurs in regular expressions](http://www. It is explained in

The link to Google in this article doesn't work, so [](https: / You can see the original discussion at /


Replace the /../n notation where the problem simply occurs with ().

tmail library diff

diff -ru tmail- tmail-
--- tmail- 2020-06-10 22:06:16.813972850 +0900
+++ tmail-      2020-06-11 11:17:51.741512220 +0900
@@ -63,24 +63,24 @@
-        /\A(?:[#{atomchars}]+|#{iso2022str}|#{eucstr})+/n,
-        /\A(?:[#{tokenchars}]+|#{iso2022str}|#{eucstr})+/n,
+"\A(?:[#{atomchars}]+|#{iso2022str}|#{eucstr})+", nil, "n"),
+"\A(?:[#{tokenchars}]+|#{iso2022str}|#{eucstr})+", nil, "n"),
-        /\A(?:[#{atomchars}]+|#{iso2022str}|#{sjisstr})+/n,
-        /\A(?:[#{tokenchars}]+|#{iso2022str}|#{sjisstr})+/n,
+"\A(?:[#{atomchars}]+|#{iso2022str}|#{sjisstr})+", nil, "n"),
+"\A(?:[#{tokenchars}]+|#{iso2022str}|#{sjisstr})+", nil, "n"),
-        /\A(?:[#{atomchars}]+|#{utf8str})+/n,
-        /\A(?:[#{tokenchars}]+|#{utf8str})+/n,
+"\A(?:[#{atomchars}]+|#{utf8str})+", nil, "n"),
+"\A(?:[#{tokenchars}]+|#{utf8str})+", nil, "n"),
@@ -258,4 +258,4 @@

 end   # module TMail
\ No newline at end of file
diff -ru tmail- tmail-
--- tmail-     2020-06-10 22:06:16.813972850 +0900
+++ tmail-  2020-06-11 11:11:59.582616384 +0900
@@ -114,10 +114,11 @@
     lwsp         = %Q| \t\r\n|
     control      = %Q|\x00-\x1f\x7f-\xff|
-    CONTROL_CHAR  = /[#{control}]/n
-    ATOM_UNSAFE   = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n
-    PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
-    TOKEN_UNSAFE  = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n
+    ## reference:
+    CONTROL_CHAR  ="[#{control}]", nil, "n")
+    ATOM_UNSAFE   ="[#{Regexp.quote aspecial}#{control}#{lwsp}]", nil, "n")
+    PHRASE_UNSAFE ="[#{Regexp.quote aspecial}#{control}]", nil, "n")
+    TOKEN_UNSAFE  ="[#{Regexp.quote tspecial}#{control}#{lwsp}]", nil, "n")
     # Returns true if the string supplied is free from characters not allowed as an ATOM
     def atom_safe?( str )
diff -ru tmail- tmail-
--- tmail-        2020-06-10 22:06:16.829961402 +0900
+++ tmail-     2020-06-11 11:18:58.457912881 +0900
@@ -36,7 +36,7 @@
   class UniversalDetector
     attr_accessor :result
     def initialize
-      @_highBitDetector = /[\x80-\xFF]/
+      @_highBitDetector ="[\x80-\xFF]")
       @_escDetector = /(\033|\~\{)/
       @_mEscCharSetProber = nil
       @_mCharSetProbers = []

Try to actually access the NNTP server

I don't know if there is an open access NetNews server, but the code to access my INN 2.5.5 looks like this:



require "rubygems"
require "bundler/setup"
require 'net/nntp'

nntp =
Net::NNTP.logger ="nntp") = ""
nntp.port = 119 
welcome = nntp.connect
if Net::NNTP::OKResponse === welcome
  group_name = "misc.test"
  group_response = nntp.process(
  listgroup_response = nntp.process(
  listgroup_response.list.each { |article_id|

Correspondence to garbled characters of Subject

This has nothing to do with Docker, so I'll add it to the section above. For the header of the stored message, some fields have a unique class, but those that do not perform specific processing such as Subject: have their values stored in the ** UnstructuredHeader ** class.

In this, Decoder.decode is called, but this implementation makes NKF.nkf called with the "-mSj" option (MIME decode + Shift-JIS input encoding + ISO-2022-JP output encoding). ..

Since there is a problem if the implementation of Decoder.decode is corrected, the omitted argument is used and the output encoding is set to UTF8.

Also, From: uses the ** AddressHeader ** class that inherits from ** StructuredHeader **, so another action is required. Since there is a check routine that assumes only "jes" (ISO-2022-JP, EUC-JP, Shift-JIS) as the encoding before calling nkf, you can also define the Decoder class a little on this point. Is put in.

Additional diffs to tmail

diff -ur lib/ruby/2.5.0/gems/tmail- lib/ruby/2.5.0/gems/tmail-
--- lib/ruby/2.5.0/gems/tmail-  2020-06-16 10:59:57.589086000 +0900
+++ lib/ruby/2.5.0/gems/tmail-       2020-06-16 11:01:30.527304391 +0900
@@ -189,5 +189,5 @@

     def parse
-      @body = Decoder.decode(@body.gsub(/\n|\r\n|\r/, ''))
+      @body = Decoder.decode(@body.gsub(/\n|\r\n|\r/, ''), 'w')

     def isempty?
diff -ur lib/ruby/2.7.0/gems/tmail- lib/ruby/2.7.0/gems/tmail-
--- lib/ruby/2.7.0/gems/tmail-  2020-06-18 14:32:37.352341212 +0900
+++ lib/ruby/2.7.0/gems/tmail-       2020-06-18 14:45:57.637057274 +0900
@@ -128,7 +128,7 @@

     def initialize( dest, encoding = nil, eol = "\n" )
       @f = StrategyInterface.create_dest(dest)
-      @encoding = (/\A[ejs]/ === encoding) ? encoding[0,1] : nil
+      @encoding = (/\A[ejsw]/ === encoding) ? encoding[0,1] : nil
       @eol = eol

diff -ur lib/ruby/2.7.0/gems/tmail- lib/ruby/2.7.0/gems/tmail-
--- lib/ruby/2.7.0/gems/tmail-  2020-06-18 14:32:37.993666420 +0900
+++ lib/ruby/2.7.0/gems/tmail-       2020-06-18 14:47:16.026796369 +0900
@@ -147,7 +147,7 @@

     def body
-      v = = '')
+      v = = '', "w")
       do_accept v
@@ -225,7 +225,7 @@
       rescue SyntaxError
         if not save and mime_encoded? @body
           save = @body
-          @body = Decoder.decode(save)
+          @body = Decoder.decode(save, "w")
         elsif save
           @body = save

When applying a patch in a ruby-2.7 environment such as Docker's ruby: 2.7-alpine image or Ubuntu 20.04 LTS, please convert the file "2.5.0" to "2.7.0" before using it.

2.7 Example of patch application to environment

$ sed -e 's/2.5.0/2.7.0/g' tmail.diff | patch -p0

Error encountered in Dockerfile

The Docker image uses ruby: 2.7-alpine. There were some errors, some of which weren't apparent in ruby-2.5, some of which came from trying to run as a non-root user.

The following is a Dockerfile that omits multi stage build etc. for the time being. In the current directory, there is a ruby-sinatra template code created by openapi-generator such as Gemfile and


FROM ruby:2.7-alpine as rubydev

RUN apk --no-cache add tzdata bash ca-certificates make gcc libc-dev linux-headers build-base patch 

RUN mkdir /app
COPY . /app

RUN cp /usr/local/include/ruby-2.7.0/ruby/defines.h /usr/local/include/ruby-2.7.0/defines.h

RUN bundle config path lib
RUN bundle install


RUN chmod +x /

RUN addgroup sinatra
RUN adduser -S -G sinatra sinatra
# RUN cp -r /root/.bundle /home/sinatra/.bundle
USER sinatra


Problem that native extension of tmail cannot be compiled with ruby-2.7

An error will occur if defines.h cannot be compiled.

Error encountered

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
current directory: /app/lib/ruby/2.7.0/gems/tmail-
make "DESTDIR="
make: *** No rule to make target '/usr/local/include/ruby-2.7.0/defines.h',
needed by 'tmailscanner.o'.  Stop.

Since defines.h does not exist in the expected location, it was supported by cp.

Workaround added to Dockerfile

RUN cp /usr/local/include/ruby-2.7.0/ruby/defines.h \

In Ubuntu 18.04 + ruby-2.5 (deb package) environment, ruby / defines.h was explicitly specified at the end of Makefile, but as far as I checked Makefile of tmail in Dockerfile, -I flag It seems that only /usr/local/include/ruby-2.7.0 is specified in.

ubuntu+ruby-2.5 environments

tmailscanner.o: tmailscanner.c $(hdrdir)/ruby/ruby.h $(arch_hdrdir)/ruby/config.h $(hdrdir)/ruby/defines.h Makefile

In the 2.7-alpine environment, for some reason defines.h should be ruby / defines.h.

2.7-alpine docker image

tmailscanner.o: tmailscanner.c $(hdrdir)/ruby.h $(arch_hdrdir)/ruby/config.h $(hdrdir)/defines.h Makefile

The problem is mkmf.rb, in the pre-processing to write out the depend file, the process to modify the line that is $ (hdrdir) /defines.h to $ (hdrdir) ruby / defines.h is ruby-2.7 mkmf. It was because it was deleted in rb.


## ruby-2.5 mkmf.rb
   depend.each_line do |line|
      line.gsub!(/\.o\b/, ".#{$OBJEXT}")
      line.gsub!(/\{\$\(VPATH\)\}/, "") unless $nmake
      line.gsub!(/\$\((?:hdr|top)dir\)\/config.h/, $config_h)
      line.gsub!(%r"\$\(hdrdir\)/(?!ruby(?![^:;/\s]))(?=[-\w]+\.h)", '\&ruby/')
      if $nmake && /\A\s*\$\(RM|COPY\)/ =~ line

## ruby-2.7 mkmf.rb
    depend.each_line do |line|
      line.gsub!(/\.o\b/, ".#{$OBJEXT}")
      line.gsub!(/\{\$\(VPATH\)\}/, "") unless $nmake
      line.gsub!(/\$\((?:hdr|top)dir\)\/config.h/, $config_h)
      if $nmake && /\A\s*\$\(RM|COPY\)/

It looks like the change committed at

It seems that I wanted to fix the location of ruby.h, but probably instead of deleting the corresponding line, I added a gsub! Line below this that rewrites "ruby / ruby.h" to "ruby.h" I think I should have done it.

It is registered as in Ruby's Issue Tracking System, but it seems that consideration is not progressing.

When using it with Docker, there is no other way but to take a corresponding workaround by changing the location of defines.h rather than patching mkmf.rb ...

Response to error that rackup cannot be found

In ruby-2.5, I used $ bundle install --path lib, but I got a warning saying deprecated, so I changed it to use `` `$ bundle config path lib``` doing.

Because of this, it depends on the contents of the ~ / .bundle / config file, so I changed the runtime user with USER sinatra, so I encountered an error because the ~ / .bundle / config file does not exist. did.

Details of the error

$ make docker-run
sudo docker run -it --rm -p 8080:8080 --name nntp-reader nntp-reader:latest
bundler: command not found: rackup
Install missing gem executables with `bundle install`
Makefile:43: recipe for target 'docker-run' failed
make: *** [docker-run] Error 127

The following measures can be considered first.

Workaround 1-run.Resolve at runtime with sh


bundle config path lib  ##Added workaround
bundle exec rackup --host --port $SINATRA_PORT

The following method was adopted this time.

Workaround 2-Resolve when creating an image with Dockerfile

RUN cp -r /root/.bundle /home/sinatra/.bundle

that's all

Recommended Posts

Try running ruby-net-nntp and tmail in 2020
Try running Selenuim 3.141.59 in eclipse (java)
Try running AWS X-Ray in Java
Try running MySql and Blazor with docker-compose
Try LetCode in Ruby-TwoSum
Implement Thread in Java and try using anonymous class, lambda
Try implementing Yubaba in Kinx
Try using RocksDB in Java
Try calling JavaScript in Java
Try developing Spresense in Java (1)
Try functional type in Java! ①
Mock and stub in RSpec
Try using gRPC in Ruby
Try mixing C and Swift in one project (OS X, Linux)