Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial new mail handler

  • Loading branch information...
commit da2e00a48938b548f2426488e0d1bc2b5c951d2b 0 parents
@singpolyma authored
Showing with 130 additions and 0 deletions.
  1. +71 −0 lib/simple_protocol.rb
  2. +59 −0 newmail.rb
71 lib/simple_protocol.rb
@@ -0,0 +1,71 @@
+require 'socket'
+require 'uri'
+
+class SimpleProtocol
+ def initialize(args={})
+ server_set args
+
+ @socket = TCPSocket.new(@host, @port)
+ gets # Eat banner
+
+ if block_given? # block start/finish wrapper
+ yield self
+ close
+ end
+ end
+
+ def close
+ @socket.close if @socket
+ end
+
+ def send_command(cmd, *args)
+ send(format_command(cmd, args))
+ end
+
+ def send_multiline(*args)
+ args.flatten!
+ args.map! {|line| line.gsub(/\r\n/, "\n").gsub(/\r|\n/, "\r\n") } # Normalize newlines
+ send(args.join("\r\n") + "\r\n.\r\n") # Append terminator
+ end
+
+ def send(data)
+ @socket.print(data)
+ end
+
+ def gets
+ # Read one line and remove the terminator. Don't choke on any bytes
+ # Downstream you probably want to force the encoding over to a real encoding
+ @socket.gets.chomp.force_encoding('BINARY')
+ end
+
+ def gets_multiline
+ buf = ''
+ while (line = gets) != '.'
+ buf << line << "\n"
+ end
+ buf.chomp.split("\n")
+ end
+
+ def method_missing(method, *args)
+ send_command(method.to_s, args)
+ end
+
+ protected
+ def format_command(cmd, args)
+ args.flatten!
+ args = args.join(' ')
+ raise 'No CR or LF allowed in command string or arguments.' if cmd.index("\r") or cmd.index("\n") or args.index("\r") or args.index("\n")
+ "#{cmd} #{args}\r\n"
+ end
+
+ def server_set(args)
+ if args[:uri] # Allow passing a string or URI object URI for host and port
+ args[:uri] = URI::parse(args[:uri]) unless args[:uri].is_a?URI
+ args[:host] = args[:uri].host
+ args[:port] = args[:uri].port
+ end
+ args[:port] = args[:default_port] unless args[:port] # Default port is 119
+ @host = args[:host]
+ @port = args[:port]
+ end
+end
59 newmail.rb
@@ -0,0 +1,59 @@
+#!/usr/bin/ruby
+
+# This script is to be run as or from MDA, it takes a single email on
+# STDIN and posts it to the correct newsgroup
+# The first command line argument is the NNTP URI of the server
+# If ORIGINAL_RECIPIENT is set in the environment, that takes precedence
+# over the To header. If there's a second command line, it argument takes
+# precendence over the environment and the To header.
+
+$: << File.dirname(__FILE__) + '/lib'
+require 'simple_protocol'
+
+unless ARGV[0]
+ warn 'You must specify an NNTP server!'
+ exit 1
+end
+
+to = ARGV[1] || ENV['ORIGINAL_RECIPIENT']
+mail = STDIN.read
+
+unless to
+ # Extract To header, first email address
+ to = mail.scan(/^To:.*?<?((?:[^<> ,"']+@)?[^<> ,"']+\.[^<> ,"']*)[> ,]/i)
+ to = to[0][0] rescue nil
+end
+
+unless to
+ warn 'No recipient address found!'
+ exit 1
+end
+
+newsgroup, _ = to.split(/@/)
+
+def add_to_header(mail, header, *args)
+ has = false
+ mail.lines.map {|line|
+ if line =~ /^#{header}:/i
+ has = true
+ items = line.chomp.split(/:\s*/, 2).last.split(/\s*,\s*/) + args
+ items.length > 0 ? "#{header}: #{items.uniq.join(', ')}\n" : nil
+ elsif !has && line.chomp == ''
+ has = true
+ "#{header}: #{args.join(', ')}\n\n"
+ else
+ line
+ end
+ }.compact.join
+end
+
+mail = add_to_header(mail, 'Newsgroups', newsgroup)
+
+SimpleProtocol.new(:uri => ARGV[0], :default_port => 119) { |nntp|
+ nntp.post
+ raise 'Error sending POST command to server.' unless nntp.gets.split(' ')[0] == '340'
+ nntp.send_multiline(mail.split(/\r?\n/))
+ unless (m = nntp.gets).split(' ')[0] == '240'
+ raise 'Error POSTing article: ' + m
+ end
+}
Please sign in to comment.
Something went wrong with that request. Please try again.