I am trying to send email from a PL/SQL program using UTL_MAIL.Send packaged procedure. But I am getting the following error:
ORA-29278: SMTP transient error: 421 Service not available
I searched on web, and found that this error occurs because there is no SMTP service on the host running my Oracle database.
Now if I want to use another SMTP server, for example , hotmail's "smtp.live.com", i need my user name and password.
How can I give my password into the UTL_MAIL.Send procedure call?
(according to my understanding, in order to use any other SMTP server, I have to provide my username/password). I know that to use UTL_MAIL package, we set the SMTP server with an initialization parameter, and we can give username in "Sender" parameter, but the question is where should we give the password?
Basically you have to use the "lower level" UTL_SMTP package in order to send the various SMTP messages required by the distant SMTP server.
Authentication
From Stefano Ghio's blog:
-- prepare base64 encoded username and password
l_encoded_username := UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw(username)));
l_encoded_password := UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw(password)));
-- Open connection and send EHLO and AUTH messages
l_conn := UTL_SMTP.open_connection(smtpHost, smtpPort);
UTL_SMTP.ehlo(l_conn, smtpHost);--DO NOT USE HELO
UTL_SMTP.command(l_conn, 'AUTH', 'LOGIN');
UTL_SMTP.command(l_conn, l_encoded_username);
UTL_SMTP.command(l_conn, l_encoded_password);
The main issue here is that you need be able to send the AUTH message using the "right" authentication scheme for your server. I can't say for "smtp.live.com" specifically, but depending the server's configuration, they might be different authentication scheme, like PLAIN and LOGIN, DIGEST_MD5, ... Usually (always ?) the parameters (username, password) are base64 encoded.
Sending mail
But the bad news is, since you are now using a low-level library, you have to implement the client part of the SMTP protocol yourself. From the same source as above (edited by myself to only keep the absolutely minimum necessary stuff):
UTL_SMTP.mail(l_conn, mailFrom);
UTL_SMTP.rcpt(l_conn, rcptTo);
[...]
--start multi line message
UTL_SMTP.open_data(l_conn);
--prepare mail header
UTL_SMTP.write_data(l_conn, 'To: ' || rcptTo || crlf);
UTL_SMTP.write_data(l_conn, 'From: ' || mailFrom || crlf);
UTL_SMTP.write_data(l_conn, 'Subject: ' || messageSubject || crlf);
--include the message body
UTL_SMTP.write_data(l_conn, messageBody || crlf || crlf);
--send the email and close connection
UTL_SMTP.close_data(l_conn);
UTL_SMTP.quit(l_conn);
Using SSL/TLS
And now, for the very bad news: some server required a secure connection. Claiming something like 530 Must issue a STARTTLS command first. Unfortunately, UTL_SMTP.STARTTLS is only supported starting from Oracle Database 11g release 2 (11.2.0.2).
If you are lucky enougth to use a recent version of Oracle, you should write something like that to open a secure connection with your server:
l_conn := UTL_SMTP.open_connection(l_conn, smtpHost,
wallet_path => 'file:/oracle/wallets/smtp_wallet',
wallet_password => 'password',
secure_connection_before_smtp => FALSE);
UTL_SMTP.starttls(l_conn);
To quote Oracle's documentation:
SSL/TLS requires an Oracle wallet which must be specified when the connection was opened by the OPEN_CONNECTION Functions.
Please see the corresponding documentation to see how to create and manage wallet
Few more readings:
Oracle's documentation about sending e-mail from PL/SQL has some nice examples too depicting you to properly send your mail message using UTL_SMTP.
See the SMTP Authentification Wikipedia page for a transcription of a typical SMTP session.
found this online. seems like either of these should do the trick:
AUTH PLAIN
v_plain_string :=
UTL_RAW.cast_to_varchar2(
UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw('my_user_name'||chr(0)||'my_user_name'||chr(0)||'my_secret_password'))
);
v_connection := UTL_SMTP.open_connection(:v_smtp_server);
UTL_SMTP.ehlo(v_connection, 'mydomain.com'); -- Must use EHLO vs HELO
UTL_SMTP.command(v_connection, 'AUTH', 'PLAIN ' || v_plain_string);
AUTH LOGIN
v_username_b64 :=
UTL_RAW.cast_to_varchar2(
UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw(:v_username))
);
v_password_b64 :=
UTL_RAW.cast_to_varchar2(
UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw(:v_password))
);
v_connection := UTL_SMTP.open_connection(:v_smtp_server);
UTL_SMTP.ehlo(v_connection, 'mydomain.com'); -- Must use EHLO vs HELO
UTL_SMTP.command(v_connection, 'AUTH', 'LOGIN'); -- should receive a 334 response, prompting for username
UTL_SMTP.command(v_connection, v_username_b64); -- should receive a 334 response, prompting for password
UTL_SMTP.command(v_connection, v_password_b64); -- should receive a 235 response, you are authenticated
also i think when you talk about the "Sender" parameter you might be referring to the "From" header for the email itself.
Related
I am writing a .NET Console application, our goal is keep a message on the queue and read the message. the message header should contain User Name & Password. I try to pass the Message with below code it is not working.
hashTable.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_CLIENT);
hashTable.Add(MQC.HOST_NAME_PROPERTY, strServerName);
hashTable.Add(MQC.CHANNEL_PROPERTY, strChannelName);
hashTable.Add(MQC.PORT_PROPERTY, 1414);
hashTable.Add(MQC.USER_ID_PROPERTY, "XXXXXX");
hashTable.Add(MQC.PASSWORD_PROPERTY, "XXXXXX");
hashTable.Add(MQC.USE_MQCSP_AUTHENTICATION_PROPERTY, true);
queueManager = new MQQueueManager(strQueueManagerName,hashTable);
queue = queueManager.AccessQueue(requestQueue, MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING);
requestMessage = new MQMessage();
requestMessage.WriteString(StrAPICMessage);
requestMessage.Format = MQC.MQFMT_STRING;
requestMessage.MessageType = MQC.MQMT_REQUEST;
requestMessage.Report = MQC.MQRO_COPY_MSG_ID_TO_CORREL_ID;
requestMessage.ReplyToQueueName = responseQueue;
requestMessage.ReplyToQueueManagerName = strQueueManagerName;
queuePutMessageOptions = new MQPutMessageOptions();
queue.Put(requestMessage, queuePutMessageOptions);
In the Message Descriptor it is taking the default value mentioned MQ Server. it is not takeing my UserName "XXXXX"
I have tried using the CSICS Bridge header also unable to send the message with my application Service account + Password.
help me on this scenario.
See "MQCSP authentication mode" here: https://www.ibm.com/docs/en/ibm-mq/latest?topic=authentication-connection-java-client
It says:
In this mode, the client-side user ID is sent as well as the user ID and password to be authenticated, so you are able to use ADOPTCTX(NO). The user ID and password are available to a server-connection security exit in the MQCSP structure that is provided in the MQCXP structure.
"client-side user ID" means the UserId that the application is running under. Therefore, if you are authenticating with a different UserId than the one that the application is running under.
Therefore, you (or your MQAdmin) will need to change ADOPTCTX to YES.
Your program works fine for me, when I fill in the correct values for my qmgr connection.
Except for one change I made: instead of TRANSPORT_MQSERIES_CLIENT I used TRANSPORT_MQSERIES_MANAGED. That keeps everything in the managed .Net space.
Without that change, I was actually getting MQRC_UNSUPPORTED_FUNCTION during the connection which typically means either some kind of mismatch between versions of interfaces, or it couldn't find the C dll that underpins the unmanaged environment. And I wasn't going to take time to dig into that further.
Running amqsbcg against the output queue, I see
UserIdentifier : 'mqguest '
which is the id I had set in the USER_ID_PROPERTY.
I have a workflow which looks like below.
The first task of this workflow runs a simple select * query and the next one sends an email. They are working fine individually. What I want is to send the output of the SQL task as an input to the email task so that it can be attached to the email being sent.
I have tried to manually enter the runId of the SQL task in below field of notification object and it works as expected. But how to make this field take dynamic value from its predecessor instead of a hardcoded one?
Also, is there a way I can include the output of the select * in the email body as a table ?
Update --1
I was able to get a hold of runId of preceding task via the below script. Now only need help with including it in the mail body as opposed to attachement.
:SET &NR# = SYS_ACT_PREV_NR()
:PRINT "RunID of the previous task is &NR#."
Firstly;
Setup
The package is loaded by running the following scripts.
sys/passwordord AS SYSDBA
#$ORACLE_HOME/rdbms/admin/utlmail.sql
#$ORACLE_HOME/rdbms/admin/prvtmail.plb
In addition the SMTP_OUT_SERVER parameter must be set to identify the SMTP server.
CONN sys/password AS SYSDBA
ALTER SYSTEM SET smtp_out_server='smtp.domain.com' SCOPE=SPFILE;
-- Instance restart only necessary in 10gR1.
SHUTDOWN IMMEDIATE
STARTUP
I would suggest you use a mail relay on the database server, rather than connecting directly to an external mail server. The mail relay configuration can be simple, with a reference to "localhost" in the SMTP_OUT_SERVER parameter. Any complexities about connecting to your external mail server are then hidden in the mail relay configuration.
Send Emails
With the configuration complete we can now send a mail using the SEND procedure. It accepts the following parameters.
SENDER : This should be a valid email address.
RECIPIENTS : A comma-separated list of email addresses.
CC : An optional comma-separated list of email addresses.
BCC : An optional comma-separated list of email addresses.
SUBJECT : The subject line for the email.
MESSAGE : The email body.
MIME_TYPE : Set to 'text/plain; charset=us-ascii' by default.
PRIORITY : (1-5) Set to 3 by default.
REPLYTO : Introduced in 11gR2. A valid email address.
Below is an example of the usage.
BEGIN
UTL_MAIL.send(sender => 'me#domain.com',
recipients => 'person1#domain.com,person2#domain.com',
cc => 'person3#domain.com',
bcc => 'myboss#domain.com',
subject => 'UTL_MAIL Test',
message => 'If you get this message it worked!');
END;
/
Send Emails with Attachments
The package also supports sending mails with RAW and VARCHAR2 attachments using the SEND_ATTACH_RAW and SEND_ATTACH_VARCHAR2 packages respectively. They work in a similar fashion to the SEND procedure, with a few extra parameters.
ATTACHMENT : The contents of the attachment. This should be VARCHAR2 or RAW depending on which procedure you call.
ATT_INLINE : Indicates if the attachment should be readable inline. Default FALSE.
ATT_MIME_TYPE : The default is 'text/plain; charset=us-ascii'.
ATT_FILENAME : The name for the attachment.
Below is an example of sending an email with a text attachment.
BEGIN
UTL_MAIL.send_attach_varchar2 (
sender => 'me#domain.com',
recipients => 'person1#domain.com,person2#domain.com',
cc => 'person3#domain.com',
bcc => 'myboss#domain.com',
subject => 'UTL_MAIL Test',
message => 'If you get this message it worked!',
attachment => 'The is the contents of the attachment.',
att_filename => 'my_attachment.txt'
);
END;
/
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
#puts params[:auth_token]
self.current_user = find_verified_user
logger.add_tags 'ActionCable', current_user.name
end
end
end
I don't use web as end point for action cable, so I want to use auth_token for authentication. By default action cable use session user id for authentication. How to pass params to connect method?
I managed to send my authentication token as a query parameter.
When creating my consumer in my javascript app, I'm passing the token in the cable server URL like this:
wss://myapp.com/cable?token=1234
In my cable connection, I can get this token by accessing the request.params:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
logger.add_tags 'ActionCable', current_user.name
end
protected:
def find_verified_user
if current_user = User.find_by(token: request.params[:token])
current_user
else
reject_unauthorized_connection
end
end
end
end
It's clearly not ideal, but I don't think you can send custom headers when creating the websocket.
Pierre's answer works. However, it's a good idea to be explicit about expecting these parameters in your application.
For instance, in one of your config files (e.g. application.rb, development.rb, etc...) you can do this:
config.action_cable.mount_path = '/cable/:token'
And then simply access it from your Connection class with:
request.params[:token]
Unfortunately for websocket connections, additional headers and custom ones are not supported1 by most2 websocket clients and servers.
So the possible options are:
Attach as an URL parameter and parse it on the server
path.to.api/cable?token=1234
# and parse it like
request.params[:token]
Cons: It could be vulnerable as it may end up in logs and system process information available to others that have access to the server, more here
Solution: Encrypt the token and attach it, so even if it can be seen in the logs, it would serve no purpose until its decrypted.
Attach JWT in one of the allowed parameters.
Client side:
# Append jwt to protocols
new WebSocket(url, existing_protocols.concat(jwt))
I created a JS library action-cable-react-jwt for React and React-Nativethat just does this. Feel free to use it.
Server side:
# get the user by
# self.current_user = find_verified_user
def find_verified_user
begin
header_array = self.request.headers[:HTTP_SEC_WEBSOCKET_PROTOCOL].split(',')
token = header_array[header_array.length-1]
decoded_token = JWT.decode token, Rails.application.secrets.secret_key_base, true, { :algorithm => 'HS256' }
if (current_user = User.find((decoded_token[0])['sub']))
current_user
else
reject_unauthorized_connection
end
rescue
reject_unauthorized_connection
end
end
1 Most Websocket APIs (including Mozilla's) are just like the one below:
The WebSocket constructor accepts one required and one optional
parameter:
WebSocket WebSocket(
in DOMString url,
in optional DOMString protocols
);
WebSocket WebSocket(
in DOMString url,
in optional DOMString[] protocols
);
url
The URL to which to connect; this should be the URL to which the
WebSocket server will respond.
protocols Optional
Either a single protocol string or an array of protocol strings. These
strings are used to indicate sub-protocols, so that a single server
can implement multiple WebSocket sub-protocols (for example, you might
want one server to be able to handle different types of interactions
depending on the specified protocol). If you don't specify a protocol
string, an empty string is assumed.
2 There are always excpetions, for instance, this node.js lib ws allows building custom headers, so you can use the usual Authorization: Bearer token header, and parse it on the server but both client and server should use ws.
As I already stated in a comment the accepted answer is not a good idea, simply because the convention is that the URL should not contain such sensitive data. You can find more information here: https://www.rfc-editor.org/rfc/rfc6750#section-5.3 (though this is specifically about OAuth).
There is however another approach: Use HTTP basic auth via the ws url. I found that most websocket clients allow you to implicitly set the headers by prepending the url with http basic auth like this: wss://user:pass#yourdomain.com/cable.
This will add the Authorization header with a value of Basic .... In my case I was using devise with devise-jwt and simply implemented a strategy which inherited from the one provided in the gem which pulls the jwt out of the Authorization header. So I set the url like this: wss://TOKEN#host.com/cable which sets the header to this (pseudo): Basic base64("token:") and parse that in the strategy.
In case any of you would like to use ActionCable.createCustomer. But have renewable token as I do:
const consumer = ActionCable.createConsumer("/cable")
const consumer_url = consumer.url
Object.defineProperty(
consumer,
'url',
{
get: function() {
const token = localStorage.getItem('auth-token')
const email = localStorage.getItem('auth-email')
return consumer_url+"?email="+email+"&token="+token
}
});
return consumer;
Then in case that the connection is lost it will be opened with a fresh new token.
to add to previous answers, if you used your JWT as a param, you're going to have to at least btoa(your_token) #js and Base64.decode64(request.params[:token]) #rails as rails considers dot '.' a separator so your token will be cut off #rails params side
Another way (the way I did it in the end instead of my other answer) would be to have a authenticate action on your channel. I used this to determine the current user and set it in the connection/channel. All the stuff is send over websockets so credentials are not an issue here when we have it encrypted (i.e. wss).
I was asked about it recently and want to share the solution that I currently use in production systems.
class MyChannel < ApplicationCable::Channel
attr_accessor :current_user
def subscribed
authenticate_user!
end
private
# this works, because it is actually sends via the ws(s) and not via the url <3
def authenticate_user!
#current_user ||= JWTHelper.new.decode_user params[:token]
reject unless #current_user
end
end
Then re-use warden strategies to work with that JWT (and let it handle all possible edge cases and pitfalls).
class JWTHelper
def decode_user(token)
Warden::JWTAuth::UserDecoder.new.call token, :user, nil if token
rescue JWT::DecodeError
nil
end
def encode_user(user)
Warden::JWTAuth::UserEncoder.new.call(user, :user, nil).first
end
end
Though I didn't use ActionCable for the frontend it should roughly work like this:
this.cable.subscriptions.create({
channel: "MyChannel",
token: "YOUR TOKEN HERE",
}, //...
It is also possible to pass the authentication token in the request headers and then validate the connection by accessing the request.headers hash.
For example, if the authentication token were specified in a header called 'X-Auth-Token' and your User model have a field auth_token you could do:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
logger.add_tags 'ActionCable', current_user.id
end
protected
def find_verified_user
if current_user = User.find_by(auth_token: request.headers['X-Auth-Token'])
current_user
else
reject_unauthorized_connection
end
end
end
end
As for security of Pierre's answer: If you're using WSS protocol, which uses SSL for encryption, then the principles for sending secure data should the same as for HTTPS. When using SSL, query string parameters are encrypted as well as the body of the request. So if in HTTP APIs you're sending any kind of token through HTTPS and deem it secure, then it should be the same for WSS. Just remember that the same as for HTTPS, don't send credentials like password through query parameters, as the URL of the request could be logged on a server and thus stored with your password. Instead use things like tokens that are issued by the server.
Also you can check this out (this basically describes something like JWT authentication + IP address verification): https://devcenter.heroku.com/articles/websocket-security#authentication-authorization.
In case of LDAP authenticaion, what are the parameters that are generally used for authentication. I guess using DN would be a headache for users logging in via ldap because it is too large to remember.
How is the option of using uid or sAMAccountName for authentication where in my implementation, I retrieve the dn of the corresponding uid or sAMAccountName and proceed to authentication.
Am I going the right track?
In LDAP, a connection or session can be authenticated. When an LDAP client makes a new connection to an LDAP directory server, the connection has an authorization state of anonymous. The LDAP client can request that the authorization state be changed by using the BIND request.
A BIND request has two forms: simple and SASL. Simple uses a distinguished name and a password, SASL uses one of a choice of mechanisms, for example, PLAIN, LOGIN, CRAM-MD5, DIGEST-MD5, GSSAPI, and EXTERNAL - all of which except for GSSAPI and EXTERNAL are too weak to use in production scenarios or mission-critical areas.
To Use the simple BIND, construct a BIND request and transmit it to the LDAP directory server. The LDAP directory server will respond with a BIND response in which is contained a result code. The result code is an integer, anything other zero indicates that the BIND request failed. If the result code is zero, the BIND request succeeded and the session authorization state has been changed to that of the distinguished name used in the BIND request.
Each subsequent BIND request on the same connection/session causes the authorization state to be set to anonymous and each successive successful BIND request on the same connection/session causes the authorization state to be set to the authorization state associated with the authentication ID, which is the distinguished name in the case of the simple BIND, but might be something else entirely where SASL is used - modern professional quality servers can map the incoming names to different DNs.
Whichever language is used, construct a BIND request, transmit it to the server, and interpret the response.
Update:
If the distinguished name is not known, or is too cumbersome (often the case with web application users who don't know how they are authenticated and would not care if they did know), the LDAP application should search the directory for the user. A successful search response always contains the distinguished name, which is then used in a simple BIND.
The search contains at a minimum, the following:
base object: a distinguished name superior to the user, for example, dc=example,dc=com
a scope: base level, one level below base, or subtree below base. For example, if users are located subordinate to ou=people,dc=example,dc=com, use base object ou=people,dc=example,dc=com and a scope of one-level. These search parameters find entries like: uid=user1,ou=people,dc=example,dc=com
a filter: narrows down the possible search results returned to the client, for example (objectClass=inetOrgPerson)
a list of requested attributes: the attributes from an entry to return to the client. In this case, use 1.1, which means no attributes and returns on the DN (distinguished name), which is all that is required for the simple BIND.
see also
the links in the about section here
LDAP servers only understand LDAP queries; they don't have "usernames" like you and I are used to.
For LDAP, to authenticate someone, you need to send a distinguished name of that person's (or entity's) entry in LDAP; along with their password.
Since you mentioned sAMAccountName I am assuming you are working with Active Directory. Active Directory allows anonymous binds - this means you can connect to it without providing any credentials; but cannot do any lookups without providing credentials.
If you are using python-ldap and Cython (and not IronPython which has access to the various .NET APIs that make this process very easy); then you follow these steps.
Typically you use a pre-set user that has appropriate rights to the tree, and connect to the directory with that user first, and then use that user's access for the rest of the authentication process; which generally goes like this:
Connect to AD with the pre-set user.
Query active directory with the pre-set user's credentials and search for the distinguished name based on the sAMAccountName that the user will enter as their "username" in your form.
Attempt to connect again to Active Directory using the distinguished name from step 2, and the password that the user entered in their form.
If this connection is successful, then the user is authenticated.
So you need two main things:
The login attribute (this is the "username" that LDAP understands)
A LDAP query that fetches information for your users
Following is some rough code that can do this for you:
AD_USER = 'your super user'
AD_PASSWORD = 'your super user password'
AD_BIND_ATTR = 'userPrincipalName' # this is the "login" for AD
AD_URL = "ldap://your-ad-server"
AD_DN = "DC=DOMAIN,DC=COM"
AD_LOGIN_ATTR = 'sAMAccountName' # this is what you user will enter in the form
# as their "login" name,
# this is what they use to login to Windows
# A listing of attributes you want to fetch for the user
AD_ATTR_SEARCH = ['cn',
'userPrincipalName',
'distinguishedName',
'mail',
'telephoneNumber','sAMAccountName']
def _getbinduser(user):
""" This method returns the bind user string for the user"""
user_dn = AD_DN
login_attr = '(%s=%s)' % (AD_LOGIN_ATTR,user)
attr_search = AD_ATTR_SEARCH
conn = ldap.initialize(AD_URL)
conn.set_option(ldap.OPT_REFERRALS,0)
conn.set_option(ldap.OPT_PROTOCOL_VERSION,3)
try:
conn.bind(AD_USER,AD_PASSWORD)
conn.result()
except:
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
# Exit the script and print an error telling what happened.
sys.exit("LDAP Error (Bind Super User)\n ->%s" % exceptionValue)
try:
result = conn.search_s(user_dn,
ldap.SCOPE_SUBTREE,
login_attr, attr_search)
except:
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
# Exit the script and print an error telling what happened.
sys.exit("LDAP Error (Search)\n ->%s" % exceptionValue)
# Return the user's entry from AD, which includes
# their 'distinguished name'
# we use this to authenticate the credentials the
# user has entered in the form
return result[0][1]
def authenticate(user,password):
bind_attr = AD_BIND_ATTR
user_dn = AD_DN
login_attr = '(%s=%s)' % (AD_LOGIN_ATTR,user)
data = _getbinduser(user)
if len(data) == 1:
return None
# Information we want to return from the directory
# for each user, season to taste.
info = {}
info['name'] = data['cn'][0]
info['email'] = data['mail'][0]
try:
info['phone'] = data['telephoneNumber'][0]
except KeyError:
info['phone'] = 'Not Available'
conn = ldap.initialize(Config.AD_URL)
conn.set_option(ldap.OPT_REFERRALS,0)
conn.set_option(ldap.OPT_PROTOCOL_VERSION,3)
try:
# Now we have the "bind attribute" (LDAP username) for our user
# we try and connect to see if LDAP will authenticate
conn.bind(data[bind_attr][0],password)
conn.search(user_dn,ldap.SCOPE_SUBTREE,login_attr,None)
conn.result()
return info
except (ldap.INVALID_CREDENTIALS,ldap.OPERATIONS_ERROR):
return None
One small expansion on Terry's excellent comment. If you store all your users in the same part of the DIT, and use the same attribute to identify them, you can programmatically construct the DN, rather than searching for it.
am developing ruby on rails3 application where i am sending an email to user and if user replies that email then that reply content, date should be updated to the database. For this i have ProductComment model. when admin sends comment to the user it will be stored in the database. if user replies to that then database should be updated accordingly. I am trying to use mailman. I have installed the gem. But am not getting how to get the comment id, what should i write in replyto address, where to write the mailman code and from which mail i should read.
Am sending email like this:
mail(:to => #user.email, :subject => "Edit Your Product", :reply_to=>"abc#syz.com)
I am handling it in products controller like this:
require 'mailman'
Mailman::Application.run do
to 'abc#xyz.com' do
ProductComment.create(message)
end
end
Please help me to come out from this problem
Please tell me how to use mailman gem in ruby on rails3 application
there is a recent pro-episode on receiving emails with mailman on railscasts: http://railscasts.com/episodes/313-receiving-email-with-mailman
chmod +x script/mailman_server
cat mailman_test.eml | script/mailman_server
script/mailman_server
-
# script/mailman_server
#!/usr/bin/env ruby
require "rubygems"
require "bundler/setup"
require "mailman"
Mailman.config.logger = Logger.new("log/mailman.log")
Mailman.config.pop3 = {
server: 'pop.gmail.com', port: 995, ssl: true,
username: ENV["GMAIL_USERNAME"],
password: ENV["GMAIL_PASSWORD"]
}
Mailman::Application.run do
default do
begin
Ticket.receive_mail(message)
rescue Exception => e
Mailman.logger.error "Exception occurred while receiving message:\n#{message}"
Mailman.logger.error [e, *e.backtrace].join("\n")
end
end
end
-
def self.receive_mail(message)
ticket_id = message.subject[/^Update (\d+)$/, 1]
if ticket_id.present? && Ticket.exists?(ticket_id)
Ticket.update(ticket_id, body: message.body.decoded)
else
Ticket.create subject: message.subject, body: message.body.decoded, from: message.from.first
end
end
Postmark Inbound is a good choice. Setup like so:
Sign up for Postmark, they will give you an email which Postmark will assign to your account.
Sign up for Google Apps branded Gmail for your domain. Set up forwarding from an account to the Postmark email address. People can now email reply#yourdamin.com, and it will be forwarded to Postmark.
Create a callback URL. When Postmark receives an email it will package it up and post it to your callback. You can then access the email attributes via the params hash.
To implement replying to messages, simply add a reply to field to your outgoing message which contains a unique hash for the message, e.g.
reply+uniquehash#yourdomain.com.
This is a legal email address, and will be sent to reply#yourdomain.com. You can then parse out the hash in your callback and use it to match the reply to the original message.
Simple :)