/*
   Unix SMB/CIFS implementation.

   Winbind client API

   Copyright (C) 2009,2010 Kai Blin  <kai@samba.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* Required Headers */

#include "replace.h"
#include "libwbclient.h"
#include "../winbind_client.h"
#include "wbc_async.h"

struct wbc_lookup_name_state {
	struct winbindd_request req;
	struct wb_context *wb_ctx;
	struct wbcDomainSid *sid;
	enum wbcSidType name_type;
};

static void wbcLookupName_done(struct tevent_req *subreq);

/**
 * @brief Request a conversion of a domaind and name to a domain sid
 *
 * @param mem_ctx	talloc context to allocate the request from
 * @param ev		tevent context to use for async operation
 * @param wb_ctx	winbind context to use
 * @param *domain	Pointer to the domain to be resolved
 * @param *name		Pointer to the name to be resolved
 *
 * @return tevent_req on success, NULL on error
 **/

struct tevent_req *wbcLookupName_send(TALLOC_CTX *mem_ctx,
				      struct tevent_context *ev,
				      struct wb_context *wb_ctx,
				      const char *domain,
				      const char *name)
{
	struct tevent_req *req, *subreq;
	struct wbc_lookup_name_state *state;

	req = tevent_req_create(mem_ctx, &state, struct wbc_lookup_name_state);
	if (req == NULL) {
		return NULL;
	}

	ZERO_STRUCT(state->req);

	state->req.cmd = WINBINDD_LOOKUPNAME;
	strncpy(state->req.data.name.dom_name, domain,
		sizeof(state->req.data.name.dom_name)-1);
	strncpy(state->req.data.name.name, name,
		sizeof(state->req.data.name.name)-1);
	state->wb_ctx = wb_ctx;


	subreq = wb_trans_send(state, ev, wb_ctx, false, &state->req);
	if (tevent_req_nomem(subreq, req)) {
		return tevent_req_post(req, ev);
	}

	tevent_req_set_callback(subreq, wbcLookupName_done, req);
	return req;
}

static void wbcLookupName_done(struct tevent_req *subreq)
{
	struct tevent_req *req = tevent_req_callback_data(
			subreq, struct tevent_req);
	struct wbc_lookup_name_state *state = tevent_req_data(
			req, struct wbc_lookup_name_state);
	struct winbindd_response *resp;
	wbcErr wbc_status;

	wbc_status = wb_trans_recv(subreq, state, &resp);
	TALLOC_FREE(subreq);
	if (!WBC_ERROR_IS_OK(wbc_status)) {
		tevent_req_error(req, wbc_status);
		return;
	}

	state->sid = talloc(state, struct wbcDomainSid);
	if (tevent_req_nomem(state->sid, req)) {
		return;
	}

	wbc_status = wbcStringToSid(resp->data.sid.sid, state->sid);
	if (!WBC_ERROR_IS_OK(wbc_status)) {
		wbcDebug(state->wb_ctx, WBC_DEBUG_ERROR,
			 "wbcStringToSid returned %s!\n",
			 wbcErrorString(wbc_status));
		tevent_req_error(req, wbc_status);
		return;
	}

	state->name_type = (enum wbcSidType)resp->data.sid.type;

	TALLOC_FREE(resp);

	tevent_req_done(req);
}

/**
 * @brief Receive a conversion a SID to a domain and name
 *
 * @param *
 * @param *pname	Resolved User or group name
 * @param *pname_type	Pointer to the resolved SID type
 *
 * @return #wbcErr
 */

wbcErr wbcLookupName_recv(struct tevent_req *req,
			  struct wbcDomainSid *sid,
			  enum wbcSidType *name_type)
{
	struct wbc_lookup_name_state *state = tevent_req_data(
			req, struct wbc_lookup_name_state);
	wbcErr wbc_status = WBC_ERR_SUCCESS;

	if (!sid || !name_type) {
		wbcDebug(state->wb_ctx, WBC_DEBUG_TRACE,
		"Sid is %p, name_type is %p\n", sid, name_type);
		wbc_status = WBC_ERR_INVALID_PARAM;
		goto failed;
	}

	if (tevent_req_is_wbcerr(req, &wbc_status)) {
		goto failed;
	}

	memcpy(sid, state->sid, sizeof(struct wbcDomainSid));
	*name_type = state->name_type;

failed:
	tevent_req_received(req);
	return wbc_status;
}


struct wbc_lookup_sid_state {
	struct winbindd_request req;
	char *domain;
	char *name;
	enum wbcSidType name_type;
};

static void wbcLookupSid_done(struct tevent_req *subreq);

/**
 * @brief Request a conversion of a SID to a domain and name
 *
 * @param mem_ctx	talloc context to allocate the request from
 * @param ev		tevent context to use for async operation
 * @param wb_ctx	winbind context to use
 * @param *sid		Pointer to the domain SID to be resolved
 *
 * @return tevent_req on success, NULL on error
 **/

struct tevent_req *wbcLookupSid_send(TALLOC_CTX *mem_ctx,
				     struct tevent_context *ev,
				     struct wb_context *wb_ctx,
				     const struct wbcDomainSid *sid)
{
	struct tevent_req *req, *subreq;
	struct wbc_lookup_sid_state *state;
	char *sid_string;
	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;

	req = tevent_req_create(mem_ctx, &state, struct wbc_lookup_sid_state);
	if (req == NULL) {
		return NULL;
	}

	ZERO_STRUCT(state->req);

	state->req.cmd = WINBINDD_LOOKUPSID;
	wbc_status = wbcSidToString(sid, &sid_string);
	if (!WBC_ERROR_IS_OK(wbc_status)) {
		tevent_req_error(req, wbc_status);
		return tevent_req_post(req, ev);
	}
	strncpy(state->req.data.sid, sid_string, sizeof(state->req.data.sid)-1);
	wbcFreeMemory(sid_string);

	subreq = wb_trans_send(state, ev, wb_ctx, false, &state->req);
	if (tevent_req_nomem(subreq, req)) {
		return tevent_req_post(req, ev);
	}

	tevent_req_set_callback(subreq, wbcLookupSid_done, req);
	return req;
}

static void wbcLookupSid_done(struct tevent_req *subreq)
{
	struct tevent_req *req = tevent_req_callback_data(
			subreq, struct tevent_req);
	struct wbc_lookup_sid_state *state = tevent_req_data(
			req, struct wbc_lookup_sid_state);
	struct winbindd_response *resp;
	wbcErr wbc_status;

	wbc_status = wb_trans_recv(subreq, state, &resp);
	TALLOC_FREE(subreq);
	if (!WBC_ERROR_IS_OK(wbc_status)) {
		tevent_req_error(req, wbc_status);
		return;
	}
	state->domain = talloc_strdup(state, resp->data.name.dom_name);
	if (tevent_req_nomem(state->domain, req)) {
		return;
	}

	state->name   = talloc_strdup(state, resp->data.name.name);
	if (tevent_req_nomem(state->name, req)) {
		return;
	}

	state->name_type = (enum wbcSidType)resp->data.name.type;

	TALLOC_FREE(resp);

	tevent_req_done(req);
}

/**
 * @brief Receive a conversion a SID to a domain and name
 *
 * @param *mem_ctx,	talloc context to move results to
 * @param *pdomain	Resolved Domain name (possibly "")
 * @param *pname	Resolved User or group name
 * @param *pname_type	Pointer to the resolved SID type
 *
 * @return #wbcErr
 */

wbcErr wbcLookupSid_recv(struct tevent_req *req,
			 TALLOC_CTX *mem_ctx,
			 char **pdomain,
			 char **pname,
			 enum wbcSidType *pname_type)
{
	struct wbc_lookup_sid_state *state = tevent_req_data(
			req, struct wbc_lookup_sid_state);
	wbcErr wbc_status;

	if (tevent_req_is_wbcerr(req, &wbc_status)) {
		tevent_req_received(req);
		return wbc_status;
	}

	if (pdomain != NULL) {
		*pdomain = talloc_steal(mem_ctx, state->domain);
	}

	if (pname != NULL) {
		*pname   = talloc_steal(mem_ctx, state->name);
	}

	if (pname_type != NULL) {
		*pname_type = state->name_type;
	}

	tevent_req_received(req);
	return WBC_ERR_SUCCESS;
}