Poco stops after SMTPClientSession.login - c++

I just started with the Poco library and tried to create an email program (Which I knew virtually nothing about). The following is my code (There may be other problems with it besides the one I've encountered so far, but I just started working on it)
int main(int argc, char** argv)
{
Poco::Net::SocketAddress add("smtp.gmail.com:465");
Poco::Net::StreamSocket sock(add);
Poco::Net::SMTPClientSession sess(sock);
std::cout << "-";
sess.login(
"gmail.com",
Poco::Net::SMTPClientSession::AUTH_LOGIN,
"----",
"----"
);
Poco::Net::MailMessage msg;
Poco::Net::MailRecipient resp(Poco::Net::MailRecipient::PRIMARY_RECIPIENT,"michaelrgoldfine#gmail.com");
msg.addRecipient(resp);
std::string content("HELP SOS");
msg.encodeWord(content);
std::cout << msg.getContent() << "-";
}
When I go into the debugger, it runs fine until it gets to sess.login then suddenly the little bar that represents were I am in the code disappears but the program keeps running (I'm not experienced enough to know what that means). None of the cout stuff I put in actually prints, the debugger just goes past that line but nothing shows up. After a little while this comes up:
terminate called throwing an exception
So what's going on?

You are attempting to use SMTP over TLS (the port 465 passed to the SocketAddress). In one shot you have to learn (1) TLS and certificate handling in POCO, before focusing on (2) your goal: sending an email message.
I suggest to start learning POCO with simpler examples. You can find sample code in the various samples directories in the POCO source code.
I think that your code is just hanging on the TLS handshake, because it doesn't know what to do.
These are the fixes you should do before looking at the solution:
Place your code inside a try/catch block. POCO uses exceptions.
Replace StreamSocket with SecureStreamSocket.
The simplest way to properly initialize SecureStreamSocket is via the Application class. See the Applications slides and Util/samples/SampleApp/src/SampleApp.cpp.
See the documentation for the SSLManager for how to properly tell the Application which certificates to use.
Don't specify an hostname to the login() method. The hostname is optional and should be the client hostname, not the server (See the SMTP RFC).
Remember to actually send the message! Your code is not sending it :-)
OK, and now for the running code. I left steps 4 and 6 as an exercise, but this code will at least run the TLS handshake, will tell you that it cannot verify the server's certificate and, if you answer Yes on the terminal to the questions on the certificates, it will fail the SMTP authentication.
class MiniApp : public Poco::Util::Application {
int main(const vector <string>& args) {
try {
Poco::Net::SocketAddress add("smtp.gmail.com:465");
Poco::Net::SecureStreamSocket sock(add);
Poco::Net::SMTPClientSession session(sock);
session.login(Poco::Net::SMTPClientSession::AUTH_LOGIN, "user", "pw");
Poco::Net::MailMessage msg;
Poco::Net::MailRecipient recipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT,
"michaelrgoldfine#gmail.com");
msg.addRecipient(recipient);
string content("HELP SOS");
msg.encodeWord(content);
} catch (Poco::Exception& e) {
cout << "Error: " << e.displayText() << endl;
return -1;
}
return 0;
}
};
POCO_APP_MAIN(MiniApp)

Yes, so I struggled with login(), trying to use smtp.gmail.com. This is the excerpt of the communication with the SSL session that made it work.
string host("smtp.gmail.com")
Poco::UInt16 port = 587;
SecureSMTPClientSession session(host, port);
session.open();
Poco::Net::initializeSSL();
SharedPtr<InvalidCertificateHandler> ptrHandler = new AcceptCertificateHandler(false);
Context::Ptr ptrContext = new Context(Context::CLIENT_USE, "", "", "", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:#STRENGTH");
SSLManager::instance().initializeClient(0, ptrHandler, ptrContext);
try
{
// SSL
session.login();
if (session.startTLS(ptrContext))
{
session.login(SMTPClientSession::AUTH_LOGIN, "user#gmail.com", "yourpassword");
session.sendMessage(message);
}
session.close();
Poco::Net::uninitializeSSL();
}
catch (SMTPException &e)
{
cout << e.message() << endl;
session.close();
Poco::Net::uninitializeSSL();
return 0;
}
Original source:
http://www.axistasoft.sg/tutorials/cpp/poco/item/sending-email-messages-using-smtp-protocol

Related

How do I open the serial port to a Zaber device?

I have a Zaber linear stage for which I'm developing a C++ backend, to integrate it in my framework.
I have installed the Zaber API by following the instructions from the Zaber webpage. The installer actually generates the dll, lib, and headers necessary for my backend, and I'm confident that my CMake configuration is correct, because I can instantiate objects from the Zaber API.
So now, I am trying in my framework to go through their first code example:
// I commented out the following block:
// - enableDeviceDbStore() is supposed to allow the library to cache
// information from the online database
// - I don't need the online db
// - when I call it, it throws a "string too long" exception.
// try
// {
// zaber::motion::Library::enableDeviceDbStore(".");
// }
// catch (std::exception& e)
// {
// LogError << e.what();
// }
try
{
_connection = zaber::motion::ascii::Connection::openSerialPort("COM6");
// this also throws a "string too long" exception
}
catch (std::exception& e)
{
std::cout << e.what() << std::endl;
}
std::vector<zaber::motion::ascii::Device> deviceList;
try
{
deviceList = _connection.detectDevices(false);
// this throws a "Connection has been closed" exception
}
catch (std::exception& e)
{
std::count << e.what() << std::endl;
}
std::count << "Found " << deviceList.size() << " devices." << std::endl;
The problem is, when I use the Zaber Launcher (their UI that allows to control a connected stage), the port is "COM6", and I have made sure to close the connection on the Zaber Launcher before trying to connect with my framework.
I have also tried to launch their pre-configured C++ code example (VS17 solution), with the same problems arising (except their example doesn't catch exceptions, so it just crashes).
None of my exception matches their troubleshooting section.
I don't know how to proceed from here, or how to interpret the "string too long" error message, considering that I'm sure of my connection port.

C++ OpenSSL: libssl fails to verify certificates on Windows

I've done a lot of looking around but I can't seem to find a decent solution to this problem. Many of the StackOverflow posts are regarding Ruby, but I'm using OpenSSL more or less directly (via the https://gitlab.com/eidheim/Simple-Web-Server library) for a C++ application/set of libraries, and need to work out how to fix this completely transparently for users (they should not need to hook up any custom certificate verification file in order to use the application).
On Windows, when I attempt to use the SimpleWeb HTTPS client, connections fail if I have certificate verification switched on, because the certificate for the connection fails to validate. This is not the case on Linux, where verification works fine.
I was advised to follow this solution to import the Windows root certificates into OpenSSL so that they could be used by the verification routines. However, this doesn't seem to make any difference as far as I can see. I have dug into the guts of the libssl verification functions to try and understand exactly what's going on, and although the above answer recommends adding the Windows root certificates to a new X509_STORE, it appears that the SSL connection context has its own store which is set up when the connection is initialised. This makes me think that simply creating a new X509_STORE and adding certificates there is not helping because the connection doesn't actually use that store.
It may well be that I've spent so much time debugging the minutiae of libssl that I'm missing what the actual approach to solving this problem should be. Does OpenSSL provide a canonical way of looking up system certificates that I'm not setting? Alternatively, could the issue be the way that the SimpleWeb library/ASIO is initialising OpenSSL? I know that the library allows you to provide a path for a "verify file" for certificates, but I feel like this wouldn't be an appropriate solution since I as a developer should be using the certificates found on the end user's system, rather than hard-coding my own.
EDIT: For context, this is the code I'm using in a tiny example application:
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
static void LoadSystemCertificates()
{
HCERTSTORE hStore;
PCCERT_CONTEXT pContext = nullptr;
X509 *x509 = nullptr;
X509_STORE *store = X509_STORE_new();
hStore = CertOpenSystemStore(NULL, "ROOT");
if (!hStore)
{
return;
}
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != nullptr)
{
const unsigned char* encodedCert = reinterpret_cast<const unsigned char*>(pContext->pbCertEncoded);
x509 = d2i_X509(nullptr, &encodedCert, pContext->cbCertEncoded);
if (x509)
{
X509_STORE_add_cert(store, x509);
X509_free(x509);
}
}
CertCloseStore(hStore, 0);
}
static void MakeRequest(const std::string& address)
{
using Client = SimpleWeb::Client<SimpleWeb::HTTPS>;
Client httpsClient(address);
httpsClient.io_service = std::make_shared<asio::io_service>();
std::cout << "Making request to: " << address << std::endl;
bool hasResponse = false;
httpsClient.request("GET", [address, &hasResponse](std::shared_ptr<Client::Response> response, const SimpleWeb::error_code& error)
{
hasResponse = true;
if ( error )
{
std::cerr << "Got error from " << address << ": " << error.message() << std::endl;
}
else
{
std::cout << "Got response from " << address << ":\n" << response->content.string() << std::endl;
}
});
while ( !hasResponse )
{
httpsClient.io_service->poll();
httpsClient.io_service->reset();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
int main(int, char**)
{
LoadSystemCertificates();
MakeRequest("google.co.uk");
return 0;
}
The call returns me: Got error from google.co.uk: certificate verify failed
OK, to anyone who this might help in future, this is how I solved this issue. This answer to a related question helped.
It turns out that the issue was indeed that the SSL context was not making use of the certificate store that I'd set up. Everything else was OK, bu the missing piece of the puzzle was a call to SSL_CTX_set_cert_store(), which takes the certificate store and provides it to the SSL context.
In the context of the SimpleWeb library, the easiest way to do this appeared to be to subclass the SimpleWeb::Client<SimpleWeb::HTTPS> class and add the following to the constructor:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <wincrypt.h>
class MyClient : public SimpleWeb::Client<SimpleWeb::HTTPS>
{
public:
MyClient( /* ... */ ) :
SimpleWeb::Client<SimpleWeb::HTTPS>( /* ... */ )
{
AddWindowsRootCertificates();
}
private:
using OpenSSLContext = asio::ssl::context::native_handle_type;
void AddWindowsRootCertificates()
{
// Get the SSL context from the SimpleWeb class.
OpenSSLContext sslContext = context.native_handle();
// Get a certificate store populated with the Windows root certificates.
// If this fails for some reason, the function returns null.
X509_STORE* certStore = GetWindowsCertificateStore();
if ( sslContext && certStore )
{
// Set this store to be used for the SSL context.
SSL_CTX_set_cert_store(sslContext, certStore);
}
}
static X509_STORE* GetWindowsCertificateStore()
{
// To avoid populating the store every time, we keep a static
// pointer to the store and just initialise it the first time
// this function is called.
static X509_STORE* certificateStore = nullptr;
if ( !certificateStore )
{
// Not initialised yet, so do so now.
// Try to open the root certificate store.
HCERTSTORE rootStore = CertOpenSystemStore(0, "ROOT");
if ( rootStore )
{
// The new store is reference counted, so we can create it
// and keep the pointer around for later use.
certificateStore = X509_STORE_new();
PCCERT_CONTEXT pContext = nullptr;
while ( (pContext = CertEnumCertificatesInStore(rootStore, pContext)) != nullptr )
{
// d2i_X509() may modify the pointer, so make a local copy.
const unsigned char* content = pContext->pbCertEncoded;
// Convert the certificate to X509 format.
X509 *x509 = d2i_X509(NULL, &content, pContext->cbCertEncoded);
if ( x509 )
{
// Successful conversion, so add to the store.
X509_STORE_add_cert(certificateStore, x509);
// Release our reference.
X509_free(x509);
}
}
// Make sure to close the store.
CertCloseStore(rootStore, 0);
}
}
return certificateStore;
}
};
Obviously GetWindowsCertificateStore() would need to be abstracted out to somewhere platform-specific if your class needs to compile on multiple platforms.

halt while sending email with Poco Net

I'm trying to send an email using the Poco Net library with this code (my credentials are arbitrary obviously):
void send_email() {
// Poco::Net::SMTPClientSession session("localhost", 465);
const std::string& smtp_host {"mail.example.com"};
const std::string& smtp_user {"marinos#example.com"};
const std::string& smtp_passwd {"myPassword"};
std::string to = "marinos#example.com";
std::string from = "marinos#example.com";
std::string subject = "Your first e-mail message sent using Poco Libraries";
subject = Poco::Net::MailMessage::encodeWord(subject, "UTF-8");
std::string content = "Well done! You've successfully sent your first message using Poco SMTPClientSession";
Poco::Net::MailMessage message;
message.setSender(from);
message.addRecipient(Poco::Net::MailRecipient{Poco::Net::MailRecipient::PRIMARY_RECIPIENT, to});
message.setSubject(subject);
message.setContentType("text/plain; charset=UTF-8");
message.setContent(content, Poco::Net::MailMessage::ENCODING_8BIT);
try {
Poco::Net::SMTPClientSession session(smtp_host, 465);
session.open(); // this is where it halts
try {
session.login(Poco::Net::SMTPClientSession::AUTH_LOGIN, smtp_user, smtp_passwd);
session.sendMessage(message);
std::cout << "Message successfully sent" << std::endl;
session.close();
} catch (Poco::Net::SMTPException& e) {
std::cerr << e.displayText() << std::endl;
session.close();
}
} catch (Poco::Net::NetException& e) {
std::cerr << e.displayText() << std::endl;
}
}
and my program simply halts. I've used a debugger to locate the problem and it seems that the program halts when calling session.open(). Am I doing something wrong here?
Since I do not know specification of your SMTP server, I am speculating that you're probably suppose to use SMTPS instead of plain SMTP, because port 465 is default port for SMTPS (according to Wikipedia). I guess the program halts during SSL handshaking. By looking into Poco documentation I see that there exists SSL version of the Poco::Net::SMTPClientSession. Thus, you should try with SecureSMTPClientSession. Check if your mail server requires START_TLS, in that case I guess you should additionally call bool startTLS().

Remote control of C++ program in Ubuntu 12.04 via HTTPS

I have an emulator program written in C++ running on Ubuntu 12.04. There are some settings and options needed for running the program, which are given by the main's arguments. I need to query and control these options via HTTPS from a remote machine/mobile device. I was wondering if someone can help me with that.
There should probably be some libraries for the ease, for example libcurl. I'm not sure how suitable it is for my case, but here is any example of connection setup in libcurl. It's not a must to use any libraries though; just the most efficient/simplest way.
#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
using namespace curlpp::options;
int main(int, char **)
{
try
{
// That's all that is needed to do cleanup of used resources (RAII style).
curlpp::Cleanup myCleanup;
// Our request to be sent.
curlpp::Easy myRequest;
// Set the URL.
myRequest.setOpt<Url>("http://example.com");
// Send request and get a result.
// By default the result goes to standard output.
myRequest.perform();
}
catch (curlpp::RuntimeError &e)
{
std::cout << e.what() << std::endl;
}
catch (curlpp::LogicError &e)
{
std::cout << e.what() << std::endl;
}
return 0;
}

Facebook chat XMPP authentication with Gloox?

Well, I'm probably doing something silly, but I've been beating my head against the wall with this for the past few hours, and so far I have no idea what I've been doing wrong.
At the moment I'm trying to make this work with PLAIN SASL because it seems like Facebook actively makes OAuth2 a pain for non-Web apps, but it really makes no difference to me as long as I can get this to work somehow.
Current code:
_client = new Client(JID(username /* no #chat.facebook.com */), password);
_client->setServer("chat.facebook.com");
_client->setPort(5222);
_client->setSASLMechanisms(gloox::SaslMechPlain);
_client->setTls(gloox::TLSPolicy::TLSRequired);
_client->connect(false);
_client->login(); // not necessary?
QThread::sleep(10); // arbitrary sleep; should be sufficient
std::cout << _client->authed() << std::endl; // false
std::cout << _client->authError() << std::endl; // AuthErrorUndefined
_client->rosterManager()->fill();
// neither one has any effect
MessageSession(_client, JID("friend#chat.facebook.com")).send("balls");
MessageSession(_client, JID("friend")).send("balls");
std::cout << _client->rosterManager()->roster()->size() << std::endl; // 0
Edit: For that matter, I can't get Gloox working with Gmail either (haven't tried any other XMPP servers).
Your JID is indeed username#chat.facebook.com, not only username - and it is very important to SASL authentication, it will not work with wrong JID.
Facebook chat supports SASL PLAIN authentication over SSL/TLS connection, as well as DIGEST-MD5
Google talk supports SASL PLAIN over TLS too
You can see supported SASL mechanisms in the first <stream:features>...</stream:features> packet from the server
It will be much better if your show error logs
Well, I'm still not 100% sure what the problem with Gloox was, but the following roughly equivalent Swiften code works with no issues.
SimpleEventLoop* eventLoop = new SimpleEventLoop();
BoostNetworkFactories networkFactories(eventLoop);
_client = new Client
(
username.append("#chat.facebook.com").toStdString(),
password.toStdString(),
&networkFactories
);
_client->setAlwaysTrustCertificates();
_client->onConnected.connect([&] () { signInStatus = SignInStatus::Success; });
_client->onDisconnected.connect([&] (const boost::optional<ClientError>& e) {
signInStatus = SignInStatus::InvalidCredentials;
});
_client->connect();
std::thread([&] () { eventLoop->run(); }).detach();
while (signInStatus == SignInStatus::NotSignedIn)
{
QThread::sleep(1);
}
if (signInStatus == SignInStatus::InvalidCredentials)
{
return signInStatus;
}
_client->requestRoster();
QThread::sleep(5);
std::cout << _client->getRoster()->getItems()[0].getName() << std::endl;
Message::ref message(new Message());
message->setTo(_client->getRoster()->getItems()[0].getJID());
message->setFrom(JID());
message->setBody("balls");
_client->sendMessage(message);