+/*
+ * Our SASL callback; we are given SASL tokens and then have to format
+ * them according to the protocol requirements, and then process incoming
+ * messages and feed them back into the SASL library.
+ */
+
+static int
+pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
+ unsigned int indatasize, unsigned char **outdata,
+ unsigned int *outdatasize, char **errstr)
+{
+ int rc;
+ char *mech, *line;
+ size_t len, b64len;
+
+ switch (mtype) {
+ case NETSEC_SASL_START:
+ /*
+ * Generate our AUTH message, but there is a wrinkle.
+ *
+ * Technically, according to RFC 5034, if your command INCLUDING
+ * an initial response exceeds 255 octets (including CRLF), you
+ * can't issue this all in one go, but have to just issue the
+ * AUTH command, wait for a blank initial response, and then
+ * send your data.
+ */
+
+ mech = netsec_get_sasl_mechanism(nsc);
+
+ if (indatasize) {
+ char *b64data;
+ b64data = mh_xmalloc(BASE64SIZE(indatasize));
+ writeBase64raw(indata, indatasize, line);
+ b64len = strlen(b64data);
+
+ /* Formula here is AUTH + SP + mech + SP + out + CR + LF */
+ len = b64len + 8 + strlen(mech);
+ if (len > 255) {
+ rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech);
+ if (rc)
+ return NOTOK;
+ if (netsec_flush(nsc, errstr) != OK)
+ return NOTOK;
+ line = netsec_readline(nsc, &len, errstr);
+ if (! line)
+ return NOTOK;
+ /*
+ * If the protocol is being followed correctly, should just
+ * be a "+ ", nothing else.
+ */
+ if (len != 2 || strcmp(line, "+ ") != 0) {
+ netsec_err(errstr, "Did not get expected blank response "
+ "for initial challenge response");
+ return NOTOK;
+ }
+ rc = netsec_printf(nsc, errstr, "%s\r\n", b64data);
+ free(b64data);
+ if (rc != OK)
+ return NOTOK;
+ if (netsec_flush(nsc, errstr) != OK)
+ return NOTOK;
+ } else {
+ rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech,
+ b64data);
+ free(b64data);
+ if (rc != OK)
+ return NOTOK;
+ if (netsec_flush(nsc, errstr) != OK)
+ return NOTOK;
+ }
+ } else {
+ if (netsec_printf(nsc, errstr, "AUTH %s\r\n", mech) != OK)
+ return NOTOK;
+ if (netsec_flush(nsc, errstr) != OK)
+ return NOTOK;
+ }
+
+ break;
+
+ /*
+ * We should get one line back, with our base64 data. Decode that
+ * and feed it back in.
+ */
+ case NETSEC_SASL_READ:
+ line = netsec_readline(nsc, &len, errstr);
+
+ if (line == NULL)
+ return NOTOK;
+ if (len < 2 || (len == 2 && strcmp(line, "+ ") != 0) {
+ netsec_err(errstr, "Invalid format for SASL response");
+ return NOTOK;
+ }
+
+ if (len == 2) {
+ *outdata = NULL;
+ *outdatalen = 0;
+ } else {
+ rc = decodeBase64(line + 2, &outdata, &outdatasize, 0, NULL);
+ if (rc != OK)
+ return NOTOK;
+ }
+ break;
+
+ return OK;
+}
+