static int samldb_add(struct ldb_module *module, struct ldb_request *req)
{
	struct ldb_context *ldb;
	struct samldb_ctx *ac;
	struct ldb_message_element *el;
	int ret;
	char *referral = NULL;

	ldb = ldb_module_get_ctx(module);
	ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add\n");

	/* do not manipulate our control entries */
	if (ldb_dn_is_special(req->op.add.message->dn)) {
		return ldb_next_request(module, req);
	}

	referral = refer_if_rodc(ldb, req, req->op.add.message->dn);
	if (referral != NULL) {
		ret = ldb_module_send_referral(req, referral);
		return ret;
	}

	el = ldb_msg_find_element(req->op.add.message, "userParameters");
	if (el != NULL && ldb_req_is_untrusted(req)) {
		const char *reason = "samldb_add: "
			"setting userParameters is not supported over LDAP, "
			"see https://bugzilla.samba.org/show_bug.cgi?id=8077";
		ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
		return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
	}

	ac = samldb_ctx_init(module, req);
	if (ac == NULL) {
		return ldb_operr(ldb);
	}

	/* build the new msg */
	ac->msg = ldb_msg_copy_shallow(ac, req->op.add.message);
	if (ac->msg == NULL) {
		talloc_free(ac);
		ldb_debug(ldb, LDB_DEBUG_FATAL,
			  "samldb_add: ldb_msg_copy_shallow failed!\n");
		return ldb_operr(ldb);
	}

	ret = samldb_check_sensitive_attributes(ac);
	if (ret != LDB_SUCCESS) {
		talloc_free(ac);
		return ret;
	}

	el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
	if (el != NULL) {
		ret = samldb_fsmo_role_owner_check(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
	if ((el != NULL)) {
		/*
		 * We need to check whether the SPN collides with an existing
		 * one (anywhere) including via aliases.
		 */
		ret = samldb_spn_uniqueness_check(ac, el);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	if (samdb_find_attribute(ldb, ac->msg,
				 "objectclass", "user") != NULL) {
		ac->type = SAMLDB_TYPE_USER;

		ret = samldb_prim_group_trigger(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		ret = samldb_objectclass_trigger(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		return samldb_fill_object(ac);
	}

	if (samdb_find_attribute(ldb, ac->msg,
				 "objectclass", "group") != NULL) {
		ac->type = SAMLDB_TYPE_GROUP;

		ret = samldb_objectclass_trigger(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		return samldb_fill_object(ac);
	}

	/* perhaps a foreignSecurityPrincipal? */
	if (samdb_find_attribute(ldb, ac->msg,
				 "objectclass",
				 "foreignSecurityPrincipal") != NULL) {
		return samldb_fill_foreignSecurityPrincipal_object(ac);
	}

	if (samdb_find_attribute(ldb, ac->msg,
				 "objectclass", "classSchema") != NULL) {
		ac->type = SAMLDB_TYPE_CLASS;

		/* If in provision, these checks are too slow to do */
		if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
			ret = samldb_schema_governsid_valid_check(ac);
			if (ret != LDB_SUCCESS) {
				return ret;
			}
		}

		ret = samldb_schema_ldapdisplayname_valid_check(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		ret = samldb_schema_info_update(ac);
		if (ret != LDB_SUCCESS) {
			talloc_free(ac);
			return ret;
		}

		return samldb_fill_object(ac);
	}

	if (samdb_find_attribute(ldb, ac->msg,
				 "objectclass", "attributeSchema") != NULL) {
		ac->type = SAMLDB_TYPE_ATTRIBUTE;

		/* If in provision, these checks are too slow to do */
		if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
			ret = samldb_schema_attributeid_valid_check(ac);
			if (ret != LDB_SUCCESS) {
				return ret;
			}

			ret = samldb_schema_add_handle_linkid(ac);
			if (ret != LDB_SUCCESS) {
				return ret;
			}

			ret = samldb_schema_add_handle_mapiid(ac);
			if (ret != LDB_SUCCESS) {
				return ret;
			}
		}

		ret = samldb_schema_ldapdisplayname_valid_check(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		ret = samldb_schema_info_update(ac);
		if (ret != LDB_SUCCESS) {
			talloc_free(ac);
			return ret;
		}

		return samldb_fill_object(ac);
	}

	if (samdb_find_attribute(ldb, ac->msg,
				 "objectclass", "subnet") != NULL) {
		ret = samldb_verify_subnet(ac, ac->msg->dn);
		if (ret != LDB_SUCCESS) {
			talloc_free(ac);
			return ret;
		}
		/* We are just checking the value is valid, and there are no
		   values to fill in. */
	}

	talloc_free(ac);

	/* nothing matched, go on */
	return ldb_next_request(module, req);
}