All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hcl.domino.jna.internal.NotesNamingUtils Maven / Gradle / Ivy

/*
 * ==========================================================================
 * Copyright (C) 2019-2022 HCL America, Inc. ( http://www.hcl.com/ )
 *                            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.  You may obtain a
 * copy of the License at .
 *
 * 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.
 * ==========================================================================
 */
package com.hcl.domino.jna.internal;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.hcl.domino.DominoException;
import com.hcl.domino.commons.gc.IAPIObject;
import com.hcl.domino.commons.util.NotesErrorUtils;
import com.hcl.domino.commons.util.PlatformUtils;
import com.hcl.domino.commons.util.ReverseStringTokenizer;
import com.hcl.domino.commons.util.StringTokenizerExt;
import com.hcl.domino.commons.util.StringUtil;
import com.hcl.domino.jna.data.JNAUserNamesList;
import com.hcl.domino.jna.internal.capi.NotesCAPI;
import com.hcl.domino.jna.internal.gc.handles.DHANDLE;
import com.hcl.domino.jna.internal.gc.handles.LockUtil;
import com.hcl.domino.misc.NotesConstants;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.ptr.ShortByReference;

/**
 * Notes name utilities to convert between various Name formats and evaluate
 * user groups on a server.
 * 
 * @author Karsten Lehmann
 */
public class NotesNamingUtils {
	private static final int MAX_STRINGCACHE_SIZE = 500;
	
	/**
	 * Names-list-building functions appear to be extrmely thread-sensitive, so this lock is used
	 * around each related C call.
	 */
	private static final Object BUILDNAMESLIST_LOCK = new Object();
	
	private static Map m_nameAbbrCache = Collections.synchronizedMap(new LinkedHashMap(16,0.75f, true) {
		private static final long serialVersionUID = -5818239831757810895L;

		@Override
		protected boolean removeEldestEntry (Map.Entry eldest) {
			if (size() > MAX_STRINGCACHE_SIZE) {
				return true;
			}
			else {
				return false;
			}
		}
	});
	private static Map m_nameCanonicalCache = Collections.synchronizedMap(new LinkedHashMap(16,0.75f, true) {
		private static final long serialVersionUID = -5818239831757810895L;

		@Override
		protected boolean removeEldestEntry (Map.Entry eldest) {
			if (size() > MAX_STRINGCACHE_SIZE) {
				return true;
			}
			else {
				return false;
			}
		}
	});
	
	/**
	 * This function converts a distinguished name in abbreviated format to canonical format.
	 * A fully distinguished name is in canonical format - it contains all possible naming components.
	 * The abbreviated format of a distinguished name removes the labels from the naming components.
	 * 
	 * @param name name to convert
	 * @return canonical name
	 */
	public static String toCanonicalName(String name) {
		return toCanonicalName(name, null);
	}
	
	/**
	 * This function converts a list of distinguished names in abbreviated format to canonical format.
	 * A fully distinguished name is in canonical format - it contains all possible naming components.
	 * The abbreviated format of a distinguished name removes the labels from the naming components.
	 * 
	 * @param names list of names
	 * @return list of names in canonical format
	 */
	public static List toCanonicalNames(Collection names) {
		if (names==null) {
			return null;
		}
		
		List namesCanonical = new ArrayList<>(names.size());
		for (String currName : names) {
			namesCanonical.add(toCanonicalName(currName));
		}
		return namesCanonical;
	}
	
	/**
	 * This function converts a distinguished name in abbreviated format to canonical format.
	 * A fully distinguished name is in canonical format - it contains all possible naming components.
	 * The abbreviated format of a distinguished name removes the labels from the naming components.
	 * 
	 * @param name name to convert
	 * @param templateName name to be used when the input name is in common name format
	 * @return canonical name
	 */
	public static String toCanonicalName(String name, String templateName) {
		if (name==null) {
			return null;
		}
		if (name.length()==0) {
			return name;
		}

		String cacheKey = name + ((templateName!=null && templateName.length()>0) ? ("|" + templateName) : ""); //$NON-NLS-1$ //$NON-NLS-2$
		String abbrName = m_nameCanonicalCache.get(cacheKey);
		if (abbrName!=null) {
			return abbrName;
		}

		Memory templateNameMem = templateName==null ? null : NotesStringUtils.toLMBCS(templateName, true); //used when abbrName is only a common name
		Memory inNameMem = NotesStringUtils.toLMBCS(name, true);
		try(DisposableMemory outNameMem = new DisposableMemory(NotesConstants.MAXUSERNAME)) {
  		ShortByReference outLength = new ShortByReference();
  		
  		short result = NotesCAPI.get().DNCanonicalize(0, templateNameMem, inNameMem, outNameMem, NotesConstants.MAXUSERNAME, outLength);
  		NotesErrorUtils.checkResult(result);
  		
  		String sOutName = NotesStringUtils.fromLMBCS(outNameMem, outLength.getValue() & 0xffff);
      
      m_nameCanonicalCache.put(cacheKey, sOutName);
      
      return sOutName;
		}
	}
	
	/**
	 * This function converts a distinguished name in canonical format to abbreviated format.
	 * A fully distinguished name is in canonical format - it contains all possible naming components.
	 * The abbreviated format of a distinguished name removes the labels from the naming components.
	 * 
	 * @param name name to convert
	 * @return abbreviated name
	 */
	public static String toAbbreviatedName(String name) {
		if (name==null) {
			return null;
		}
		if (name.length()==0) {
			return name;
		}
		
		final String cacheKey = name;
		String abbrName = m_nameAbbrCache.get(cacheKey);
		
		if (abbrName==null) {
			StringTokenizerExt st=new StringTokenizerExt(name, "/"); //$NON-NLS-1$
			StringBuilder sb=new StringBuilder(name.length());
			while (st.hasMoreTokens()) {
				String currToken=st.nextToken();
				int iPos = currToken.indexOf("="); //$NON-NLS-1$
				if (sb.length()>0) {
					sb.append("/"); //$NON-NLS-1$
				}
				
				if (iPos!=-1) {
					sb.append(currToken.substring(iPos+1));
				}
				else {
					sb.append(currToken);
				}
			}
			
			abbrName = sb.toString();
			m_nameAbbrCache.put(cacheKey, abbrName);
		}

		return abbrName;
	}

	/**
	 * This function converts a list of distinguished names in canonical format to abbreviated format.
	 * A fully distinguished name is in canonical format - it contains all possible naming components.
	 * The abbreviated format of a distinguished name removes the labels from the naming components.
	 * 
	 * @param names list of names to convert
	 * @return list of abbreviated names
	 */
	public static List toAbbreviatedNames(Collection names) {
		if (names==null) {
			return null;
		}
		
		List namesAbbr = new ArrayList<>(names.size());
		for (String currName : names) {
			namesAbbr.add(toAbbreviatedName(currName));
		}
		return namesAbbr;
	}
	
	/**
	 * Method to compare two Notes names. We compare the abbreviated forms of both names
	 * ignoring the case
	 * 
	 * @param p_sNotesName1 Notes name 1
	 * @param p_sNotesName2 Notes name 2
	 * @return true if equal
	 */
	public static boolean equalNames(String p_sNotesName1, String p_sNotesName2) {
		String sNotesName1Abbr = toAbbreviatedName(p_sNotesName1);
		String sNotesName2Abbr = toAbbreviatedName(p_sNotesName2);
		
		if (sNotesName1Abbr==null) {
			return sNotesName2Abbr==null;
		}
		else {
			return sNotesName1Abbr.equalsIgnoreCase(sNotesName2Abbr);
		}
	}

	/**
	 * Extracts the common name part of an abbreviated or canonical name
	 * 
	 * @param name abbreviated or canonical name
	 * @return common name
	 */
	public static String toCommonName(String name) {
		int iPos = name.indexOf('/');
		String firstPart = iPos==-1 ? name : name.substring(0, iPos);
		if (StringUtil.startsWithIgnoreCase(firstPart, "cn=")) { //$NON-NLS-1$
			return firstPart.substring(3);
		}
		else {
			return firstPart;
		}
	}
	
	/**
	 * Extracts the common name part of a list of abbreviated or canonical names
	 * 
	 * @param names list of abbreviated or canonical name
	 * @return list of common names
	 */
	public static List toCommonNames(Collection names) {
		if (names==null) {
			return null;
		}
		
		List namesAbbr = new ArrayList<>(names.size());
		for (String currName : names) {
			namesAbbr.add(toCommonName(currName));
		}
		return namesAbbr;
	}
	
	/**
	 * Checks whether a Notes name matches a wildcard string, e.g. "Karsten Lehmann / Mindoo" would match
	 * "* / Mindoo".
	 * 
	 * @param name notes name (abbreviated or canonical)
	 * @param wildcard (abbreviated or canonical)
	 * @return true if match
	 */
	public static boolean nameMatchesWildcard(String name, String wildcard) {
		if ("*".equals(wildcard)) { //$NON-NLS-1$
			return true;
		}
		
		String nameAbbr = toAbbreviatedName(name);
		String wildcardAbbr = toAbbreviatedName(wildcard);
		
		ReverseStringTokenizer nameSt = new ReverseStringTokenizer(nameAbbr, "/"); //$NON-NLS-1$
		ReverseStringTokenizer wildcardSt = new ReverseStringTokenizer(wildcardAbbr, "/"); //$NON-NLS-1$
		
		while (nameSt.hasMoreTokens()) {
			String currNameToken = nameSt.nextToken();
			
			if (!wildcardSt.hasMoreTokens()) {
				return false;
			}
			else {
				String currWildcardToken = wildcardSt.nextToken();
				if ("*".equals(currWildcardToken)) { //$NON-NLS-1$
					if (wildcardSt.hasMoreTokens()) {
						throw new IllegalArgumentException("The wildcard * can only be the leftmost part of the wildcard pattern");
					}
					
					return true;
				}
				else if (!currNameToken.equalsIgnoreCase(currWildcardToken)) {
					return false;
				}
			}
		}
		
		return true;
	}
	
	/**
	 * This function converts a distinguished name in canonical format to abbreviated format.
	 * A fully distinguished name is in canonical format - it contains all possible naming components.
	 * The abbreviated format of a distinguished name removes the labels from the naming components.
	 * 
	 * @param name name to convert
	 * @param templateName name to be used when the input name is in common name format
	 * @return abbreviated name
	 */
	public static String toAbbreviatedName(String name, String templateName) {
		if (name==null) {
			return null;
		}
		if (name.length()==0) {
			return name;
		}
		
		String cacheKey = name + ((templateName!=null && templateName.length()>0) ? ("|" + templateName) : ""); //$NON-NLS-1$ //$NON-NLS-2$
		String abbrName = m_nameAbbrCache.get(cacheKey);
		if (abbrName!=null) {
			return abbrName;
		}
		
		Memory templateNameMem = templateName==null || templateName.length()==0 ? null : NotesStringUtils.toLMBCS(templateName, true); //used when abbrName is only a common name
		Memory inNameMem = NotesStringUtils.toLMBCS(name, true);
		try(DisposableMemory outNameMem = new DisposableMemory(NotesConstants.MAXUSERNAME)) {
  		ShortByReference outLength = new ShortByReference();
  		
  		short result = NotesCAPI.get().DNAbbreviate(0, templateNameMem, inNameMem, outNameMem, NotesConstants.MAXUSERNAME, outLength);
  		NotesErrorUtils.checkResult(result);
  		
  		String sOutName = NotesStringUtils.fromLMBCS(outNameMem, outLength.getValue() & 0xffff);
  		
  		m_nameAbbrCache.put(cacheKey, sOutName);
  		
  		return sOutName;
		}
	}

	/**
	 * Writes the specified names list in null terminated LMBCS strings to a {@link ByteArrayOutputStream}
	 * 
	 * @param names names to write
	 * @param bOut target output stream
	 */
	private static void storeAsUserNamesList(List names, ByteArrayOutputStream bOut) {
		//convert to canonical format
		List namesCanonical = new ArrayList<>(names.size());
		for (int i=0; i names) {
		ByteArrayOutputStream bOut = new ByteArrayOutputStream();
		storeAsUserNamesList(names, bOut);
		
		if (PlatformUtils.is64Bit()) {
			throw new IllegalStateException("Only supported for 32 bit");
		}
		
		Memory namesListMem;
		if (PlatformUtils.isWindows()) {
			namesListMem = new Memory(JNANotesConstants.winNamesListHeaderSize32);
			WinNotesNamesListHeader32Struct namesListHeader = WinNotesNamesListHeader32Struct.newInstance(namesListMem);
			namesListHeader.NumNames = (short) (names.size() & 0xffff);
			namesListHeader.write();
		}
		else {
			namesListMem = new Memory(JNANotesConstants.namesListHeaderSize32);
			NotesNamesListHeader32Struct namesListHeader = NotesNamesListHeader32Struct.newInstance(namesListMem);
			namesListHeader.NumNames = (short) (names.size() & 0xffff);
			namesListHeader.write();
		}
		
		DHANDLE.ByReference retHandle = DHANDLE.newInstanceByReference();
		short result = Mem.OSMemAlloc((short) 0, JNANotesConstants.namesListHeaderSize32 + bOut.size(), retHandle);
		NotesErrorUtils.checkResult(result);
		
		//write the data
		LockUtil.lockHandle(retHandle, (handleByVal) -> {
			Pointer ptr = Mem.OSLockObject(handleByVal);
			try {
				byte[] namesListByteArr = namesListMem.getByteArray(0, (int) namesListMem.size());
				ptr.write(0, namesListByteArr, 0, namesListByteArr.length);
				
				byte[] namesDataArr = bOut.toByteArray();
				ptr.write(namesListByteArr.length, namesDataArr, 0, namesDataArr.length);
			}
			finally {
				Mem.OSUnlockObject(handleByVal);
			}
			return 0;
		});

		return retHandle;
	}

	/**
	 * Allocates memory in the Notes memory pool and writes a NAMES_LIST data structure with
	 * the specified names. The names are automatically converted to canonical format.
	 * 
	 * @param names names for names list, similar to result of @UserNamesList formula, e.g. usernames, wildcards, groups and roles; either abbreviated or canonical
	 * @return memory handle to NAMES_LIST
	 */
	private static DHANDLE b64_writeUserNamesList(List names) {
		ByteArrayOutputStream bOut = new ByteArrayOutputStream();
		storeAsUserNamesList(names, bOut);
		
		if (!PlatformUtils.is64Bit()) {
			throw new IllegalStateException("Only supported for 64 bit");
		}
		
		Memory namesListMem;
		if (PlatformUtils.isWindows()) {
			namesListMem = new Memory(JNANotesConstants.winNamesListHeaderSize64);
			WinNotesNamesListHeader64Struct namesListHeader = WinNotesNamesListHeader64Struct.newInstance(namesListMem);
			namesListHeader.NumNames = (short) (names.size() & 0xffff);
			namesListHeader.write();
		}
		else if (PlatformUtils.isMac()) {
			namesListMem = new Memory(JNANotesConstants.macNamesListHeaderSize64);
			MacNotesNamesListHeader64Struct namesListHeader = MacNotesNamesListHeader64Struct.newInstance(namesListMem);
			namesListHeader.NumNames = (short) (names.size() & 0xffff);
			namesListHeader.write();
		}
		else {
			namesListMem = new Memory(JNANotesConstants.linuxNamesListHeaderSize64);
			LinuxNotesNamesListHeader64Struct namesListHeader = LinuxNotesNamesListHeader64Struct.newInstance(namesListMem);
			namesListHeader.NumNames = (short) (names.size() & 0xffff);
			namesListHeader.write();
		}

		DHANDLE.ByReference retHandle = DHANDLE.newInstanceByReference();
		short result = Mem.OSMemAlloc((short) 0, (int) namesListMem.size() + bOut.size(), retHandle);
		NotesErrorUtils.checkResult(result);
		
		//write the data
		LockUtil.lockHandle(retHandle, (handleByVal) -> {
			Pointer ptr = Mem.OSLockObject(handleByVal);
			try {
				byte[] namesListByteArr = namesListMem.getByteArray(0, (int) namesListMem.size());
				ptr.write(0, namesListByteArr, 0, namesListByteArr.length);
				
				byte[] namesDataArr = bOut.toByteArray();
				ptr.write(namesListByteArr.length, namesDataArr, 0, namesDataArr.length);
			}
			finally {
				Mem.OSUnlockObject(handleByVal);
			}
			return 0;
		});
		
		return retHandle;
	}

	/**
	 * Programatically creates a {@link JNAUserNamesList}
	 * 
	 * @param parentObj parent API object used for memory management
	 * @param names names for names list, similar to result of @UserNamesList formula, e.g. usernames, wildcards, groups and roles; either abbreviated or canonical
	 * @return names list
	 */
	public static JNAUserNamesList writeNewNamesList(IAPIObject parentObj, List names) {
		if (PlatformUtils.is64Bit()) {
			DHANDLE handle64 = b64_writeUserNamesList(names);
			JNAUserNamesList namesList = new JNAUserNamesList(parentObj, handle64);
			return namesList;
		}
		else {
			DHANDLE handle32 = b32_writeUserNamesList(names);
			JNAUserNamesList namesList = new JNAUserNamesList(parentObj, handle32);
			return namesList;
		}
	}
	
	/**
	 * Computes a {@link JNAUserNamesList} structure with all name variants, wildcards and groups for
	 * the specified user
	 * 
	 * @param parent API parent object for memory management
	 * @param server name of server, either abbreviated or canonical or null/empty string for local
	 * @param userName username, either abbreviated or canonical
	 * @return names list
	 */
	public static synchronized JNAUserNamesList buildNamesList(IAPIObject parent, String server, String userName) {
		Objects.requireNonNull(userName, "Name cannot be null");
		
		if (server==null || "".equals(server)) { //$NON-NLS-1$
			return buildNamesList(parent, userName);
		}

		//make sure that server and username are canonical
		userName = toCanonicalName(userName);
		server = toCanonicalName(server);
		
		Memory userNameLMBCS = NotesStringUtils.toLMBCS(userName, true);
		Memory serverNameLMBCS = NotesStringUtils.toLMBCS(server, true);
		
		boolean bDontLookupAlternateNames = false;
		short fDontLookupAlternateNames = (short) (bDontLookupAlternateNames ? 1 : 0);
		Pointer pLookupFlags = null;

		DHANDLE.ByReference rethNamesList = DHANDLE.newInstanceByReference();
		synchronized(BUILDNAMESLIST_LOCK) {
			short result = NotesCAPI.get().CreateNamesListFromSingleName(serverNameLMBCS,
					fDontLookupAlternateNames, pLookupFlags, userNameLMBCS, rethNamesList);
			NotesErrorUtils.checkResult(result);
		}
		
		JNAUserNamesList newList =  new JNAUserNamesList(parent, rethNamesList);
		return newList;
	}
	
	/**
	 * Expand one or more target names (e.g., might contain an alternate name) into a list of
	 * names (including any groups the target names belong to) by any given server name.
	 * 
	 * @param parent API parent object for memory management
	 * @param server name of server, either abbreviated or canonical or null/empty string for local
	 * @param names names to expand
	 * @return names list
	 */
	public static JNAUserNamesList createNamesListFromNames(IAPIObject parent, String server, String[] names) {
		server = toCanonicalName(server);
		
		try(DisposableMemory ptrArrMem = new DisposableMemory(Native.POINTER_SIZE * names.length)) {
    		Memory[] namesMem = new Memory[names.length];
    		
    		for (int i=0; i parent, String server, String group) {
		server = toCanonicalName(server);
		
		Memory groupLMBCS = NotesStringUtils.toLMBCS(group, true);
		Memory serverNameLMBCS = NotesStringUtils.toLMBCS(server, true);
		
		DHANDLE.ByReference rethNamesList = DHANDLE.newInstanceByReference();
		synchronized(BUILDNAMESLIST_LOCK) {
			short result = NotesCAPI.get().CreateNamesListFromGroupNameExtend(serverNameLMBCS, groupLMBCS, rethNamesList);
			NotesErrorUtils.checkResult(result);
		}
		
		JNAUserNamesList newList =  new JNAUserNamesList(parent, rethNamesList);
		return newList;
	}
	
	/**
	 * Computes a {@link JNAUserNamesList} structure with all name variants, wildcards and groups for
	 * the specified user on a remote server
	 * 
	 * @param parent API parent object for memory management
	 * @param userName username, either abbreviated or canonical
	 * @return names list
	 */
	public static JNAUserNamesList buildNamesList(IAPIObject parent, String userName) {
		Objects.requireNonNull(userName, "Name cannot be null");
		
		//make sure that username is canonical
		userName = toCanonicalName(userName);
		
		Memory userNameLMBCS = NotesStringUtils.toLMBCS(userName, true);
		
		DHANDLE.ByReference rethNamesList = DHANDLE.newInstanceByReference();
		
		//synchronizing the NSFBuildNamesList call because we had reproducible multithreading issues
		//under heavy application load
		synchronized (BUILDNAMESLIST_LOCK) {
			short result = NotesCAPI.get().NSFBuildNamesList(userNameLMBCS, 0, rethNamesList);
			NotesErrorUtils.checkResult(result);
		}
		
		JNAUserNamesList newList =  new JNAUserNamesList(parent, rethNamesList);
		return newList;
	}
	
	/**
	 * Computes the usernames list for the specified user, which is his name, name wildcards
	 * and all his groups and nested groups
	 * 
	 * @param parent API parent object for memory management
	 * @param server name of server, either abbreviated or canonical or null/empty string for local
	 * @param userName username in abbreviated or canonical format
	 * @return usernames list
	 */
	public static List getUserNamesList(IAPIObject parent, String server, String userName) {
		JNAUserNamesList namesList = buildNamesList(parent, server, userName);
		List names = namesList.toList();
		namesList.dispose();
		
		return names;
	}

	/**
	 * Computes the usernames list for the specified user, which is his name, name wildcards
	 * and all his groups and nested groups
	 * 
	 * @param parent API parent object for memory management
	 * @param userName username in canonical format
	 * @return usernames list
	 */
	public static List getUserNamesList(IAPIObject parent, String userName) {
		JNAUserNamesList namesList = buildNamesList(parent, userName);
		List names = namesList.toList();
		namesList.dispose();
		
		return names;
	}
	
	/**
	 * Enum of available user privileges
	 * 
	 * @author Karsten Lehmann
	 */
	public enum Privileges {

		/** Set if names list has been authenticated via Notes (e.g. user is allowed to open a database) */
		Authenticated(0x0001),

		/**	Set if names list has been authenticated using external password -- Triggers "maximum password access allowed" feature */
		PasswordAuthenticated(0x0002),

		/**	Set if user requested full admin access and it was granted */
		FullAdminAccess(0x0004);

		private int m_flag;

		Privileges(int flag) {
			m_flag = flag;
		}

		public int getValue() {
			return m_flag;
		}
	};
	
	/**
	 * Internal helper function that modifies the Authenticated flag of a {@link JNAUserNamesList}
	 * in order to grant access to certain C API functionality (e.g. when opening a database).
	 * 
	 * @param namesList names list
	 * @param privileges new privileges
	 */
	public static void setPrivileges(JNAUserNamesList namesList, EnumSet privileges) {
		int bitMask = 0;
		for (Privileges currPrivilege : Privileges.values()) {
			if (privileges.contains(currPrivilege)) {
				bitMask = bitMask | currPrivilege.getValue();
			}
		}
		
		short bitMaskAsShort = (short) (bitMask & 0xffff);

		/*Use different header implementations based on architecture, because we have
		//different alignments and data types:

		typedef struct {
			WORD		NumNames;
			LICENSEID	License;
											

			#if defined(UNIX) || defined(OS2_2x) || defined(W32)
			DWORD		Authenticated;
			#else							
			WORD		Authenticated;
			#endif
			} NAMES_LIST;
		*/
		
		DHANDLE handle = namesList.getAdapter(DHANDLE.class);
		if (handle==null || handle.isNull()) {
			throw new DominoException(0, MessageFormat.format("Missing DHANDLE value in names list: {0}", handle));
		}
		
		final int fBitMask = bitMask;
		
		if (PlatformUtils.is64Bit()) {
			LockUtil.lockHandle(handle, (handleByVal) -> {
				Pointer namesListBufferPtr = Mem.OSLockObject(handleByVal);
				
				try {
					if (PlatformUtils.isWindows()) {
						WinNotesNamesListHeader64Struct namesListHeader = WinNotesNamesListHeader64Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();
						namesListHeader.Authenticated = fBitMask;
						namesListHeader.write();
						namesListHeader.read();
					}
					else if (PlatformUtils.isMac()) {
						MacNotesNamesListHeader64Struct namesListHeader = MacNotesNamesListHeader64Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();

						namesListHeader.Authenticated = bitMaskAsShort;
						namesListHeader.write();
						namesListHeader.read();
					}
					else {
						LinuxNotesNamesListHeader64Struct namesListHeader = LinuxNotesNamesListHeader64Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();
						
						//setting authenticated flag for the user is required when running on the server
						namesListHeader.Authenticated = fBitMask;
						namesListHeader.write();
						namesListHeader.read();
					}
				}
				finally {
					Mem.OSUnlockObject(handleByVal);
				}
				return 0;
			});
		}
		else {
			LockUtil.lockHandle(handle, (handleByVal) -> {
				Pointer namesListBufferPtr = Mem.OSLockObject(handleByVal);
				
				try {
					//setting authenticated flag for the user is required when running on the server
					if (PlatformUtils.isWindows()) {
						WinNotesNamesListHeader32Struct namesListHeader = WinNotesNamesListHeader32Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();
						namesListHeader.Authenticated = fBitMask;
						namesListHeader.write();
						namesListHeader.read();
					}
					else {
						NotesNamesListHeader32Struct namesListHeader = NotesNamesListHeader32Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();
						namesListHeader.Authenticated = fBitMask;
						namesListHeader.write();
						namesListHeader.read();
					}
				}
				finally {
					Mem.OSUnlockObject(handleByVal);
				}
				return 0;
			});
		}
	}
	
	/**
	 * Reads which privileges have been set in the names list by method {@link #setPrivileges(JNAUserNamesList, EnumSet)}
	 * 
	 * @param namesList names list
	 * @return privileges
	 */
	public static EnumSet getPrivileges(JNAUserNamesList namesList) {
		/*Use different header implementations based on architecture, because we have
		//different alignments and data types:

		typedef struct {
			WORD		NumNames;
			LICENSEID	License;
											

			#if defined(UNIX) || defined(OS2_2x) || defined(W32)
			DWORD		Authenticated;
			#else							
			WORD		Authenticated;
			#endif
			} NAMES_LIST;
		*/
		
		int authenticated;
		
		DHANDLE handle = namesList.getAdapter(DHANDLE.class);
		if (handle==null || handle.isNull()) {
			throw new DominoException(0, MessageFormat.format("Missing DHANDLE value in names list: {0}", handle));
		}

		if (PlatformUtils.is64Bit()) {
			authenticated = LockUtil.lockHandle(handle, (handleByVal) -> {
				Pointer namesListBufferPtr = Mem.OSLockObject(handleByVal);
				
				try {
					if (PlatformUtils.isWindows()) {
						WinNotesNamesListHeader64Struct namesListHeader = WinNotesNamesListHeader64Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();
						return namesListHeader.Authenticated;
					}
					else if (PlatformUtils.isMac()) {
						MacNotesNamesListHeader64Struct namesListHeader = MacNotesNamesListHeader64Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();

						return (int) namesListHeader.Authenticated;
					}
					else {
						LinuxNotesNamesListHeader64Struct namesListHeader = LinuxNotesNamesListHeader64Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();
						
						//setting authenticated flag for the user is required when running on the server
						return namesListHeader.Authenticated;
					}
				}
				finally {
					Mem.OSUnlockObject(handleByVal);
				}
			});
		}
		else {
			authenticated = LockUtil.lockHandle(handle, (handleByVal) -> {
				Pointer namesListBufferPtr = Mem.OSLockObject(handleByVal);
				
				try {
					//setting authenticated flag for the user is required when running on the server
					if (PlatformUtils.isWindows()) {
						WinNotesNamesListHeader32Struct namesListHeader = WinNotesNamesListHeader32Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();
						return namesListHeader.Authenticated;
					}
					else {
						NotesNamesListHeader32Struct namesListHeader = NotesNamesListHeader32Struct.newInstance(namesListBufferPtr);
						namesListHeader.read();
						return namesListHeader.Authenticated;
					}
				}
				finally {
					Mem.OSUnlockObject(handleByVal);
				}
			});
		}
		
		EnumSet privileges = EnumSet.noneOf(Privileges.class);
		
		for (Privileges currPrivilege : Privileges.values()) {
			if ((authenticated & currPrivilege.getValue()) == currPrivilege.getValue()) {
				privileges.add(currPrivilege);
			}
		}
		return privileges;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy