// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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.

//go:build darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris

// package user re-implements os/user functions without the use of cgo for unix
package user

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"os/user"
	"strconv"
	"strings"
	"syscall"
)

const (
	PASSWD_PATH       = "/etc/passwd"
	CURRENT_ERROR_MSG = "failed to get the current user from the system user database"

	PASSWD_USERNAME_INDEX = 0
	PASSWD_UID_INDEX      = 2
	PASSWD_GID_INDEX      = 3
	PASSWD_GEOCS_INDEX    = 4
	PASSWD_HOME_DIR_INDEX = 5
)

func current() (*user.User, error) {

	// get current user's UID
	uid := syscall.Getuid()

	// open passwd path
	f, err := os.Open(PASSWD_PATH)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("%v - %v", CURRENT_ERROR_MSG, err.Error()))
	}

	defer f.Close()

	// read file by line
	scanner := bufio.NewScanner(f)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		line := scanner.Text()

		// parse user from line string
		user, err := parsePasswdUser(line)
		if err != nil {
			continue
		}

		// return user if UIDs match
		if user.Uid == strconv.Itoa(uid) {
			return user, nil
		}
	}

	return nil, errors.New(CURRENT_ERROR_MSG)
}

func parsePasswdUser(passwdUserStr string) (*user.User, error) {
	// format - username:password:UID:GID:GECOS:home_directory:shell
	parsed_str := strings.Split(passwdUserStr, ":")
	if len(parsed_str) != 7 {
		return nil, errors.New("invalid format to parse User")
	}

	if _, err := strconv.Atoi(parsed_str[PASSWD_UID_INDEX]); err != nil {
		return nil, errors.New("invalid UID to parse User")
	}

	if _, err := strconv.Atoi(parsed_str[PASSWD_GID_INDEX]); err != nil {
		return nil, errors.New("invalid GID to parse User")
	}

	return &user.User{
		Username: parsed_str[PASSWD_USERNAME_INDEX], // username
		Uid:      parsed_str[PASSWD_UID_INDEX],      // UID
		Gid:      parsed_str[PASSWD_GID_INDEX],      // GID
		Name:     parsed_str[PASSWD_GEOCS_INDEX],    // GECOS
		HomeDir:  parsed_str[PASSWD_HOME_DIR_INDEX], // home directory
	}, nil

}
