/*    Copyright 2012 10gen Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#include "mongo/platform/basic.h"

#include "mongo/client/sasl_client_authenticate.h"

#include <string>

#include "mongo/base/string_data.h"
#include "mongo/bson/util/bson_extract.h"
#include "mongo/util/base64.h"
#include "mongo/util/mongoutils/str.h"

namespace mongo {

using namespace mongoutils;

Status (*saslClientAuthenticate)(DBClientWithCommands* client,
                                 const BSONObj& saslParameters) = NULL;

const char* const saslStartCommandName = "saslStart";
const char* const saslContinueCommandName = "saslContinue";
const char* const saslCommandAutoAuthorizeFieldName = "autoAuthorize";
const char* const saslCommandCodeFieldName = "code";
const char* const saslCommandConversationIdFieldName = "conversationId";
const char* const saslCommandDoneFieldName = "done";
const char* const saslCommandErrmsgFieldName = "errmsg";
const char* const saslCommandMechanismFieldName = "mechanism";
const char* const saslCommandMechanismListFieldName = "supportedMechanisms";
const char* const saslCommandPasswordFieldName = "pwd";
const char* const saslCommandPayloadFieldName = "payload";
const char* const saslCommandUserDBFieldName = "db";
const char* const saslCommandUserFieldName = "user";
const char* const saslCommandServiceHostnameFieldName = "serviceHostname";
const char* const saslCommandServiceNameFieldName = "serviceName";
const char* const saslCommandDigestPasswordFieldName = "digestPassword";
const char* const saslDefaultDBName = "$external";
const char* const saslDefaultServiceName = "mongodb";

Status saslExtractPayload(const BSONObj& cmdObj, std::string* payload, BSONType* type) {
    BSONElement payloadElement;
    Status status = bsonExtractField(cmdObj, saslCommandPayloadFieldName, &payloadElement);
    if (!status.isOK())
        return status;

    *type = payloadElement.type();
    if (payloadElement.type() == BinData) {
        const char* payloadData;
        int payloadLen;
        payloadData = payloadElement.binData(payloadLen);
        if (payloadLen < 0)
            return Status(ErrorCodes::InvalidLength, "Negative payload length");
        *payload = std::string(payloadData, payloadData + payloadLen);
    } else if (payloadElement.type() == String) {
        try {
            *payload = base64::decode(payloadElement.str());
        } catch (UserException& e) {
            return Status(ErrorCodes::FailedToParse, e.what());
        }
    } else {
        return Status(ErrorCodes::TypeMismatch,
                      (str::stream() << "Wrong type for field; expected BinData or String for "
                                     << payloadElement));
    }

    return Status::OK();
}
}  // namespace mongo
