Oracle user management

Create a new user:

create user bob
identified by str0nggpAsswd
default tablespace tblspace1
temporary tablespace temp;

Change user password:

alter user bob
identified by n3wPaswrd0;

Unlock a locked user account:

alter user bob
account unlock;

Assign privileges to user:

-- Allow user to connect to the database
grant create session to bob;

-- Allow user to create various objects;
-- usually reserved to DBAs and developers.
grant create table to bob;
grant create view to bob;
grant create sequence to bob;

-- Grant/revoke various table/view rights to objects to a user.
grant select on address_table to bob;
grant insert, update, delete on address_table to bob;
grant all on address_table to bob;

revoke delete on address_table from bob;
revoke all on address_table from bob;

-- Grant/revoke execute rights on functions/procedures for a user.
grant execute on sp_validate_address to bob;
revoke execute on sp_validate_address from bob;

Lock/unlock a user:

alter user bob account lock;
alter user bob account unlock;

Remove/delete a user:

drop user bob cascade;

List Oracle users:

-- List all users
select username, user_id, account_status, default_tablespace, temporary_tablespace, profile 
from dba_users;

-- List active users only
select username, user_id, account_status, default_tablespace, temporary_tablespace, profile 
from dba_users
where account_status='OPEN';

Set up a log off script to do backup

One possibility for the Windows log off script is to copy some data from the machine being logged off (perhaps being shut down?) to another for backup purposes. For example, I grab my Firefox settings and make a copy of it at “D:\systemBackup\windows\mozilla\”. You may wish to expand this to include others; maybe your FTP program’s configuration file that is found in “C:\Program Files”? Or progress of your favorite games? Below is what I have in my log off script, which is written as a batch file.

@echo off
set backupcmd=xcopy /c /d /e /h /r /y

echo Backing up Firefox settings...
%backupcmd% "C:\Documents and Settings\me\Application Data\Mozilla\*.*" "D:\systemBackup\windows\mozilla\"

echo Backing up my docs...
%backupcmd% "C:\docs\*.*" "D:\docs\*.*"

echo Backing up a file...
%backupcmd% "C:\john\doe.txt" "D:\john\doe.txt"

Note that in this example, I made use of the xcopy command found in all versions of Windows. The switches I used with the command allow us to copy only those files that had been changed, so the amount of time required to copy files may not be so much.

To set up this log off script, type in “c:\windows\system32\gpedit.msc” in your Run dialogue and click OK. Go to the User Configuration-Scripts (Logon/Logoff) section and add your script. This script will now fire the next time you log off.

Simple HTML tips for better search engine placement

We should make good use of the H1 and H2 tags. H1 carries greater weight, but it should only be used once on each page. We suggest using the H1 tag for either a list of meaningful keywords for that particular page, or use it for the website’s title. The H2 tag can be used several times on a page, though there probably should not be too many occurrences. We should use H2 tags for our page titles and important headers and sub-headers on the page.

Although Google does not look at META KEYWORDS and META DESCRIPTION tags, fill them in. Google may be the big player at the time of this writing, they are not the only player in town, so make sure you use these two fields. We should not overload these two meta fields; use a short list of quality keywords. Another obvious one is the TITLE tag. We should make sure it is used, and each page should have a title that is meaning for that particular page; also, should we regularly have long titles, make sure the first 10 words or so contain words and phrases that will grab visitors’ attention. Below is an example of the META tags we just discussed, taken from the World War II Database website.

<head>
<title>World War II Database: Your WW2 History Reference Destination</title>
<meta name="description" content="World War II Database: Your WW2 History Reference Destination">
<meta name="keywords" content="ww2db, history, military, world war, ww2, photo, photograph, panzer, yamato, pearl harbor, stalingrad, okinawa, iwo jima, normandy, d-day">

...
</head>

Finally, we need to make sure most, if not all, of our IMG tags should have descriptive ALT parameters. Here is a quick example:

<img src="images/picture.jpg" alt="US Marines and US Navy Corpsman raising the American flag on Mount Suribachi at Iwo Jima">

Use Java to generate Word document from XML template

To accomplish this, we first need a template. I already created a template for the purpose of this sample, which resembles a very simple invoice; please download word_template.xml file should you wish to follow along. In this file, you will see that there are many fields enclosed by “##” markers. For example:

...
<w:r>
<w:t>Invoice Number: ##INVOICENUMBER##</w:t>
</w:r>
...

The “##” markers signify places where the program will substitute in values we will provide when we call the Java program. The Java program code is below, with the main() method being an example we can run. The example shown by main() sets values for each required key in a hashtable, as well as setting up the location and file name of the template and output XML files. When we wish to run this example, place the template file in C: and let it go! The output file should look like the content of word_output.xml.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Hashtable;

/**
 * This code takes in a hashtable containing key fields required to populate
 * values into a Word template (XML) and output a Word document (also XML).
 * Template should contain ##KEY## fields for each hashtable key with same
 * name (without the ##s); the ##KEY## will be replaced by the value.
 * The main() method is written as an example.
 * Modified from code found at http://dinoch.dyndns.org:7070/WordML/AboutWordML.jsp
 * @author C. Peter Chen of http://dev-notes.com
 * @date 20080327
 */

public class msWordUtils {
	
	/**
	 * This main() method is used for demonstration purposes only.
	 * @param args
	 * @author C. Peter Chen of http://dev-notes.com
	 * @date 20080327
	 */
	public static void main(String[] args) {
		String templatePathFilename = "c:\word_template.xml";
		String outputPathFilename = "c:\word_output.xml";

		Hashtable ht = new Hashtable();
		ht.put("INVOICENUMBER","384123");
		ht.put("CUSTOMERNAME","Some Company, LLC.");
		ht.put("ITEMNAME1","Coffee");
		ht.put("UNITPRICE1","1.50");
		ht.put("QTY1","1");
		ht.put("LINETOTAL1","1.50");
		ht.put("ITEMNAME2","Donut");
		ht.put("UNITPRICE2","1.00");
		ht.put("QTY2","2");
		ht.put("LINETOTAL2","2.00");
		ht.put("INVOICETOTAL","3.50");
		ht.put("DUEDATE","4/1/2008");
		
		generateWordDoc(ht, templatePathFilename, outputPathFilename);
	}
	
	/**
	 * 
	 * @param ht
	 * @param templatePathFileName
	 * @param outputPathFileName
	 * @author C. Peter Chen of http://dev-notes.com
	 * @date 20080327
	 */
	public static void generateWordDoc(Hashtable ht, String templatePathFilename, String outputPathFilename) {	
		try {
			BufferedReader reader = new BufferedReader(new FileReader(templatePathFilename));
			
			File destination = new File(outputPathFilename);
			BufferedWriter writer = new BufferedWriter(new FileWriter(destination));
			
			String thisLine;
			int i = 0;
			
			while ((thisLine = reader.readLine()) != null) {
				System.out.println(i);
				
				for (java.util.Enumeration e = ht.keys(); e.hasMoreElements();) {
					String name = (String) e.nextElement();
					String value = ht.get(name).toString();
					// Use this if we need to XML-encode the string in hashtable value...
					thisLine = thisLine.replaceAll("##" + name.toUpperCase() + "##", XmlEncode(value));
					// ... or this if we do not need to do XML-encode.
					//thisLine= thisLine.replaceAll("##" + name.toUpperCase() + "##", value);
			    }
				writer.write(thisLine);
				writer.newLine();
				i++;
			}
			writer.close();
			System.out.println("done");
		}
		catch (Exception e) {
			System.out.println("exception!=" + e);
		}
	}

	/**
	 * Encodes regular text to XML.
	 * @param text
	 * @return string
	 * @author http://dinoch.dyndns.org:7070/WordML/AboutWordML.jsp
	 * @date 20050328
	 */
	private static String XmlEncode(String text) {
		int[] charsRequiringEncoding = {38, 60, 62, 34, 61, 39};
		for(int i = 0; i < charsRequiringEncoding.length - 1; i++) {
			text = text.replaceAll(String.valueOf((char)charsRequiringEncoding[i]),"&#"+charsRequiringEncoding[i]+";");
		}
		return text; 
	}
	
}

As noted in the JavaDoc, much of the basis for this code came from http://dinoch.dyndns.org:7070/WordML/AboutWordML.jsp. Much thanks to the unnamed author!

Create an auto-increment field in Oracle

If we do not already have a table to work with, we will create a sample table for this purpose.

create table schema.test_table (
	row_id		number(5),
	name		varchar2(50),
	status		varchar2(1),
	entry_date	date
);

Let us say we want to make the “row_id” field an “auto-increment” field. To do so, we need a sequence object first. The code below gives us an sequence that starts off at 1 and capped at 99999.

create sequence schema.seq_test_table_row_id
minvalue 1
maxvalue 99999
start with 1
increment by 1
nocache;

All we have left is the actual “auto-increment” part. To do this, we build a very simple trigger on the table.

create trigger schema.trg_test_table_row_id
before insert on schema.test_table
for each row
begin
	if (:NEW.row_id) is null then
		select schema.seq_test_table_row_id.nextval into :NEW.row_id from dual;
	end if;
end;

Now, when you insert into the table schema.test_table, if you wish to use the sequence object as an auto-incrementer, just do not enter the row_id field. Two examples of using this auto-incrementer below:

insert into schema.test_table (name, status, entry_date) values('John Doe','1',sysdate);
insert into schema.test_table values(null,'John Doe','1',sysdate);

What if we want to override this trigger and use a number of our choice for the row_id field? No problem, just insert the value as you would normally. Note the trigger has an if-clause in it; the trigger will only have an effect (ie. auto-increment from the sequence) if the incoming row_id field is not populated.

Generating a report of user password expiration status

To start off, check out the setup section somewhat near the top. They should be fairly explanatory, with the only potentially tricky one being the “ouList” variable, which holds a list of Organization Units whose users you which to see in your report; it should be a tilde (“~”) delimited string. Some examples:

ou=contractors
ou=engineers,ou=contractors
ou=contractors~ou=engineers,ou=contractors

Compare example 1 and example 2; because the code is not meant to go into deeper levels, “ou=contractors” will only check for user objects at that particular level only. To include the nested OU “engineers”, it must be explicitly listed, such as in example 2.

The full sample is below:

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Description: Makes a CSV listing of password expiration status
' Author: C. Peter Chen, http://dev-notes.com
' Revision History:
'	1.0	20080318	Original concept written
'	1.0.1	20080319	Added column headings in the output
'				Added "excludeNoOu" switch to exclude objects 
'				  not in an Organizational Unit as a possible 
'				  way to exclude system objects like IWAM and 
'				  IUSR from output.
'	1.0.2	20080325	Added the "ouList" delimited string the 
'				  restriction of specific OUs to show up in 
'				  the report.
'				Added "debugMode" switch
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

option explicit
dim maxPwdAge, warningThreshold, ad, outputPath, outputFileName, keepDailyOutput, excludeNoOu, ouList, debugMode

''''''''''''''
' Some Setup '
''''''''''''''
maxPwdAge = 45			' What is the maximum age (in days) of passwords in your domain?
warningThreshold = 10		' The report will show warning (almost expired) status if expiration is within this many days.
ad = "dc=domain,dc=com"		' What is your domain? Example format: "dc=domainname,dc=com"
outputPath = "\\share\folder\"	' The path for the output CSV report with slash at the end; examples: "c:\", "c:\reports\", "\\fileshare\folder\"
outputFileName = "reportName"	' The file name of the output CSV report; no need for ".csv" at the end
keepDailyOutput = "N"		' Valid values include Y or N.  If Y, a new report will be created each time with file name format "filenameYYYY-MM-DD-HH24-MI.csv"; if N, each day's report will overwrite the previous day's as "filename.csv".
excludeNoOu = "Y"		' Valid values include Y or N.  If Y, objects not in an Organizational Unit will not appear in the report; it maybe a way to exclude system objects like IWAM and IUSR.

' ouList is a list of OUs, delimited by tilde ("~"), whose users will appear in the report
' Sample: "ou=sales,ou=fulltimers~ou=engineers,ou=contractors"
ouList = "ou=sales,ou=fulltimers~ou=engineers,ou=contractors"

debugMode = "N"			' If Y, any encountered errors will pop up on screen and a pop up will appear when the script completes

'''''''''''''
' End Setup '
'''''''''''''


''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''


'''''''''''''
' Constants '
'''''''''''''
Const ADS_UF_DONT_EXPIRE_PASSWD = &h10000

'''''''''''''
' Functions '
'''''''''''''
dim i, foundUser

function getAdObject (strUserFullName, ad, ouListArray)
	On Error Resume Next
	err.clear

	Set objUser = GetObject("LDAP://cn=" & strUserFullName & "," & ouListArray(0) & "," & ad)

	if err.number <> 0 then
		foundUser = "N"
		i = 1 ' Yes, start at the second one...
		do while i <= UBound(ouListArray)
			if err.number = -2147016656 then
				foundUser = "N"
				Set objUser = GetObject("LDAP://cn=" & strUserFullName & "," & ouListArray(i) & "," & ad)
			else
				foundUser = "Y"
				exit do
			end if
			i = i + 1
		loop
	else
		foundUser = "Y"
	end if
	
	if err.number <> 0 and foundUser = "Y" then
		exit function
	else
		getAdObject = objUser
		err.clear
		exit function
	end if
end function

'''''''''''''''''''''''''''''''
' The Main Part of the Script '
'''''''''''''''''''''''''''''''
dim fso, f, objConnection, objCommand, objRecordSet, intUAC, objUser, dtmValue, intTimeInterval

set fso = CreateObject("Scripting.FileSystemObject")

if keepDailyOutput="Y" then
	filedate=year(now) & "-" & month(now) & "-" & day(now) & "-" & hour(now) & "-" & minute(now)
	set f = fso.createtextfile(outputPath & outputFileName & filedate & ".csv")
else
	set f = fso.createtextfile(outputPath & outputFileName & ".csv")
end if

f.writeline("""User Name"",""Password Status"",""Password Age (Days)"",""LDAP Distinguished Name""")

Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection
objCommand.CommandText = ";(objectCategory=User);userAccountControl,distinguishedName,name;subtree"
Set objRecordSet = objCommand.Execute

dim ouListArray
ouListArray = split(ouList,"~",-1,1)

Do Until objRecordset.EOF
	if (excludeNoOu = "Y" and instr(objRecordset.Fields("distinguishedName"),"OU=") = 0) then ' Do not show user not in an OU; this may exclude system users like IWAM and IUSR
		' Do nothing and skip this user
	else
		intUAC=objRecordset.Fields("userAccountControl")

		If intUAC AND ADS_UF_DONT_EXPIRE_PASSWD Then ' Password never expires for this user
			on error resume next
			
			set objUser = nothing
			getAdObject objRecordset.Fields("name"), ad, ouListArray

			dtmValue = objUser.PasswordLastChanged ' Just to test if error occurs
			
			if err.number = 0 then
				f.writeline("""" & objRecordset.Fields("name") & """,""never expire"","""",""" & objRecordset.Fields("distinguishedName") & """")
			else ' Error encountered...
				if debugMode = "Y" then
					msgbox(objRecordset.Fields("name") & "error. err.number='" & err.number & "', err.description='" & err.description & "'")
				else
					' Not in debug mode, so we suppress the error and do nothing
				end if
			end if
		else ' Password will expire for this user
			set objUser = nothing
			getAdObject objRecordset.Fields("name"), ad, ouListArray

			if not (objUser is nothing) then
				if objUser.AccountDisabled = false then ' show ENABLED users only
					on error resume next

					dtmValue = objUser.PasswordLastChanged 

					if err.number = 0 then
						intTimeInterval = int(now - dtmValue)
						if intTimeInterval >= maxPwdAge then
							f.writeline("""" & objRecordset.Fields("name") & """,""expired"","""& intTimeInterval & """,""" & objRecordset.Fields("distinguishedName") & """")
						elseif intTimeInterval >= (maxPwdAge - warningThreshold) then
							f.writeline("""" & objRecordset.Fields("name") & """,""expiring soon"","""& intTimeInterval & """,""" & objRecordset.Fields("distinguishedName") & """")				
						else
							f.writeline("""" & objRecordset.Fields("name") & """,""ok"","""& intTimeInterval & """,""" & objRecordset.Fields("distinguishedName") & """")
						end if
					else ' This user's password is set to force-change at next logon
						'if objUser.name <> "" then ' We still need to make sure this user is in the list of OUs we want to check
							f.writeline("""" & objRecordset.Fields("name") & """,""forced to change at next logon"","" "",""" & objRecordset.Fields("distinguishedName") & """")
						'end if
						err.clear
					end if
				else
					' Do nothing for DISABLED users
				end if
			else
				' Do nothing for users not found; probably in a different OU then specified.
			end if
		end if
	end if
	
	objRecordset.MoveNext
Loop
 
objConnection.Close

if debugMode = "Y" then
	msgbox("AD user password expiration script completed.  Report = " & outputPath & outputFileName & ".csv")
end if

Cleaning up the listener log

Since we noticed the listener log has grown out of control, we should already know the location of it. Just in case you need it, by default that log file is located at “NETWORK/log/listener.log” in your Oracle home.

The listener log cannot be modified while the listener is active, and more than likely, we need to minimize listener down time so we interrupt users the least. To do so, we are going to create a batch file so that the three commands we are going to run will run successively in a short amount of time. Alternatively, you can also perform them during business off-hours, if that is available to you. Either running in a batch file or running them manually, the commands are the same.

lsnrctl set log_status off
rename listener.log listener.old
lsnrctl set log_status on

The first commands is used to disable the listener, which will release the file lock on the listener.log file so we can work with it. The second command renames the file. Finally, the third command restarts the listener to resume normal operations. Immediately after the third command runs, a new listener.log file will be created with 0 size.

What to do with the old listener log “listener.old” is up to each of us. Personally, I tend to zip up the file and store it at a safe place, just in case I ever need to review it.

Change Leopard’s login background

This set of instructions is written for Mac OSX version 10.5.2.

First, run this command in terminal to back up your default photo, just in case you ever need it again.

sudo mv /System/Library/CoreServices/DefaultDesktop.jpg /System/Library/CoreServices/DefaultDesktopBackup.jpg

Then, run the following terminal command to copy the new background to the system library. In this example below, we assume newBackground.jpg is the photo of your choosing.

sudo cp newBackground.jpg /System/Library/CoreServices/DefaultDesktop.jpg

The next time you login, you will see your new photo replacing the default Mac one.

If you decide you wish to revert this change, just run the following terminal command to copy the default image back.

sudo cp /System/Library/CoreServices/DefaultDesktopBackup.jpg /System/Library/CoreServices/DefaultDesktop.jpg

Checking admin share free space and folder space usage

The code to perform these two space checks is below, with the output file being generated in comma-delimited CSV format. When we use this, we need to first set a few configuration items in the first section.

  • outputFile : Enter the path and file name of the output report.
  • i : The number of items you wish to check; in the example, the number is 4 (2 drives and 2 folders)
  • arrayList(x, y) : These are the details of each drive/folder you wish to check…

The code is as follows:

option explicit
dim outputFile, arrayList, fso, obj, fl, j, drivepath, driveName, status, msg
'''''''Config''''''''''

' Where do you want to output the results to?
outputFile = "checkSpace.log"

' How many drives do you want to check for free space?
const i = 4
redim arrayList(i,5) 'Please do not touch this line.

' List the server drives you want to check for free space or folders to check for space usage
' Each set has 5 values:
' a. D=drive, F=folder
' b. server hostname
' c. admin share drive letter (applicable for type D only)
' d. warning level in gb
' e. alarm level in gb
' f. common name of this share

arrayList(0,0) = "D"
arrayList(0,1) = "web1"
arrayList(0,2) = "c"
arrayList(0,3) = 10
arrayList(0,4) = 5
arrayList(0,5) = "Windows 2003 web server, C-drive"

arrayList(1,0) = "D"
arrayList(1,1) = "exch2"
arrayList(1,2) = "d"
arrayList(1,3) = 200
arrayList(1,4) = 100
arrayList(1,5) = "Windows 2003 Exchange server, D-drive"

arrayList(2,0) = "F"
arrayList(2,1) = "\fileserverpublicfileShare1"
arrayList(2,2) = ""
arrayList(2,3) = 1
arrayList(2,4) = 2
arrayList(2,5) = "File share 1"

arrayList(3,0) = "F"
arrayList(3,1) = "\fileserverpublicfileShare2"
arrayList(3,2) = ""
arrayList(3,3) = 100
arrayList(3,4) = 200
arrayList(3,5) = "File share 2"

'''''''End Config'''''''''

set fso = CreateObject("Scripting.FileSystemObject")
set fl = fso.CreateTextFile(outputFile, true)

fl.writeline("""Item"",""Status"",""Message""")

j = 0
do while j <= i-1
	if arrayList(j,0) = "D" then
		drivepath = "\\" & arrayList(j,1) & "\" & arrayList(j,2) & "$"
		set obj = fso.GetDrive(fso.GetDriveName(drivepath))
	elseif arrayList(j,0) = "F" then
		drivepath = arrayList(j,1)
		set obj = fso.GetFolder(drivepath)
	else
		' shouldn't really get in here...
	end if
		
	driveName = arrayList(j,5)
	
	if arrayList(j,0) = "D" then
		if round(obj.FreeSpace/1024/1024/1024) < arrayList(j,4) then
			status = "alarm"
			msg = drivepath & " (" & driveName & ") only has " & round(obj.FreeSpace/1024/1024/1024) & "gb free"
		else
			if round(obj.FreeSpace/1024/1024/1024) < arrayList(j,3) then
				status = "warning"
				msg = drivepath & " (" & driveName & ") only has " & round(obj.FreeSpace/1024/1024/1024) & "gb free"
			else
				status = "ok"
				msg = drivepath & " (" & driveName & ") is ok with " & round(obj.FreeSpace/1024/1024/1024) & "gb free"
			end if
		end if
	elseif arrayList(j,0) = "F" then
		if round(obj.size/1024/1024/1024) > arrayList(j,4) then
			status = "alarm"
			msg = drivepath & " (" & driveName & ") has reached " & round(obj.size/1024/1024/1024) & "gb"
		elseif round(obj.size/1024/1024/1024) > arrayList(j,3) then
			status = "warning"
			msg = drivepath & " (" & driveName & ") has reached " & round(obj.size/1024/1024/1024) & "gb"
		else
			status = "ok"
			msg = drivepath & " (" & driveName & ") is ok at " & round(obj.size/1024/1024/1024) & "gb used"
		end if
	else
		status = "error"
		msg = "Configuration error"
	end if
	
	fl.writeline("""" & drivepath & """,""" & status & """,""" & msg & """")
	
	set obj = nothing
	j = j+1
loop

set fl=nothing
set fso=nothing

The output CSV file should look something like this.

"Item","Status","Message"
"\\web1\c$","ok","\\web1\c$ (Windows 2003 web server, C-drive) is ok with 10gb free"
"\\exch2\c$","alarm","\\exch2\c$ (Windows 2003 Exchange server, D-drive) only has 16gb free"
"\\fileserver\public\fileShare1","warning","\\fileserver\public\fileShare1 (File share 1) has reached 2gb"
"\\fileserver\public\fileShare2","ok","\\fileserver\public\fileShare2 (File sahre2) is ok at 91gb used"

Using .htaccess file to control web directory access

Naturally, if the .htaccess (ht.acl in Windows) does not already exist in the directory we wish to protect, we must create it first. It is a plain text file, so you may use any text editor to create/modify this file, such as pico, emacs, Notepad, or TextEdit.

Our first step is to add these lines below to the .htaccess file.

AuthName "This is a restricted area, please log in first."
AuthType Basic
AuthUserFile /directory/path/passwdfile

AuthName is the text that will appear in the browser pop-up when the user is challenged. AuthType value of “Basic” means we are using basic HTTP authentication. AuthUserFile is the path and file name of our password file; more on that later.

Also in the .htaccess file, we add a list of user names we wish to allow to access the web directory we are locking down. For example:

require user jdoe
require user spannu

We are now done with the .htaccess file. Now we just have to create the password file. In the Apache bin, there is an executable called “htpasswd”. The first example below is used to create a new password file with the user “jdoe”; note that when using the -c parameter to create a new file, we will overwrite any password file that exists in the same directory, so be careful. To add a new user to an existing file, we should run the second example, the difference being the lack of the -c parameter.

htpasswd -c -b /directory/path/passwdfile jdoe secUr3Pwd

htpasswd -b /directory/path/passwdfile spannu an0therPwd

The -b parameter allows us to type in the password in the command line, which is helpful when you are setting up a script that creates a large number of users at once. If having the password in the command line cache is a concern, just remove the -b parameter, and we will be prompted to enter a password for each user.

We should now be all set. The next web visitor that reaches the directory where the .htaccess file resides should be challenged with a password prompt.

To remove a user from a certain password file:

htpasswd -D /directory/path/passwdfile jdoe

For our reference, below is the help text for the htpasswd command.

Usage:
        htpasswd [-cmdpsD] passwordfile username
        htpasswd -b[cmdpsD] passwordfile username password

        htpasswd -n[mdps] username
        htpasswd -nb[mdps] username password
 -c  Create a new file.
 -n  Don't update file; display results on stdout.
 -m  Force MD5 encryption of the password (default).
 -d  Force CRYPT encryption of the password.
 -p  Do not encrypt the password (plaintext).
 -s  Force SHA encryption of the password.
 -b  Use the password from the command line rather than prompting for it.
 -D  Delete the specified user.
On Windows, NetWare and TPF systems the '-m' flag is used by default.
On all other systems, the '-p' flag will probably not work.