Facebook chat XMPP authentication with Gloox? - c++

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);

Related

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.

How can identify when the other party has answered a call with PJSIP?

I am using PJSUA2 with the new c++ high level api.
I want to play a pre-recorded .wav message after the other party has answered the call. So far, PJSIP's onCallState() supports a number of events, limited to the SIP part of the call, which means that after the session part is done (e.g. the other end is ringing), the only other event to recognize is the PJSIP_INV_STATE_DISCONNECTED one.
What I am trying to do, is identify whether the other party has actually answered the call. I thought of trying to peek at the RTP side, but have not found anything so far and the documentation is not really helpful.
Anyone has done this before?
edit:
This is the code when the call is ended.
void MyCall::onCallState(OnCallStateParam& prm) {
PJ_UNUSED_ARG(prm);
CallInfo ci = getInfo();
std::cout << "*** Call: " << ci.remoteUri << " [" << ci.stateText << "]" << std::endl;
if ( ci.state == PJSIP_INV_STATE_DISCONNECTED )
{
//myAcc->removeCall(this);
/* Delete the call */
//delete this;
myAcc->removeCall(this);
delete this;
return;
}
}
The sip part ends when the invite has been sent to the other side.
Check state on equal to PJSIP_INV_STATE_CONNECTING and PJSIP_INV_STATE_CONFIRMED as established call.
https://www.pjsip.org/pjsip/docs/html/group__PJSIP__INV.htm#ga083ffd9c75c406c41f113479cc1ebc1c
And check not in CallState, but in CheckMediaState as when answering an incoming call: https://www.pjsip.org/docs/book-latest/html/call.html#working-with-call-s-audio-media
If connection establishet you get media point. And can play you .wav file:
void MyCall::onCallMediaState(OnCallMediaStateParam &prm)
{
CallInfo ci = getInfo();
// Iterate all the call medias
for (unsigned i = 0; i < ci.media.size(); i++) {
if (ci.media[i].type==PJMEDIA_TYPE_AUDIO && getMedia(i)) {
AudioMedia *aud_med = (AudioMedia *)getMedia(i);
AudioMediaPlayer player;
try {
player.createPlayer("file.wav");
player.startTransmit(*aud_med);
} catch(Error& err) {
}
}
}
}

C++/gRPC - IsCancelled not working properly

I am using gRPC sync api with C++.
Here is how on server side I am checking if the client has stopped the stream.
grpc::Status AuthServer::ConnectServiceImpl::HearthBeat(grpc::ServerContext *context,
grpc::ServerReaderWriter<Pulse, Pulse> *stream) {
Pulse note;
if(ctx_.IsCancelled()){
std::cout << "DISCONNECT" << std::endl;
}
while (stream->Read(&note)) {
Pulse reply;
reply.set_rate(note.rate()+1);
std::cout << "RECEIVED: " << note.rate() << std::endl;
stream->Write(reply);
}
return grpc::Status::OK;
}
This is bidi stream which is stopped forcefully on client side with killing the client app and still the "DISCONNECT" message does not appear.
Why is that, am I using IsCancelled() not correctly?
I think I already answered this in GRPC/C++ - How to detect client disconnected in Async Server.
Your code appears to be checking IsCancelled() on ctx_. I'm not sure what that object is, but the context you want to be checking is the one passed into the request handler method as context.

Google Cloud PubSub Streaming Pull hangs forever

I have this simple code to pull the messages from Google PubSub Subscription:
#include "google/pubsub/v1/pubsub.grpc.pb.h"
#include "google/pubsub/v1/pubsub.pb.h"
#include "grpc++/grpc++.h"
#include "base/logging.h"
int main() {
auto creds = grpc::GoogleDefaultCredentials();
auto stub = std::make_unique<google::pubsub::v1::Subscriber::Stub>(
grpc::CreateChannel("pubsub.googleapis.com", creds));
grpc::ClientContext context;
std::unique_ptr<
grpc::ClientReaderWriter<google::pubsub::v1::StreamingPullRequest,
google::pubsub::v1::StreamingPullResponse>>
stream(stub->StreamingPull(&context));
google::pubsub::v1::StreamingPullRequest request;
request.set_subscription("my_subscription");
request.set_stream_ack_deadline_seconds(10);
stream->Write(request);
google::pubsub::v1::StreamingPullResponse response;
size_t count = 0;
while (stream->Read(&response)) {
google::pubsub::v1::StreamingPullRequest ack_request;
for (const auto& message : response.received_messages()) {
ack_request.add_ack_ids(message.ack_id());
if (++count % 1000 == 0) {
LOG(Info, "count: " << count << " message_size: " << message.message().data().size());
}
}
stream->Write(ack_request);
}
return 0;
}
It turned out that while (stream->Read(&response)) doesn't work forever and stops after ~30 minutes (I don't know why that happens). I tried to wrap the code in while (true) so messages will be pulled in an infinite loop but it turned out that the second iteration can't pull any messages (I see in Google Cloud monitoring that messages are coming).
What is wrong with this code?
I know that GCP didn't implement C++ client yet and StreamingPull is a low level API but I don't wanna wait until they make it (it's unclear when it'll happen) and also don't wanna switch to other language (my application is in C++).
Streaming connections to GCP can be closed for a variety of reasons, e.g. transient network issues or max TTLs on connection lifetimes. A stream should not be expected to be open indefinitely.
When stream->Read(&response) returns false, that's an indication that the stream has been closed [source]. Your code should then recreate the stream to continue pulling messages.

Poco stops after SMTPClientSession.login

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