+ result = sasl_client_new("pop", host, NULL, NULL, NULL, 0, &conn);
+
+ if (result != SASL_OK) {
+ snprintf(response, sizeof(response), "SASL client initialization "
+ "failed: %s", sasl_errstring(result, NULL, NULL));
+ return NOTOK;
+ }
+
+ /*
+ * Initialize the security properties
+ */
+
+ memset(&secprops, 0, sizeof(secprops));
+ secprops.maxbufsize = SASL_BUFFER_SIZE;
+ secprops.max_ssf = UINT_MAX;
+
+ result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
+
+ if (result != SASL_OK) {
+ snprintf(response, sizeof(response), "SASL security property "
+ "initialization failed: %s", sasl_errdetail(conn));
+ return NOTOK;
+ }
+
+ /*
+ * Start the actual protocol. Feed the mech list into the library
+ * and get out a possible initial challenge
+ */
+
+ result = sasl_client_start(conn,
+ (const char *) (mech ? mech : server_mechs),
+ NULL, (const char **) &buf,
+ &buflen, &chosen_mech);
+
+ if (result != SASL_OK && result != SASL_CONTINUE) {
+ snprintf(response, sizeof(response), "SASL client start failed: %s",
+ sasl_errdetail(conn));
+ return NOTOK;
+ }
+
+ if (buflen) {
+ status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
+ if (status != SASL_OK) {
+ snprintf(response, sizeof(response), "SASL base64 encode "
+ "failed: %s", sasl_errstring(status, NULL, NULL));
+ return NOTOK;
+ }
+
+ status = command("AUTH %s %s", chosen_mech, outbuf);
+ } else
+ status = command("AUTH %s", chosen_mech);
+
+ while (result == SASL_CONTINUE) {
+ if (status == NOTOK)
+ return NOTOK;
+
+ /*
+ * If we get a "+OK" prefix to our response, then we should
+ * exit out of this exchange now (because authenticated should
+ * have succeeded)
+ */
+
+ if (strncmp(response, "+OK", 3) == 0)
+ break;
+
+ /*
+ * Otherwise, make sure the server challenge is correctly formatted
+ */
+
+ if (strncmp(response, "+ ", 2) != 0) {
+ command("*");
+ snprintf(response, sizeof(response),
+ "Malformed authentication message from server");
+ return NOTOK;
+ }
+
+ result = sasl_decode64(response + 2, strlen(response + 2),
+ outbuf, sizeof(outbuf), &outlen);
+
+ if (result != SASL_OK) {
+ command("*");
+ snprintf(response, sizeof(response), "SASL base64 decode "
+ "failed: %s", sasl_errstring(result, NULL, NULL));
+ return NOTOK;
+ }
+
+ result = sasl_client_step(conn, outbuf, outlen, NULL,
+ (const char **) &buf, &buflen);
+
+ if (result != SASL_OK && result != SASL_CONTINUE) {
+ command("*");
+ snprintf(response, sizeof(response), "SASL client negotiaton "
+ "failed: %s", sasl_errdetail(conn));
+ return NOTOK;
+ }
+
+ status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
+
+ if (status != SASL_OK) {
+ command("*");
+ snprintf(response, sizeof(response), "SASL base64 encode "
+ "failed: %s", sasl_errstring(status, NULL, NULL));
+ return NOTOK;
+ }
+
+ status = command(outbuf);
+ }
+
+ /*
+ * If we didn't get a positive final response, then error out
+ * (that probably means we failed an authorization check).
+ */
+
+ if (status != OK)
+ return NOTOK;
+
+ /*
+ * We _should_ be okay now. Get a few properties now that negotiation
+ * has completed.
+ */
+
+ result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &moutbuf);
+
+ if (result != SASL_OK) {
+ snprintf(response, sizeof(response), "Cannot retrieve SASL negotiated "
+ "output buffer size: %s", sasl_errdetail(conn));
+ return NOTOK;
+ }
+
+ maxoutbuf = *moutbuf;
+
+ result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
+
+ sasl_ssf = *ssf;
+
+ if (result != SASL_OK) {
+ snprintf(response, sizeof(response), "Cannot retrieve SASL negotiated "
+ "security strength factor: %s", sasl_errdetail(conn));
+ return NOTOK;
+ }
+
+ /*
+ * Limit this to what we can deal with. Shouldn't matter much because
+ * this is only outgoing data (which should be small)
+ */
+
+ if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
+ maxoutbuf = BUFSIZ;
+
+ sasl_complete = 1;
+
+ return status;
+}
+
+/*
+ * Callback to return the userid sent down via the user parameter
+ */
+
+static int
+sasl_get_user(void *context, int id, const char **result, unsigned *len)
+{
+ char *user = (char *) context;
+
+ if (! result || id != SASL_CB_USER)
+ return SASL_BADPARAM;
+
+ *result = user;
+ if (len)
+ *len = strlen(user);
+
+ return SASL_OK;
+}
+
+/*
+ * Callback to return the password (we call ruserpass, which can get it
+ * out of the .netrc
+ */
+
+static int
+sasl_get_pass(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
+{
+ struct pass_context *p_context = (struct pass_context *) context;
+ char *pass = NULL;
+ int len;
+
+ NMH_UNUSED (conn);
+
+ if (! psecret || id != SASL_CB_PASS)
+ return SASL_BADPARAM;
+
+ ruserpass(p_context->user, &(p_context->host), &pass);
+
+ len = strlen(pass);
+
+ *psecret = (sasl_secret_t *) mh_xmalloc(sizeof(sasl_secret_t) + len);
+
+ (*psecret)->len = len;
+ strcpy((char *) (*psecret)->data, pass);
+
+ return SASL_OK;