// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package user import ( "fmt" "syscall" "unsafe" ) func isDomainJoined() (bool, error) { var domain *uint16 var status uint32 err := syscall.NetGetJoinInformation(nil, &domain, &status) if err != nil { return false, err } syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) return status == syscall.NetSetupDomainName, nil } func lookupFullNameDomain(domainAndUser string) (string, error) { return syscall.TranslateAccountName(domainAndUser, syscall.NameSamCompatible, syscall.NameDisplay, 50) } func lookupFullNameServer(servername, username string) (string, error) { s, e := syscall.UTF16PtrFromString(servername) if e != nil { return "", e } u, e := syscall.UTF16PtrFromString(username) if e != nil { return "", e } var p *byte e = syscall.NetUserGetInfo(s, u, 10, &p) if e != nil { return "", e } defer syscall.NetApiBufferFree(p) i := (*syscall.UserInfo10)(unsafe.Pointer(p)) if i.FullName == nil { return "", nil } name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:]) return name, nil } func lookupFullName(domain, username, domainAndUser string) (string, error) { joined, err := isDomainJoined() if err == nil && joined { name, err := lookupFullNameDomain(domainAndUser) if err == nil { return name, nil } } name, err := lookupFullNameServer(domain, username) if err == nil { return name, nil } // domain worked neigher as a domain nor as a server // could be domain server unavailable // pretend username is fullname return username, nil } func newUser(usid *syscall.SID, gid, dir string) (*User, error) { username, domain, t, e := usid.LookupAccount("") if e != nil { return nil, e } if t != syscall.SidTypeUser { return nil, fmt.Errorf("user: should be user account type, not %d", t) } domainAndUser := domain + `\` + username uid, e := usid.String() if e != nil { return nil, e } name, e := lookupFullName(domain, username, domainAndUser) if e != nil { return nil, e } u := &User{ Uid: uid, Gid: gid, Username: domainAndUser, Name: name, HomeDir: dir, } return u, nil } func current() (*User, error) { t, e := syscall.OpenCurrentProcessToken() if e != nil { return nil, e } defer t.Close() u, e := t.GetTokenUser() if e != nil { return nil, e } pg, e := t.GetTokenPrimaryGroup() if e != nil { return nil, e } gid, e := pg.PrimaryGroup.String() if e != nil { return nil, e } dir, e := t.GetUserProfileDirectory() if e != nil { return nil, e } return newUser(u.User.Sid, gid, dir) } // BUG(brainman): Lookup and LookupId functions do not set // Gid and HomeDir fields in the User struct returned on windows. func newUserFromSid(usid *syscall.SID) (*User, error) { // TODO(brainman): do not know where to get gid and dir fields gid := "unknown" dir := "Unknown directory" return newUser(usid, gid, dir) } func lookup(username string) (*User, error) { sid, _, t, e := syscall.LookupSID("", username) if e != nil { return nil, e } if t != syscall.SidTypeUser { return nil, fmt.Errorf("user: should be user account type, not %d", t) } return newUserFromSid(sid) } func lookupId(uid string) (*User, error) { sid, e := syscall.StringToSid(uid) if e != nil { return nil, e } return newUserFromSid(sid) }