+/*
+ * Our callback functions to feed data to the SASL library
+ */
+
+static int
+sm_get_user(void *context, int id, const char **result, unsigned *len)
+{
+ nmh_creds_t creds = (nmh_creds_t) context;
+
+ if (! result || ((id != SASL_CB_USER) && (id != SASL_CB_AUTHNAME)))
+ return SASL_BADPARAM;
+
+ if (creds->user == NULL) {
+ /*
+ * Pass the 1 third argument to nmh_get_credentials() so
+ * that a default user if the -user switch to send(1)/post(8)
+ * wasn't used, and so that a default password will be supplied.
+ * That's used when those values really don't matter, and only
+ * with legacy/.netrc, i.e., with a credentials profile entry.
+ */
+ if (nmh_get_credentials (creds->host, creds->user, 1, creds) != OK) {
+ return SASL_BADPARAM;
+ }
+ }
+
+ *result = creds->user;
+ if (len)
+ *len = strlen(creds->user);
+
+ return SASL_OK;
+}
+
+static int
+sm_get_pass(sasl_conn_t *conn, void *context, int id,
+ sasl_secret_t **psecret)
+{
+ nmh_creds_t creds = (nmh_creds_t) context;
+ int len;
+
+ NMH_UNUSED (conn);
+
+ if (! psecret || id != SASL_CB_PASS)
+ return SASL_BADPARAM;
+
+ if (creds->password == NULL) {
+ /*
+ * Pass the 0 third argument to nmh_get_credentials() so
+ * that the default password isn't used. With legacy/.netrc
+ * credentials support, we'll only get here if the -user
+ * switch to send(1)/post(8) wasn't used.
+ */
+ if (nmh_get_credentials (creds->host, creds->user, 0, creds) != OK) {
+ return SASL_BADPARAM;
+ }
+ }
+
+ len = strlen (creds->password);
+
+ if (! (*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len))) {
+ return SASL_NOMEM;
+ }
+
+ (*psecret)->len = len;
+ strcpy((char *) (*psecret)->data, creds->password);
+
+ return SASL_OK;
+}
+#endif /* CYRUS_SASL */
+
+/* https://developers.google.com/gmail/xoauth2_protocol */
+static int
+sm_auth_xoauth2(const char *user, const char *oauth_svc, int snoop)
+{
+ const char *xoauth_client_res;
+ int status;
+
+#ifdef OAUTH_SUPPORT
+ xoauth_client_res = mh_oauth_do_xoauth(user, oauth_svc,
+ snoop ? stderr : NULL);
+
+ if (xoauth_client_res == NULL) {
+ return sm_ierror("Internal error: mh_oauth_do_xoauth() returned NULL");
+ }
+#else
+ NMH_UNUSED(user);
+ NMH_UNUSED(snoop);
+ adios(NULL, "sendfrom built without OAUTH_SUPPORT, "
+ "so oauth_svc %s is not supported", oauth_svc);
+#endif /* OAUTH_SUPPORT */
+
+ status = smtalk(SM_AUTH, "AUTH XOAUTH2 %s", xoauth_client_res);
+ if (status == 235) {
+ /* It worked! */
+ return RP_OK;
+ }
+
+ /*
+ * Status is 334 and sm_reply.text contains base64-encoded JSON. As far as
+ * epg can tell, no matter the error, the JSON is always the same:
+ * {"status":"400","schemes":"Bearer","scope":"https://mail.google.com/"}
+ * I tried these errors:
+ * - garbage token
+ * - expired token
+ * - wrong scope
+ * - wrong username
+ */
+ /* Then we're supposed to send an empty response ("\r\n"). */
+ smtalk(SM_AUTH, "");
+ /*
+ * And now we always get this, again, no matter the error:
+ * 535-5.7.8 Username and Password not accepted. Learn more at
+ * 535 5.7.8 http://support.google.com/mail/bin/answer.py?answer=14257
+ */
+ return RP_BHST;
+}