Esempio n. 1
0
int main (int argc, char **argv)
{
  try
    {
      MailMessage msg;

      msg.addRecipient (MailRecipient (MailRecipient::PRIMARY_RECIPIENT,
                                       "*****@*****.**",
                                       "Alice Moralis"));
      msg.addRecipient (MailRecipient (MailRecipient::CC_RECIPIENT,
                                       "*****@*****.**",
                                       "Patrick Kilpatrick"));
      msg.addRecipient (MailRecipient (MailRecipient::BCC_RECIPIENT,
                                       "*****@*****.**",
                                       "Michael Carmichael"));

      msg.setSender ("Roy Kilroy <*****@*****.**>");

      msg.setSubject ("Rosetta Code");
      msg.setContent ("Sending mail from C++ using POCO C++ Libraries");

      SMTPClientSession smtp ("mail.example.com"); // SMTP server name
      smtp.login ();
      smtp.sendMessage (msg);
      smtp.close ();
      std::cerr << "Sent mail successfully!" << std::endl;
    }
  catch (std::exception &e)
    {
      std::cerr << "failed to send mail: " << e.what() << std::endl;
      return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}
void SMTPClientSessionTest::testSendFailed()
{
	DialogServer server;
	server.addResponse("220 localhost SMTP ready");
	server.addResponse("250 Hello localhost");
	server.addResponse("250 OK");
	server.addResponse("250 OK");
	server.addResponse("354 Send data");
	server.addResponse("500 Error");
	server.addResponse("221 Bye");
	SMTPClientSession session("localhost", server.port());
	session.login("localhost");

	MailMessage message;
	message.setSender("*****@*****.**");
	message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "Jane Doe"));
	message.setSubject("Test Message");
	message.setContent("Hello\r\nblah blah\r\n\r\nJohn\r\n");
	server.clearCommands();
	try
	{
		session.sendMessage(message);
		fail("internal error - must throw");
	}
	catch (SMTPException&)
	{
	}
	
	session.close();
}
Esempio n. 3
0
void MailMessageTest::testWrite8Bit()
{
	MailMessage message;
	MailRecipient r1(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "John Doe");
	message.addRecipient(r1);
	message.setSubject("Test Message");
	message.setSender("*****@*****.**");
	message.setContent(
		"Hello, world!\r\n"
		"This is a test for the MailMessage class.\r\n",
		MailMessage::ENCODING_8BIT
	);
	Timestamp ts(0);
	message.setDate(ts);
	
	std::ostringstream str;
	message.write(str);
	std::string s = str.str();
	assert (s == 
		"Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n"
		"Content-Type: text/plain\r\n"
		"Subject: Test Message\r\n"
		"From: [email protected]\r\n"
		"Content-Transfer-Encoding: 8bit\r\n"
		"To: John Doe <*****@*****.**>\r\n"
		"\r\n"
		"Hello, world!\r\n"
		"This is a test for the MailMessage class.\r\n"
	);
}
Esempio n. 4
0
void MailMessageTest::testWriteBase64()
{
	MailMessage message;
	MailRecipient r1(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "John Doe");
	message.addRecipient(r1);
	message.setSubject("Test Message");
	message.setSender("*****@*****.**");
	message.setContent(
		"Hello, world!\r\n"
		"This is a test for the MailMessage class.\r\n",
		MailMessage::ENCODING_BASE64
	);
	Timestamp ts(0);
	message.setDate(ts);

	std::ostringstream str;
	message.write(str);
	std::string s = str.str();
	assert (s == 
		"Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n"
		"Content-Type: text/plain\r\n"
		"Subject: Test Message\r\n"
		"From: [email protected]\r\n"
		"Content-Transfer-Encoding: base64\r\n"
		"To: John Doe <*****@*****.**>\r\n"
		"\r\n"
		"SGVsbG8sIHdvcmxkIQ0KVGhpcyBpcyBhIHRlc3QgZm9yIHRoZSBNYWlsTWVzc2FnZSBjbGFz\r\n"
		"cy4NCg=="
	);
}
Esempio n. 5
0
void SendMail::sendMessage(const std::string& content)
{
    try {
        const Poco::Util::AbstractConfiguration* config = Poco::Util::Application::instance().config().createView("ion.mail");
        MailMessage message;
        message.setSender(config->getString("sender"));
        message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, config->getString("recipient")));
        message.setSubject(MailMessage::encodeWord(config->getString("subject"), "UTF-8"));
        message.setContentType("text/plain; charset=UTF-8");
        message.addContent(new Poco::Net::StringPartSource(content));
        Poco::Net::SocketAddress address(config->getString("host"), config->getInt("port"));
        Poco::SharedPtr<Poco::Net::StreamSocket> socket(nullptr);
        if (config->getBool("ssl")) {
            socket = new Poco::Net::SecureStreamSocket(address);
        }
        else {

            socket = new Poco::Net::StreamSocket(address);
        }
        _logger.debug("Connecting to %s", address.toString());
        SMTPClientSession session(*socket);
        session.login(getLoginMethod(config->getString("loginmethod")), config->getString("user"), config->getString("password"));
        session.sendMessage(message);
        session.close();
        _logger.debug("Message sent");
    }
    catch (Poco::Exception& ex) {
        _logger.error(ex.displayText());
        throw;
    }
}
Esempio n. 6
0
void MailMessageTest::testWriteMultiPart()
{
	MailMessage message;
	MailRecipient r1(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "John Doe");
	message.addRecipient(r1);
	message.setSubject("Test Message");
	message.setSender("*****@*****.**");
	Timestamp ts(0);
	message.setDate(ts);
	message.addContent(new StringPartSource("Hello World!\r\n", "text/plain"), MailMessage::ENCODING_8BIT);
	StringPartSource* pSPS = new StringPartSource("This is some binary data. Really.", "application/octet-stream", "sample.dat");
	pSPS->headers().set("Content-ID", "abcd1234");
	message.addAttachment("sample", pSPS);

	assert (message.isMultipart());

	std::ostringstream str;
	message.write(str);
	std::string s = str.str();
	std::string rawMsg(
		"Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n"
		"Content-Type: multipart/mixed; boundary=$\r\n"
		"Subject: Test Message\r\n"
		"From: [email protected]\r\n"
		"To: John Doe <*****@*****.**>\r\n"
		"Mime-Version: 1.0\r\n"
		"\r\n"
		"--$\r\n"
		"Content-Type: text/plain\r\n"
		"Content-Transfer-Encoding: 8bit\r\n"
		"Content-Disposition: inline\r\n"
		"\r\n"
		"Hello World!\r\n"
		"\r\n"
		"--$\r\n"
		"Content-ID: abcd1234\r\n"
		"Content-Type: application/octet-stream; name=sample\r\n"
		"Content-Transfer-Encoding: base64\r\n"
		"Content-Disposition: attachment; filename=sample.dat\r\n"
		"\r\n"
		"VGhpcyBpcyBzb21lIGJpbmFyeSBkYXRhLiBSZWFsbHku\r\n"
		"--$--\r\n"
	);
	std::string::size_type p1 = s.find('=') + 1;
	std::string::size_type p2 = s.find('\r', p1);
	std::string boundary(s, p1, p2 - p1);
	std::string msg;
	for (std::string::const_iterator it = rawMsg.begin(); it != rawMsg.end(); ++it)
	{
		if (*it == '$')
			msg += boundary;
		else
			msg += *it;
	}

	assert (s == msg);
}
Esempio n. 7
0
void MailMessageTest::testWriteQP()
{
	MailMessage message;
	MailRecipient r1(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "John Doe");
	MailRecipient r2(MailRecipient::CC_RECIPIENT, "*****@*****.**", "Jane Doe");
	MailRecipient r3(MailRecipient::BCC_RECIPIENT, "*****@*****.**", "Frank Foo");
	MailRecipient r4(MailRecipient::BCC_RECIPIENT, "*****@*****.**", "Bernie Bar");
	message.addRecipient(r1);
	message.addRecipient(r2);
	message.addRecipient(r3);
	message.addRecipient(r4);
	message.setSubject("Test Message");
	message.setSender("*****@*****.**");
	message.setContent(
		"Hello, world!\r\n"
		"This is a test for the MailMessage class.\r\n"
		"To test the quoted-printable encoding, we'll put an extra long line here. This should be enough.\r\n"
		"And here is some more =fe.\r\n"
	);
	Timestamp ts(0);
	message.setDate(ts);
	
	assert (!message.isMultipart());
	
	std::ostringstream str;
	message.write(str);
	std::string s = str.str();

	assert (s == 
		"Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n"
		"Content-Type: text/plain\r\n"
		"Subject: Test Message\r\n"
		"From: [email protected]\r\n"
		"Content-Transfer-Encoding: quoted-printable\r\n"
		"To: John Doe <*****@*****.**>\r\n"
		"CC: Jane Doe <*****@*****.**>\r\n"
		"\r\n"
		"Hello, world!\r\n"
		"This is a test for the MailMessage class.\r\n"
		"To test the quoted-printable encoding, we'll put an extra long line here. T=\r\n"
		"his should be enough.\r\n"
		"And here is some more =3Dfe.\r\n"
	);
}
Esempio n. 8
0
void SMTPChannel::log(const Message& msg)
{
	try
	{
		MailMessage message;
		message.setSender(_sender);
		message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, _recipient));
		message.setSubject("Log Message from " + _sender);
		std::stringstream content;
		content << "Log Message\r\n"
			<< "===========\r\n\r\n"
			<< "Host: " << Environment::nodeName() << "\r\n"
			<< "Logger: " << msg.getSource() << "\r\n";

		if (_local)
		{
			DateTime dt(msg.getTime());
			content	<< "Timestamp: " << DateTimeFormatter::format(LocalDateTime(dt), DateTimeFormat::RFC822_FORMAT) << "\r\n";
		}
		else
			content	<< "Timestamp: " << DateTimeFormatter::format(msg.getTime(), DateTimeFormat::RFC822_FORMAT) << "\r\n";

		content	<< "Priority: " << NumberFormatter::format(msg.getPriority()) << "\r\n"
			<< "Process ID: " << NumberFormatter::format(msg.getPid()) << "\r\n"
			<< "Thread: " << msg.getThread() << " (ID: " << msg.getTid() << ")\r\n"
			<< "Message text: " << msg.getText() << "\r\n\r\n";

		message.addContent(new StringPartSource(content.str()));
	
		if (!_attachment.empty())
		{
			{
				Poco::FileInputStream fis(_attachment, std::ios::in | std::ios::binary | std::ios::ate);
				if (fis.good())
				{
					int size = fis.tellg();
					char* pMem = new char [size];
					fis.seekg(std::ios::beg);
					fis.read(pMem, size);
					message.addAttachment(_attachment, new StringPartSource(std::string(pMem, size), _type, _attachment));
					delete [] pMem;
				}
			}
			if (_delete) File(_attachment).remove();
		}

		SMTPClientSession session(_mailHost);
		session.login();
		session.sendMessage(message);
		session.close();
	} 
	catch (Exception&) 
	{ 
		if (_throw) throw; 
	}
}
Esempio n. 9
0
File: Mail.cpp Progetto: 12307/poco
int main(int argc, char** argv)
{
	SSLInitializer sslInitializer;

	if (argc < 4)
	{
		Path p(argv[0]);
		std::cerr << "usage: " << p.getBaseName() << " <mailhost> <sender> <recipient> [<username> <password>]" << std::endl;
		std::cerr << "       Send an email greeting from <sender> to <recipient>," << std::endl;
		std::cerr << "       using a secure connection to the SMTP server at <mailhost>." << std::endl;
		return 1;
	}
	
	std::string mailhost(argv[1]);
	std::string sender(argv[2]);
	std::string recipient(argv[3]);
	std::string username(argc >= 5 ? argv[4] : "");
	std::string password(argc >= 6 ? argv[5] : "");
	
	try
	{
		// Note: we must create the passphrase handler prior Context 
		SharedPtr<InvalidCertificateHandler> pCert = new ConsoleCertificateHandler(false); // ask the user via console
		Context::Ptr pContext = new Context(Context::CLIENT_USE, "", "", "", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
		SSLManager::instance().initializeClient(0, pCert, pContext);

		MailMessage message;
		message.setSender(sender);
		message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, recipient));
		message.setSubject("Hello from the POCO C++ Libraries");
		std::string content;
		content += "Hello ";
		content += recipient;
		content += ",\r\n\r\n";
		content += "This is a greeting from the POCO C++ Libraries.\r\n\r\n";
		std::string logo(reinterpret_cast<const char*>(PocoLogo), sizeof(PocoLogo));
		message.addContent(new StringPartSource(content));
		message.addAttachment("logo", new StringPartSource(logo, "image/gif"));
		
		SecureSMTPClientSession session(mailhost);
		session.login();
		session.startTLS(pContext);
		if (!username.empty())
		{
			session.login(SMTPClientSession::AUTH_LOGIN, username, password);
		}
		session.sendMessage(message);
		session.close();
	}
	catch (Exception& exc)
	{
		std::cerr << exc.displayText() << std::endl;
		return 1;
	}
	return 0;
}
Esempio n. 10
0
void MailMessageTest::testWriteManyRecipients()
{
	MailMessage message;
	MailRecipient r1(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "John Doe");
	MailRecipient r2(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "Jane Doe");
	MailRecipient r3(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "Frank Foo");
	MailRecipient r4(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "Bernie Bar");
	MailRecipient r5(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "Joe Spammer");
	message.addRecipient(r1);
	message.addRecipient(r2);
	message.addRecipient(r3);
	message.addRecipient(r4);
	message.addRecipient(r5);
	message.setSubject("Test Message");
	message.setSender("*****@*****.**");
	message.setContent(
		"Hello, world!\r\n"
		"This is a test for the MailMessage class.\r\n",
		MailMessage::ENCODING_8BIT
	);
	Timestamp ts(0);
	message.setDate(ts);
	
	std::ostringstream str;
	message.write(str);
	std::string s = str.str();
	assert (s == 
		"Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n"
		"Content-Type: text/plain\r\n"
		"Subject: Test Message\r\n"
		"From: [email protected]\r\n"
		"Content-Transfer-Encoding: 8bit\r\n"
		"To: John Doe <*****@*****.**>, Jane Doe <*****@*****.**>, \r\n"
		"\tFrank Foo <*****@*****.**>, Bernie Bar <*****@*****.**>, \r\n"
		"\tJoe Spammer <*****@*****.**>\r\n"
		"\r\n"
		"Hello, world!\r\n"
		"This is a test for the MailMessage class.\r\n"
	);
}
void SMTPClientSessionTest::testSend()
{
	DialogServer server;
	server.addResponse("220 localhost SMTP ready");
	server.addResponse("250 Hello localhost");
	server.addResponse("250 OK");
	server.addResponse("250 OK");
	server.addResponse("354 Send data");
	server.addResponse("250 OK");
	server.addResponse("221 Bye");
	SMTPClientSession session("localhost", server.port());
	session.login("localhost");

	MailMessage message;
	message.setSender("*****@*****.**");
	message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, "*****@*****.**", "Jane Doe"));
	message.setSubject("Test Message");
	message.setContent("Hello\r\nblah blah\r\n\r\nJohn\r\n");
	server.clearCommands();
	session.sendMessage(message);
	std::string cmd = server.popCommandWait();
	assert (cmd == "MAIL FROM: <*****@*****.**>");
	cmd = server.popCommandWait();
	assert (cmd == "RCPT TO: <*****@*****.**>");
	cmd = server.popCommandWait();
	assert (cmd == "DATA");
	cmd = server.popCommandWait();
	assert (cmd.substr(0, 4) == "Date");
	cmd = server.popCommandWait();
	assert (cmd == "Content-Type: text/plain");
	cmd = server.popCommandWait();
	assert (cmd == "From: [email protected]");
	cmd = server.popCommandWait();
	assert (cmd == "Subject: Test Message");
	cmd = server.popCommandWait();
	assert (cmd == "Content-Transfer-Encoding: quoted-printable");
	cmd = server.popCommandWait();
	assert (cmd == "To: Jane Doe <*****@*****.**>");
	cmd = server.popCommandWait();
	assert (cmd == "Hello");
	cmd = server.popCommandWait();
	assert (cmd == "blah blah");
	cmd = server.popCommandWait();
	assert (cmd == "John");
	cmd = server.popCommandWait();
	assert (cmd == ".");

	session.close();
}
Esempio n. 12
0
int main(int argc, char** argv)
{
	if (argc != 4)
	{
		Path p(argv[0]);
		std::cerr << "usage: " << p.getBaseName() << " <mailhost> <sender> <recipient>" << std::endl;
		std::cerr << "       Send an email greeting from <sender> to <recipient>," << std::endl;
		std::cerr << "       the SMTP server at <mailhost>." << std::endl;
		return 1;
	}
	
	std::string mailhost(argv[1]);
	std::string sender(argv[2]);
	std::string recipient(argv[3]);
	
	try
	{
		MailMessage message;
		message.setSender(sender);
		message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, recipient));
		message.setSubject("Hello from the POCO C++ Libraries");
		std::string content;
		content += "Hello ";
		content += recipient;
		content += ",\r\n\r\n";
		content += "This is a greeting from the POCO C++ Libraries.\r\n\r\n";
		std::string logo(reinterpret_cast<const char*>(PocoLogo), sizeof(PocoLogo));
		message.addContent(new StringPartSource(content));
		message.addAttachment("logo", new StringPartSource(logo, "image/gif"));
		
		SMTPClientSession session(mailhost);
		session.login();
		session.sendMessage(message);
		session.close();
	}
	catch (Exception& exc)
	{
		std::cerr << exc.displayText() << std::endl;
		return 1;
	}
	return 0;
}
Esempio n. 13
0
void Test_MailMessage()
{
    std::string sender("*****@*****.**");
    std::string recipient("*****@*****.**");
    std::string subject("Generated email");
    std::string content = "Hi ";
    content += recipient;
    content += ",\n\n";
    content += "Have a good day!\n\n";
    content += "Regards,\n";
    content += "A-Team";
    std::string attachment("C:\\DSC.jpg");

    MailMessage message;
    message.setSender( sender );
    message.addRecipient( MailRecipient(MailRecipient::PRIMARY_RECIPIENT, recipient) );
    message.setSubject( subject );
    message.setContent( content );
    message.addAttachment( attachment );

    send(message);
}
Esempio n. 14
0
int main(int argc, char* argv[])
{
	// Check the required number of command line arguments.
	if (argc != 5)
	{
		cout << "usr host user pwd jobs_path" << endl;
		return 0;
	}

	// Fetch command line arguments.
	const auto host = argv[1];
	const auto user = argv[2];
	const auto pwd = argv[3];
	const path jobs_path = argv[4];

	// Connect to host and authenticate user.
	DBClientConnection conn;
	{
		cout << local_time() << "Connecting to " << host << " and authenticating " << user << endl;
		string errmsg;
		if ((!conn.connect(host, errmsg)) || (!conn.auth("istar", user, pwd, errmsg)))
		{
			cerr << local_time() << errmsg << endl;
			return 1;
		}
	}

	// Initialize constants.
	cout << local_time() << "Initializing" << endl;
	const auto collection = "istar.usr";
	const auto epoch = date(1970, 1, 1);
	const size_t num_usrs = 2;
	constexpr array<size_t, num_usrs> qn{{ 12, 60 }};
	constexpr array<double, num_usrs> qv{{ 1.0 / qn[0], 1.0 / qn[1] }};
	const size_t num_references = 4;
	const size_t num_subsets = 5;
	const array<string, num_subsets> SubsetSMARTS
	{{
		"[!#1]", // heavy
		"[#6+0!$(*~[#7,#8,F]),SH0+0v2,s+0,S^3,Cl+0,Br+0,I+0]", // hydrophobic
		"[a]", // aromatic
		"[$([O,S;H1;v2]-[!$(*=[O,N,P,S])]),$([O,S;H0;v2]),$([O,S;-]),$([N&v3;H1,H2]-[!$(*=[O,N,P,S])]),$([N;v3;H0]),$([n,o,s;+0]),F]", // acceptor
		"[N!H0v3,N!H0+v4,OH+0,SH+0,nH+0]", // donor
	}};

	// Initialize variables.
	array<array<double, qn.back()>, 1> qw;
	array<array<double, qn.back()>, 1> lw;
	auto q = qw[0];
	auto l = lw[0];

	// Read ZINC ID file.
	const string_array<size_t> zincids("16_zincid.txt");
	const auto num_ligands = zincids.size();

	// Read SMILES file.
	const string_array<size_t> smileses("16_smiles.txt");
	assert(smileses.size() == num_ligands);

	// Read supplier file.
	const string_array<size_t> suppliers("16_supplier.txt");
	assert(suppliers.size() == num_ligands);

	// Read property files of floating point types and integer types.
	const auto zfproperties = read<array<float, 4>>("16_zfprop.f32");
	assert(zfproperties.size() == num_ligands);
	const auto ziproperties = read<array<int16_t, 5>>("16_ziprop.i16");
	assert(ziproperties.size() == num_ligands);

	// Open files for subsequent reading.
	std::ifstream usrcat_bin("16_usrcat.f64");
	stream_array<size_t> ligands("16_ligand.pdbqt");
	assert(ligands.size() == num_ligands);
	array<vector<double>, 2> scores
	{{
		vector<double>(num_ligands, 0),
		vector<double>(num_ligands, 0)
	}};
	const auto& u0scores = scores[0];
	const auto& u1scores = scores[1];
	vector<size_t> scase(num_ligands);

	// Enter event loop.
	cout << local_time() << "Entering event loop" << endl;
	bool sleeping = false;
	while (true)
	{
		// Fetch an incompleted job in a first-come-first-served manner.
		if (!sleeping) cout << local_time() << "Fetching an incompleted job" << endl;
		BSONObj info;
		conn.runCommand("istar", BSON("findandmodify" << "usr" << "query" << BSON("done" << BSON("$exists" << false) << "started" << BSON("$exists" << false)) << "sort" << BSON("submitted" << 1) << "update" << BSON("$set" << BSON("started" << Date_t(duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count())))), info); // conn.findAndModify() is available since MongoDB C++ Driver legacy-1.0.0
		const auto value = info["value"];
		if (value.isNull())
		{
			// No incompleted jobs. Sleep for a while.
			if (!sleeping) cout << local_time() << "Sleeping" << endl;
			sleeping = true;
			this_thread::sleep_for(chrono::seconds(10));
			continue;
		}
		sleeping = false;
		const auto job = value.Obj();

		// Obtain job properties.
		const auto _id = job["_id"].OID();
		cout << local_time() << "Executing job " << _id.str() << endl;
		const auto job_path = jobs_path / _id.str();
		const auto format = job["format"].String();
		const auto email = job["email"].String();

		// Parse the user-supplied ligand.
		OBMol obMol;
		OBConversion obConversion;
		obConversion.SetInFormat(format.c_str());
		obConversion.ReadFile(&obMol, (job_path / ("ligand." + format)).string());
		const auto num_atoms = obMol.NumAtoms();
//		obMol.AddHydrogens(); // Adding hydrogens does not seem to affect SMARTS matching.

		// Classify subset atoms.
		array<vector<int>, num_subsets> subsets;
		for (size_t k = 0; k < num_subsets; ++k)
		{
			auto& subset = subsets[k];
			subset.reserve(num_atoms);
			OBSmartsPattern smarts;
			smarts.Init(SubsetSMARTS[k]);
			smarts.Match(obMol);
			for (const auto& map : smarts.GetMapList())
			{
				subset.push_back(map.front());
			}
		}
		const auto& subset0 = subsets.front();

		// Check user-provided ligand validity.
		if (subset0.empty())
		{
			// Record job completion time stamp.
			const auto millis_since_epoch = duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count();
			conn.update(collection, BSON("_id" << _id), BSON("$set" << BSON("done" << Date_t(millis_since_epoch))));

			// Send error notification email.
			cout << local_time() << "Sending an error notification email to " << email << endl;
			MailMessage message;
			message.setSender("usr <*****@*****.**>");
			message.setSubject("Your usr job has failed");
			message.setContent("Description: " + job["description"].String() + "\nSubmitted: " + to_simple_string(ptime(epoch, boost::posix_time::milliseconds(job["submitted"].Date().millis))) + " UTC\nFailed: " + to_simple_string(ptime(epoch, boost::posix_time::milliseconds(millis_since_epoch))) + " UTC\nReason: failed to parse the provided ligand.");
			message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, email));
			SMTPClientSession session("137.189.91.190");
			session.login();
			session.sendMessage(message);
			session.close();
			continue;
		}

		// Calculate the four reference points.
		const auto n = subset0.size();
		const auto v = 1.0 / n;
		array<vector3, num_references> references{};
		auto& ctd = references[0];
		auto& cst = references[1];
		auto& fct = references[2];
		auto& ftf = references[3];
		for (const auto i : subset0)
		{
			ctd += obMol.GetAtom(i)->GetVector();
		}
		ctd *= v;
		double cst_dist = numeric_limits<double>::max();
		double fct_dist = numeric_limits<double>::lowest();
		double ftf_dist = numeric_limits<double>::lowest();
		for (const auto i : subset0)
		{
			const auto& a = obMol.GetAtom(i)->GetVector();
			const auto this_dist = a.distSq(ctd);
			if (this_dist < cst_dist)
			{
				cst = a;
				cst_dist = this_dist;
			}
			if (this_dist > fct_dist)
			{
				fct = a;
				fct_dist = this_dist;
			}
		}
		for (const auto i : subset0)
		{
			const auto& a = obMol.GetAtom(i)->GetVector();
			const auto this_dist = a.distSq(fct);
			if (this_dist > ftf_dist)
			{
				ftf = a;
				ftf_dist = this_dist;
			}
		}

		// Precalculate the distances between each atom and each reference point.
		array<vector<double>, num_references> dista;
		for (size_t k = 0; k < num_references; ++k)
		{
			const auto& reference = references[k];
			auto& dists = dista[k];
			dists.resize(1 + num_atoms); // OpenBabel atom index starts from 1. dists[0] is dummy.
			for (size_t i = 0; i < n; ++i)
			{
				dists[subset0[i]] = sqrt(obMol.GetAtom(subset0[i])->GetVector().distSq(reference));
			}
		}

		// Calculate USR and USRCAT features of the input ligand.
		size_t qo = 0;
		for (const auto& subset : subsets)
		{
			const auto n = subset.size();
			for (size_t k = 0; k < num_references; ++k)
			{
				const auto& distp = dista[k];
				vector<double> dists(n);
				for (size_t i = 0; i < n; ++i)
				{
					dists[i] = distp[subset[i]];
				}
				array<double, 3> m{};
				if (n > 2)
				{
					const auto v = 1.0 / n;
					for (size_t i = 0; i < n; ++i)
					{
						const auto d = dists[i];
						m[0] += d;
					}
					m[0] *= v;
					for (size_t i = 0; i < n; ++i)
					{
						const auto d = dists[i] - m[0];
						m[1] += d * d;
					}
					m[1] = sqrt(m[1] * v);
					for (size_t i = 0; i < n; ++i)
					{
						const auto d = dists[i] - m[0];
						m[2] += d * d * d;
					}
					m[2] = cbrt(m[2] * v);
				}
				else if (n == 2)
				{
					m[0] = 0.5 *     (dists[0] + dists[1]);
					m[1] = 0.5 * fabs(dists[0] - dists[1]);
				}
				else if (n == 1)
				{
					m[0] = dists[0];
				}
				#pragma unroll
				for (const auto e : m)
				{
					q[qo++] = e;
				}
			}
		}
		assert(qo == qn.back());

		// Compute USR and USRCAT scores.
		usrcat_bin.seekg(0);
		for (size_t k = 0; k < num_ligands; ++k)
		{
			usrcat_bin.read(reinterpret_cast<char*>(l.data()), sizeof(l));
			double s = 0;
			#pragma unroll
			for (size_t i = 0, u = 0; u < num_usrs; ++u)
			{
				#pragma unroll
				for (const auto qnu = qn[u]; i < qnu; ++i)
				{
					s += fabs(q[i] - l[i]);
				}
				scores[u][k] = s;
			}
		}
		assert(usrcat_bin.tellg() == sizeof(l) * num_ligands);

		// Sort ligands by USRCAT score and then by USR score and then by ZINC ID.
		iota(scase.begin(), scase.end(), 0);
		sort(scase.begin(), scase.end(), [&](const size_t val0, const size_t val1)
		{
			const auto u1score0 = u1scores[val0];
			const auto u1score1 = u1scores[val1];
			if (u1score0 == u1score1)
			{
				const auto u0score0 = u0scores[val0];
				const auto u0score1 = u0scores[val1];
				if (u0score0 == u0score1)
				{
					return zincids[val0] < zincids[val1];
				}
				return u0score0 < u0score1;
			}
			return u1score0 < u1score1;
		});

		// Write results.
		filtering_ostream log_csv_gz;
		log_csv_gz.push(gzip_compressor());
		log_csv_gz.push(file_sink((job_path / "log.csv.gz").string()));
		log_csv_gz.setf(ios::fixed, ios::floatfield);
		log_csv_gz << "ZINC ID,USR score,USRCAT score\n" << setprecision(8);
		filtering_ostream ligands_pdbqt_gz;
		ligands_pdbqt_gz.push(gzip_compressor());
		ligands_pdbqt_gz.push(file_sink((job_path / "ligands.pdbqt.gz").string()));
		ligands_pdbqt_gz.setf(ios::fixed, ios::floatfield);
		for (size_t t = 0; t < 10000; ++t)
		{
			const size_t k = scase[t];
			const auto zincid = zincids[k].substr(0, 8); // Take another substr() to get rid of the trailing newline.
			const auto u0score = 1 / (1 + scores[0][k] * qv[0]);
			const auto u1score = 1 / (1 + scores[1][k] * qv[1]);
			log_csv_gz << zincid << ',' << u0score << ',' << u1score << '\n';

			// Only write conformations of the top ligands to ligands.pdbqt.gz.
			if (t >= 1000) continue;

			const auto zfp = zfproperties[k];
			const auto zip = ziproperties[k];
			ligands_pdbqt_gz
				<< "MODEL " << '\n'
				<< "REMARK 911 " << zincid
				<< setprecision(3)
				<< ' ' << setw(8) << zfp[0]
				<< ' ' << setw(8) << zfp[1]
				<< ' ' << setw(8) << zfp[2]
				<< ' ' << setw(8) << zfp[3]
				<< ' ' << setw(3) << zip[0]
				<< ' ' << setw(3) << zip[1]
				<< ' ' << setw(3) << zip[2]
				<< ' ' << setw(3) << zip[3]
				<< ' ' << setw(3) << zip[4]
				<< '\n'
				<< "REMARK 912 " << smileses[k]  // A newline is already included in smileses[k].
				<< "REMARK 913 " << suppliers[k] // A newline is already included in suppliers[k].
				<< setprecision(8)
				<< "REMARK 951    USR SCORE: " << setw(10) << u0score << '\n'
				<< "REMARK 952 USRCAT SCORE: " << setw(10) << u1score << '\n'
			;
			const auto lig = ligands[k];
			ligands_pdbqt_gz.write(lig.data(), lig.size());
			ligands_pdbqt_gz << "ENDMDL\n";
		}

		// Update progress.
		cout << local_time() << "Setting done time" << endl;
		const auto millis_since_epoch = duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count();
		conn.update(collection, BSON("_id" << _id), BSON("$set" << BSON("done" << Date_t(millis_since_epoch))));

		// Send completion notification email.
		cout << local_time() << "Sending a completion notification email to " << email << endl;
		MailMessage message;
		message.setSender("istar <*****@*****.**>");
		message.setSubject("Your usr job has completed");
		message.setContent("Description: " + job["description"].String() + "\nSubmitted: " + to_simple_string(ptime(epoch, boost::posix_time::milliseconds(job["submitted"].Date().millis))) + " UTC\nCompleted: " + to_simple_string(ptime(epoch, boost::posix_time::milliseconds(millis_since_epoch))) + " UTC\nResult: http://istar.cse.cuhk.edu.hk/usr/iview/?" + _id.str());
		message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, email));
		SMTPClientSession session("137.189.91.190");
		session.login();
		session.sendMessage(message);
		session.close();
	}
}