Connecting to Yahoo! mail from Ruby - ruby

I try to connect to mail Yahoo! account from Ruby using both net/imap and net/pop. But I randomly get error EOFile (from IMAP) or Connection Refused/Reset by peer (from POP). Has anybody tried to connect to Yahoo! Mail and had some experiences about it?

There's a bug in ruby's net/imap library that is exposed when connecting to Yahoo.
The fix is straightforward and described here:
http://redmine.ruby-lang.org/issues/4509
Basically, edit imap.rb and change the inner loop of search_response method from:
token = lookahead
case token.symbol
when T_CRLF
break
when T_SPACE
shift_token
end
data.push(number)
to:
token = lookahead
case token.symbol
when T_CRLF
break
when T_SPACE
shift_token
else
data.push(number)
end
then test with the following code:
require 'net/imap'
Net::IMAP.debug = true
conn = Net::IMAP.new('imap.mail.yahoo.com', 143, false)
conn.instance_eval { send_command('ID ("GUID" "1")') }
conn.authenticate('LOGIN', ARGV[0], ARGV[1] )
conn.select("INBOX")
uids = conn.uid_search(['ALL'])
puts uids.join(',')
conn.logout
conn.disconnect

Related

OpenSSL::SSL::SSLServer doesn't add peer_cert

I have server working that looks a little bit like this
require "socket"
require "openssl"
require "thread"
listeningPort = Integer(ARGV[0])
server = TCPServer.new(listeningPort)
sslContext = OpenSSL::SSL::SSLContext.new
sslContext.cert = OpenSSL::X509::Certificate.new(File.open("cert.pem"))
sslContext.key = OpenSSL::PKey::RSA.new(File.open("priv.pem"))
sslServer = OpenSSL::SSL::SSLServer.new(server, sslContext)
puts "Listening on port #{listeningPort}"
loop do
connection = sslServer.accept
Thread.new {...}
end
When I connect with TLS1.3 and I provide a client cert, I can see that it's working when I verify the cert in the ssl context, but peer_cert is never set on the connection, only the context receives a session.
Do I need to upgrade manually to TLS to access the cert from the client?
The reason why I want it is, I can restrict content or authenticate by looking at the cert on the Gemini protocol
After a lot of reading in the OpenSSL docs I found a solution:
I set the sslContext.verify_mode = OpenSSL::SSL::VERIFY_PEER and add a verification callback
sslContext.verify_callback = proc do |_a, _b|
true
end
Which will behave like VERIFY_NONE, but it does request the peer certificate (which it won't when mode is set to VERIFY_NONE as the documentation states: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_verify.html

send email via telnet ruby script

I'm new with ruby and need to send an email via telnet using a relay host with no authentication. I can do it with a linux shell but I need to put it in a script so I can "simplify" its use, I know it's not the best way but I can't find other since the server where i'm working on it's severely restricted and limited.
require 'net/telnet.rb'
mail = Net::Telnet::new(
"Host" => "domain.ip", # default: "localhost"
"Port" => 25, # default: 23
"Output_log" => "output_log", # default: nil (no output)
"Dump_log" => "dump_log", # default: nil (no output)
"Prompt" => /[$%#>] \z/n, # default: /[$%#>] \z/n
"Telnetmode" => true, # default: true
"Timeout" => 10, # default: 10
"Waittime" => 0, # default: 0
)
mail.cmd('helo MYDOMAIN'){ |c| print c }
mail.cmd('mail from: test#domain.com')
mail.cmd('rcpt to: test2#domain.com')
mail.cmd('data')
mail.cmd("subject: test cmd \n\n mensaje de prueba\n\n")
mail.cmd(".\n")
mail.close
I found the net/telnet.rb ruby class and this is my try... after mail.cmd('helo MYDOMAIN') I can't keep writing other commands, what I get is:
220 mail.server.com ESMTP
250 mail.server.com
After this I'm suposed to write mail from, etc. to create the mail. But I can't in the ruby script. I have try using:
mail.puts('mail from: test...')
mail.write('mail from: test...')
mail.print('mail from: test...')
mail.cmd('mail from: test...')
As written in documentation
Also I don't get the telnetmode(true|false) command maybe you could explain it to me please.
-- Edit --
Shell code trying to emulate:
telnet domain.ip 25
#=> Trying domain.ip...
#=> Connected to domain.ip.
#=> Escape character is '^]'.
#=> 220 mail.server.com ESMTP
helo MYDOMAIN
#=>250 mail.server.com
mail from:test#mydomain.com
#=> 250 2.1.0 Ok
rcpt to:test2#mydomain.com
#=> 250 2.1.0 Ok
data
#=> 354 End data with <CR><LF>.<CR><LF>
subject: test mail
test mail body
.
#=> 250 2.0.0 =k: queued as B6F08480D12
quit
#=> 221 2.0.0 Bye
#=> Connection closed by foreign host.
The telnet protocol is really, really rudimentary which is why the telnet command is useful for testing TCP/IP based services such as SMTP or HTTP. It does not mean those services actually use the telnet protocol, as they don't. They're conveniently plain-text in nature which means it's practical to use telnet for simple tests.
You should not be using the Telnet module for anything other than connecting to telnet services, though given it's 2017 it's unlikely you'll find any of those around.
You should be using something like Socket to connect. This can create a bare TCP/IP connection with full control over sending. As this is a wrapper around a regular POSIX filehandle you can use all the IO methods on it for reading, writing, and other control functions, like a proper socket shutdown.
Writing an SMTP adapter is not as easy as it seems, there's a lot of tricky things to tackle with regard to IO. You'll need to use IO.select to properly test for new data, plus that the socket is clear to write your email.
Here's a new stub:
require 'socket'
mail = TCPSocket.new("smtp.example.com", 25)
mail.write("HELO example.com\r\n")
Another note is that when you call require you should never specify the file extension. It's always handled for you.
Thanks to the help of the user ddubs how suggest the net\smtp gem (One that I didn't know) I was able to create a simple mail sender and using the mailfactory gem
Is it a strict requirement that you use telnet? Using ruby-doc.org/stdlib-2.0.0/libdoc/net/smtp/rdoc/Net/SMTP.html will turn your "difficult to maintain" script into something that is much easier to maintain. Even for someone who is completely new to Ruby. – ddubs
Here is the code sample
require 'net/smtp'
require 'mailfactory'
mail_body_HTML = '<h1> mail title</h1> your text in <b>HTML</b>'
mail_body_PLAIN = 'this is plain text'
mail_subject = 'test email'
mail_from = 'noreply#mydomain.com'
mail_to = 'user#otherdomain.com'
# mail_filePath = ''
mail = MailFactory.new()
mail.to = mail_to
mail.from = mail_from
mail.subject = mail_subject
mail.html = mail_body_HTML
# mail.text = mail_body_PLAIN
# mail.attach(mail_filePath)
relay_ip = x.x.x.x
Net::SMTP.start(relay_ip,25) do |smtp|
smtp.send_message(mail.to_s, mail_from, mail_to)
end

are there any APIs for changing google-talk status?

I want to write an application, which will:
recieve and send email messages ( I know, I can do it with
ActionMailer using RoR )
chat with my Google+ friends
change my GoogleTalk (gmail) status
So, when I open my gmail interface, I see list with my contacts on the left side of page. I can open chat with people from this list, I can change status and name (near my little google+ avatar).
Is exists some Google API for changing google-talk status (special message)? Can I do it using some RubyOnRails gems?
Thanks.
So, this pretty lines of ruby code ( using xmpp4r gem ),
change your google_talk status and send chat_message to your friend.
Thank you, #Arkan!
require 'xmpp4r'
# init jabber client
client_jid = Jabber::JID.new( 'your_email#gmail.com' )
client = Jabber::Client.new( client_jid )
client.connect 'talk.google.com'
client.auth 'your_gmail_password'
# change google_talk status
client.send( Jabber::Presence.new.set_show( :chat ).set_status( 'Your New GoogleTalk status' ) )
# send chat_message to friend
friend = Jabber::JID.new("your_friend_email#gmail.com")
message = Jabber::Message::new(friend, "it's chat message").set_type(:normal).set_id('1')
client.send(message)
I love ruby ^_^ !
Xmpp Implementation of Gtalk. To change status This might help you.
import xmpp
import dns
class Gtalk():
def __init__(self,bot_id,bot_pwd):
self.bot_id = bot_id
self.bot_pwd = bot_pwd
def connect(self):
self.jid = xmpp.protocol.JID(self.bot_id)
self.presnc = xmpp.protocol.Presence()
self.conn = xmpp.Client(self.jid.getDomain(),debug=[])
if self.conn.connect():
print 'connected..'
self.auth()
else:
print 'err to connect'
def auth(self):
if self.conn.auth(self.jid.getNode(),self.bot_pwd):
self.conn.sendInitPresence()
print 'Authenticated..'
else:
print 'err to authenticate'
def setStatus(self,value):
self.conn.send(xmpp.protocol.Presence(status=value))
def invisible(self,username):
self.conn.send(xmpp.protocol.Presence(username,typ='unavailable'))
def visible(slef,username):
self.conn.send(xmpp.protocol.Presence(username,typ=None))
def disconnect(self):
self.conn.disconnect()

Can't send a message from xmpp4r?

I'm trying to test sending a message to one jid account by using xmpp4r:
require 'xmpp4r'
include Jabber
jid = JID::new('alice#wonderland.lit')
password = 'secr3t'
cl = Client::new(jid)
cl.connect('166.78.7.179')
cl.auth(password)
cl.send(Presence.new)
to = 'arthur#wonderland.lit'
subject = 'XMPP4R test'
body = 'Hi, this is a XMPP4R test'
m = Message::new( to, body ).set_type(:chat).set_id('1').set_subject(subject)
cl.send m
But I always get the following exception:
/home/subout/.rvm/gems/ruby-1.9.3-p374#subout/gems/xmpp4r-0.5/lib/xmpp4r/client.rb:118:in `rescue in auth': closed stream (Jabber::ClientAuthenticationFailure)
from /home/subout/.rvm/gems/ruby-1.9.3-p374#subout/gems/xmpp4r-0.5/lib/xmpp4r/client.rb:108:in `auth'
from send_message2.rb:9:in `<main>'
First of all, would you please add Jabber::debug = true setting
before cl.connect and post output here?
Secondly, it looks like there is a problem with XMPP server (are you sure it’s running at
'166.78.7.179'?)
And, the last but not the least, why do you decide
to use “obsolete” xmpp4r rather than it’s modern successor
Blather?

Is there a way to attach Ruby Net::HTTP request to a specific IP address / network interface?

Im looking a way to use different IP addresses for each GET request with standard Net::HTTP library. Server has 5 ip addresses and assuming that some API`s are blocking access when request limit per IP is reached. So, only way to do it - use another server. I cant find anything about it in ruby docs.
For example, curl allows you to attach it to specific ip address (in PHP):
$req = curl_init($url)
curl_setopt($req, CURLOPT_INTERFACE, 'ip.address.goes.here';
$result = curl_exec($req);
Is there any way to do it with Net::HTTP library? As alternative - CURB (ruby curl binding). But it will be the last thing i`ll try.
Suggestions / Ideas?
P.S. The solution with CURB (with dirty tests, ip`s being replaced):
require 'rubygems'
require 'curb'
ip_addresses = [
'1.1.1.1',
'2.2.2.2',
'3.3.3.3',
'4.4.4.4',
'5.5.5.5'
]
ip_addresses.each do |address|
url = 'http://www.ip-adress.com/'
c = Curl::Easy.new(url)
c.interface = address
c.perform
ip = c.body_str.scan(/<h2>My IP address is: ([\d\.]{1,})<\/h2>/).first
puts "for #{address} got response: #{ip}"
end
I know this is old, but hopefully someone else finds this useful, as I needed this today. You can do the following:
http = Net::HTTP.new(uri.host, uri.port)
http.local_host = ip
response = http.request(request)
Note that you I don't believe you can use Net::HTTP.start, as it doesn't accept local_host as an option.
There is in fact a way to do this if you monkey patch TCPSocket:
https://gist.github.com/800214
Curb is awesome but won't work with Jruby so I've been looking into alternatives...
Doesn't look like you can do it with Net:HTTP. Here's the source
http://github.com/ruby/ruby/blob/trunk/lib/net/http.rb
Line 644 is where the connection is opened
s = timeout(#open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
The third and fourth arguments to TCPSocket.open are local_address and local_port, and since they're not specified, it's not possible. Looks like you'll have to go with curb.
Of course you can. I did as below:
# remote_host can be IP or hostname
uri = URI.parse( "http://" + remote_host )
http = Net::HTTP.new( uri.host, uri.port )
request = Net::HTTP::Get.new(uri.request_uri)
request.initialize_http_header( { "Host" => domain })
response = http.request( request )

Resources