Using NDS Connection APIs To Write Enterprise Enabled Applications
Articles and Tips: article
Developer Support Engineer
Developer Support
01 May 1996
This article describes how to use NetWare Directory Services APIs to extend applications from the locality of the office to a corporate NDS tree.The NDS Connection APIs allow an application to authenticate to a server in the NDS tree without any user action and access resources on the server as required. The ability of the connection APIs to perform background authentication is part of the NDS Authentication system, whereby a user only has to authenticate to the NDS tree. The process of background authentication is central to making a local application an enterprise application where it can access resources at any point in the corporate NDS tree.
Introduction
This article describes how to use NetWare Directory Services APIs to extend applications from the locality of the office to a corporate NDS tree. Normally, an application can only access resources from servers for which the workstation has an authenticated connection. To access resources from other servers the workstation has to first authenticate to the server. This could involve the user of the application having to enter a username and password for the server so that the application can authenticate to the server. Naturally this interruption is inconvenient and possibly confusing to a network-ignorant user.
The NDS Connection APIs allow an application to authenticate to a server in the NDS tree without any user action and access resources on the server as required. The connection APIs' ability to perform background authentication is part of the NDS Authentication system, where a user only has to authenticate to the NDS tree.
Once the user has been authenticated to the NDS tree, any server in the tree can confirm that the authentication was valid and grant the user an authenticated connection to the server. The process of background authentication is central to making a local application an enterprise application where it can access resources at any point in the corporate NDS tree.
Services Provided by the NetWare Requester
Writing applications that are network enabled normally requires no additional code in the application. This is because the NetWare Requester supplied by Novell handles any request intended for the network and performs the required actions on behalf of the local operating system. For example, the standard C open() function in a DOS environment can be used with variety of path formats, as shown below.
open("F:PATH\\FILE.EXT",O_RDONLY);" open("SERVER/VOLUME:PATH/FILE.EXT",O_RDONLY);" open("\\\\SERVER\\VOLUME\\PATH\\FILE.EXT",O_RDONLY);"
In each case the NetWare Requester will handle the call and return a DOS file handle. In fact, many applications make themselves network disabled by rejecting network filenames as syntactically incorrect when the application could use them without any modification.The NetWare requester also handles network printing by redirecting print jobs from the local port to a print queue for printing. In this case the application believes it is sending the print job to a local port and is unaware that the data is being redirected.
In both of the above examples the services can be provided by the NetWare Requester only if the requester has been configured to use those resources, for example by using the NetWare LOGIN, MAP,and CAPTURE commands. An application can follow two courses if the user requires resources on a server for which the requester has not been configured to use. The application can cancel the operation and display a message asking the user to attach to the server, and the user can then use commands like MAPor NWUSER to connect to the server. Alternately, the application can use the NetWare connection APIs to connect to the server and proceed with the operation. An application that uses APIs to directly access NetWare resources is normally referred to as a NetWare-aware application. Applications that are NetWare aware provide better functionality and performance than standalone applications since NetWare-aware applications communicate directly with the NetWare Requester rather than through the local operating system.
NDS Authentication
NetWare Directory Services changed the focus of network authentication from server to corporate environment. Under NetWare 3.x a user had to authenticate with each server the user wished to use. With NetWare 4.x and NDS a user authenticates once to the NDS tree, with subsequent authentications being performed automatically in the background. When a user authenticates to the NDS, either by using the LOGIN command or the NWDSLogin()function, the workstation generates a software key pair called the credential and signature.
For more informationon how NDS Authenticationworks, see "AuthenticationOperations" in the UsingNetWare Servicesfor NLM Applications manual on the NovellSDK. |
Once the credential/signature pair has been generated the user can establish a connection without needing to give a password. When the user needs to authenticate to a server, the server can query the NDS and verify that the key pair supplied was generated by the user and that the key has not been copied or forged. It must be emphasised that the background authentication system does not rely on password caching, in fact the password is only held for a few milli-seconds and never leaves the user's workstation.
Connection States in NDS
A NetWare 4.x connection can be in one of three states: attached, authenticated or licensed. The attached state is the initial state after the connection handle has been obtained using the function NWAttachToFileServer(). An attached connection can only view NDS data that has [public] access. Once the credential/signature pair has been generated a NetWare 4.x connection can be authenticated by using the NWDSAuthenticate() function. This identifies the user to the server and allows the user to access NDS data available to the user. When a connection has been authenticated it can then be licensed by using the NWDSLockConnection() function. A licensed connection can be used to access a server's resources. As the name suggests, a licensed connection uses one of the server's connection licenses, an authenticated connection does not.
The process of gaining a licensed connection to a NetWare 4.x server is described by the following pseudo-code.
function GetLicensedConnection() begin if licensed connection to server already exists then return success if not NDS authenticated then return error("Not authenticated to NDS")" get NDS tree name if not attached to server and attach to server fails then return error("Failed to attach to server")" if server is not in NDS tree then return error("Server is not in NDS tree")" if authentication to server fails then return error("Cannot authenticate to server")" if cannot lock server connection then return error("Cannot license server connection")" return success end
The 16-bit NetWare Client can only support a single credential/signature pair; hence a user can only authenticate as an NDS user to servers in a single tree. The 32-bit client supports multiple NDS trees. If a user requires a connection to a server that is not in the current NDS tree, for example a NetWare 3.x server, then the user will need to establish a bindery connection. A bindery connection can be established by using the NWAttachToFileServer() and NWLoginToFileServer()functions. The NWLoginToFileServer() function requires a user name and password.
NETCOPY Example - An Enterprise File Copy Application
To demonstrate the use of background authentication an example application that can be used to copy files between servers in an NDS tree will be described. The standard file copy functions such as ncopy, copyand winfile require that the user has licensed connections to both the source and destination servers. Occasionally a user may wish to copy a file to or from a server for which there is no licensed connection.
Normally this will involve logging into the server, copying the file, and then logging out of the server. Even with background authentication this process is awkward and hard to encapsulate in a batch file. NETCOPY uses the NDS Connection APIs to perform the background authentication if a new connection is needed, and will close the connection after the file copy if a new connection was created.The central structure of NETCOPY is the C++ class Path. The Path constructor parses a path into server, volume, and path fields. If a server is specified, a new licensed connection is made to the server if one does not already exist. If a server is not specified, the path is local and hence no connection is needed. The Path destructor will close the connection if it was created by the constructor.
The declaration of Path is given below. In the Path constructor the function NWParsePath() is used to translate a path from a command line parameter into its server, volume, and path components. NWParsePath() is a very useful function since it can parse several different formats and return the server name and connection handle given a network drive letter. The member function Pathauthenticate() is used to connect and authenticate to a server if a connection does not already exist.
The member function Pathprint() is used to print the path in UNC or drive:path format depending on if the path is remote or local. Both formats can be used directly by the open() command to copy the file. (UNC format is used for remote paths since this format is understood by OS/2 clients).
class Path { char Server[48]; char Volume[18]; char Path[256+18+48]; int Connected; int Authenticated; int ConnType; NWCONN_HANDLE Conn ; public: int State ; Path (char * path) ; ~ Path (); int print(); int authenticate(); int getStat() ; const char * path() { return Path ; } const char * concatenate(const char * file) { return (file) ? strcat(Path,file) : Path ; } } ; /* ** PathPath(char * filename) ** ** => filename Filename given as a command line argument.> ** returns void. ** ** If the argument is a network path then parse path into server/volume ** format and open a connection with the server. If it is a local path ** then use drive:path format. */ PathPath(char * filename) { memset(this,0,sizeof(struct Path)) ; // If the argument is NULL then use current directory. if (filename == 0){ Volume[0] = getdisk() + 'A' ; Path[0] = '\\' ; State = getcurdir(0,Path+1) ; return ; } // All paths should be in upper case. strupr(filename) ; // Parse path into server, volume and path. If Conn is non zero then // there is already a connection to the server. If Server is NULL // then the path is local. State = NWParsePath(filename,Server,& Conn,Volume,Path) ;& if (Conn){ Connected = 1 ; // Mark connection as pre-existing NWGetConnInfo(Conn,NW_AUTHENTICATED,& Authenticated) ;& NWGetConnInfo(Conn,NW_CONN_TYPE,& ConnType) ;& if (Authenticated && ConnType == NW_CONN_BIND) return ;& } if (Server[0]) authenticate() ; } /* ** Path~Path ** ** arguments void. ** returns void. ** ** If a connection was established in the constructor then ** the connection is closed. */ Path~Path() { if (Conn == 0) return ; NWDSUnlockConnection(Conn) ; if (! Connected){ NWFreeConnectionSlot(Conn,SYSTEM_DISCONNECT); NWDetachFromFileServer(Conn); } } /* ** int Pathprint() ** ** arguments void. ** returns TRUE. ** ** Format the path into \\server\volume\path or drive:path as ** appropriate. UNC format is used for the benefit of OS/2 users. */ int Pathprint() { char buffer[256+18+48] ; if (Conn) sprintf(buffer, (Path[0] == '\\') ? "\\\\%s\\%s%s" : "\\\\%s\\%s\\%s"," Server,Volume,Path); else sprintf(buffer, (Path[0] == '\\') ? "%s:%s" : "%s:\\%s",Volume,Path) ;" strcpy(Path,buffer) ; return TRUE ; } /* ** int Pathauthenticate() ** ** arguments void. ** returns TRUE if a licensed connection was established. ** ** Establish a new connection to the server in Server[]. */ int Pathauthenticate() { char tree[64] ; if (! NWIsDSAuthenticated()) return error("Not authenticated to NDS\n") ;" if (Conn == 0 && (State = NWAttachToFileServer(Server,0,& Conn)) != 0)& return error("Fail to attach to server %s\n",Server) ;" if (! NWIsDSServer(Conn,tree)) return error("Server %s is not an NDS server\n",Server) ;" if (! Authenticated && (State = NWDSAuthenticate(Conn,0,0)) != 0)& return error("Fail to authenticate to server %s\n",Server) ;" if ((State = NWDSLockConnection(Conn)) != 0) return error("Fail to lock connection to server %s",Server) ;" return TRUE ; } /* ** int PathgetStat() ** ** arguments void. ** returns Status bits associated with the file. ** ** Returns the status bits associated with the file using the stat() ** command. If stat() fails then return zero. */ int PathgetStat() { struct stat statbuf ; return stat(Path,& statbuf) == &1 ? 0 : statbuf.st_mode ;& }
The code used in Pathauthenticate() is not identical to that given in the earlier pseudo-code because function NWParsePath() is used to test if there is a connection to the server, and the function NWDSAuthenticate() is used to see if the server is in the same NDS tree. If a server connection already exists, the function NWGetConnInfo() is used to see if the connection has been authenticated. The authentication procedure used here makes no provision for creating Bindery connections. In this case the function NWLoginToFileServer() should be used rather than NWDSAuthenticate(). As stated earlier, NWLoginToFileServer() requires a user name and password.
The structure of main() is straightforward since most of the work is performed by the Path class. An abridged version of main() is given below. The class Locale is used to initialise the interface to NetWare requester and load the Unicode tables. The Locale destructor will unload the Unicode tables.
int main(int argc, char * argv[]) { // Initialise the interface to the requester and load Unicode // tables to interface with the NDS. Locale locale ; // Initialise Path objects and connect to serves if necessary. Path From(argv[1]), Dest(argc == 3 ? argv[2] : 0) ; // Format paths into SERVER/VOLUME:PATH or DRIVE:PATH format and // determine status of the files. From.print() ; From.getStat(); Dest.print() ; Dest.getStat(); // Check that the source file exists and that a directory is not // being copied onto a single file. if ((From.State & S_IREAD) == 0)& return error("Cannot access file %s\n",From.path()) ;" if ((Dest.State & S_IFDIR) == 0 &&& (From.State & S_IFDIR) && Dest.State)& return error("Cannot copy directory over a file\n") ;" // When copying a file to directory, add the filename to the // directory to get the destination file name. if ((From.State & S_IFDIR) == 0 && (Dest.State & S_IFDIR))& Dest.concatenate(strrchr(From.Path,'\\')); // Copy file(s) to destination. if (From.State & S_IFDIR)& copyDirectory(From.Path,Dest.Path); else copyFile(From.Path,Dest.Path); return 0 ; }
NETCOPY source code is available from Novell ftp sites and or from the author.
Listing 1: Locale.cpp
/* ** FILE: LOCALE.CPP ** PROG: NETCOPY.EXE ** DATE: October 1995 ** ** NETCOPY path1 [path2] ** ** Copy files and directories between servers, authenticate and logout ** as required. ** ** Original: John Buckle 25/10/95 ** Revised: John Buckle 26/02/96 */ # define NWL_EXCLUDE_FILE # define NWL_EXCLUDE_TIME # include <dir.h<<< # include <nwmisc.h<<< # include <unicode.h<<< # include <nwlocale.h<<< # include "netcopy.h""" /* ** int LocaleInitialised ** ** Indicates if the tables have already been loaded. */ int LocaleInitialised = 0 ; /* ** LocaleLocale() ** ** => arguments void>> ** returns void ** ** Initialises the unicode tables by searching all drives for thev ** NLS directory. The member field Status records whether the tables ** were loaded. */ LocaleLocale() { LCONV lconvInfo ; if (Initialised++) return; NWCallsInit(0,0) ; NWLsetlocale(LC_ALL,"");"" NWLlocaleconv(&lconvInfo);&& int currentDrive = getdisk(); if (NWInitUnicodeTables(lconvInfo.country_id,lconvInfo.code_page) == 0) return ; for (int drive = 'Z' 'A'; drive 'B' 'A' ; drive ){ if (foundUnicodeTables(drive,lconvInfo.country_id,lconvInfo.code_page)) break ; } if (drive == 'B' 'A') Initialised ; setdisk(currentDrive) ; } /* ** Locale~Locale() ** ** => arguments void>> ** returns void ** ** Releases the unicode tables if this is the last object using them. */ Locale~Locale() { if ( Initialised == 0) NWFreeUnicodeTables() ; } /* ** int LocalefoundUnicodeTables(int drive, int country, int codePage) ** ** => drive Drive to test for unicode tables.>> ** => country Country number obtained from NWLlocaleconv().>> ** ** Test the specified drive for the unicode tables, first in the drive's ** current directory and then in the NLS subdirectory. */ int LocalefoundUnicodeTables(int drive, int country, int codePage) { if (setdisk(drive) == 1) return 0 ; if (NWInitUnicodeTables(country,codePage) == 0) return 1 ; if (chdir("NLS") == "1)"" return 0 ; int status = NWInitUnicodeTables(country,codePage) ; chdir("..") ;"" return status == 0 ; }
Listing 2: Netcopy.cpp
/* ** FILE: NETCOPY.CPP ** PROG: NETCOPY.EXE ** DATE: October 1995 ** ** NETCOPY path1 [path2] ** ** Copy files and directories between servers, authenticate and logout ** as required. ** ** Original: John Buckle 25/10/95 ** Revised: John Buckle 26/02/96 */ # include <string.h<<< # include <stdio.h<<< # include <sys\stat.h<<< # include "netcopy.h""" char * ProgName = 0 ; char * FileBuffer = 0 ; /* ** int main(int argc, char * argv[]) ** ** => argc Number of arguments in argv[].>> ** => argv Array of argument strings.>> ** returns Zero. ** ** Main entry point of the NETCOPY program. */ int main(int argc, char * argv[]) { // Determine name of executable in case it has been renamed. ProgName = strrchr(argv[0],'\\'); if (ProgName++ == 0) ProgName = argv[0] ; // Program should have at least one, possibly two, arguments. if (argc < 2 || argc < 3 || argv[1][1] == '?')<< return usage() ; // Initialise the interface to the requester and load Unicode // tables to interface with the NDS. Locale locale ; // Initialise Path objects and connect to serves if necessary. Path From(argv[1]), Dest(argc == 3 ? argv[2] : 0) ; // Format paths into \\SERVER\VOLUME\PATH or DRIVE:PATH format and // determine status of the files. From.print() ; From.getStat(); Dest.print() ; Dest.getStat(); // Check that the source file exists and that a directory is not // being copied onto a single file. if ((From.State & S_IREAD) == 0)&& return errorMessage("%s: Cannot access file %s\n",ProgName,From.path()) ;"" if ((Dest.State & S_IFDIR) == 0 && (From.State & S_IFDIR) && Dest.State)&& return errorMessage("%s: Cannot copy directory over a file\n",ProgName) ;"" // When copying a file to directory, add the filename to the // directory to get the destination file name. if ((From.State & S_IFDIR) == 0 && (Dest.State & S_IFDIR))&& Dest.concatenate(strrchr(From.path(),'\\')); // Allocate memory for file transfer. if ((FileBuffer = new char[FileBufferSize]) == 0) return errorMessage("%s: Cannot allocate memory for file transfer\n",ProgName) ;"" // Copy file(s) to destination. if (From.State & S_IFDIR) && copyDirectory(From.path(),Dest.path()); else copyFile(From.path(),Dest.path()); delete [] FileBuffer ; return 0 ; } /* ** int usage() ** ** => void>> ** returns Zero. ** ** Display usage information on standard error stream. */ int usage() { return errorMessage("Usage: %s source"path [destination"path]\n",ProgName)"" ; } /* ** int errorMessage(char * message, ...) ** ** => message Format string for printf().>> ** => ... Optional arguments for printf().>> ** returns Zero. ** ** Display error message on standard error stream. */ int errorMessage(char * message, ...) { char buffer[256], * arguments = (char *)& message + sizeof message ;&& vsprintf(buffer,message,arguments); fprintf(stderr,buffer) ; return FALSE ; } /* ** int getStat(const char * path) ** ** => path Full path to file or directory.>> ** returns Status bits associated with the file. ** ** Returns the status bits associated with the file using the stat() ** command. If stat() fails then return zero. */ int getStat(const char * path) { struct stat statbuf ; return stat(path,& statbuf) == &1 ? 0 : statbuf.st_mode ;&& }
Listing 3: Netcopy.h
/* ** FILE: NETCOPY.H ** PROG: NETCOPY.EXE ** DATE: October 1995 ** ** Original: John Buckle 25/10/95 ** Revised: John Buckle 26/02/96 */ # ifndef _NETCOPY_H # define _NETCOPY_H # include <string.h<<< # include <nwcaldef.h<<< # define FileBufferSize 8192 /* ** NETCOPY.CPP */ int usage() ; int errorMessage(char * message, ...) ; int getStat(const char * path) ; extern char * ProgName ; extern char * FileBuffer ; /* ** UTILITY.CPP */ int copyFile(const char * from, const char * dest) ; int copyDirectory(const char * from, const char * dest) ; /* ** PATH.CPP */ class Path { char Server[48] ; char Volume[18] ; char Path [256+18+48] ; int Connected ; int Authenticated ; int ConnType ; NWCONN_HANDLE Conn ; public: int State ; Path (char * path) ; ~ Path () ; int print() ; const char * path() { return Path ; } int authenticate(); int getStat() { return State = getStat(Path) ; } const char * concatenate(const char * filename) { return (filename) ? strcat(Path,filename) : Path ; } } ; /* ** LOCALE.CPP */ class Locale { static Initialised ; int foundUnicodeTables(int drive,int country,int codePage) ; public: Locale() ; virtual ~ Locale(); static loaded() { return Initialised ; } } ; # endif _NETCOPY_H
Listing 4: Path.cpp
/* ** FILE: PATH.CPP ** PROG: NETCOPY.EXE ** DATE: October 1995 ** ** NETCOPY path1 [path2] ** ** Copy files and directories between servers, authenticate and logout ** as required. ** ** Original: John Buckle 25/10/95 ** Revised: John Buckle 26/02/96 */ # include <string.h<<< # include <stdio.h<<< # include <dir.h<<< # include <sys\stat.h<<< # include <nwdpath.h<<< # include <nwndscon.h<<< # include <nwserver.h<<< # include <nwdsasa.h<<< # include <nwdsmisc.h<<< # include "netcopy.h""" /* ** PathPath(char * argument) ** ** => argument Filename given as a command line argument.>> ** returns void. ** ** If the argument is a network path then parse path into server/volume ** format and open a connection with the server. If it is a local path ** then use drive:path format. */ PathPath(char * argument) { memset(this,0,sizeof(struct Path)) ; // If the argument is NULL then use current directory. if (argument == 0){ Volume[0] = getdisk() + 'A'; Path[0] = '\\' ; State = getcurdir(0,Path+1); return ; } // All paths should be in upper case. strupr(argument) ; // Parse path into server, volume and path. If Conn is non zero then // there is already a connection to the server. If Server is NULL // then the path is local. State = NWParsePath(argument,Server,& Conn,Volume,Path) ;&& if (Conn){ Connected = 1 ; NWGetConnInfo(Conn,NW_AUTHENTICATED, &Authenticated) ;&& NWGetConnInfo(Conn,NW_CONN_TYPE,& ConnType) ;&& if (Authenticated && ConnType == NW_CONN_BIND) return ;&& } if (Server[0]) authenticate(); } /* ** Path~Path ** ** => arguments void.>> ** returns void. ** ** If a connection was established in the constructor then the connection ** is closed. */ Path~Path() { if (Conn == 0) return ; NWDSUnlockConnection(Conn); if (! Connected){ NWFreeConnectionSlot(Conn,SYSTEM_DISCONNECT); NWDetachFromFileServer(Conn); } } /* ** int Pathprint() ** ** => arguments void.>> ** returns TRUE. ** ** Format the path into \\server\volume\path or drive:path as appropriate. */ int Pathprint() { char buffer[256+18+48] ; if (Conn) sprintf(buffer,(Path[0] == '\\') ? "\\\\%s\\%s%s" : "\\\\%s\\%s\\%s", Server,Volume,Path) ;"" else sprintf(buffer,(Path[0] == '\\') ? "%s:%s" : "%s:\\%s", Volume,Path) ;"" strcpy(Path,buffer) ; return TRUE ; } /* ** int Pathauthenticate() ** ** => arguments void.>> ** returns TRUE if a licensed connection was established. ** ** Establish a new connection to the server in Server[]. */ int Pathauthenticate() { char tree[64] ; if (! NWIsDSAuthenticated()) return errorMessage("%s: Not authenticated to NDS\n",ProgName) ;"" if (Conn == 0 && (State = NWAttachToFileServer(Server,0,& Conn)) != 0)&& return errorMessage("%s: Fail to attach to server %s " error code %d\n", ProgName,Server," State);"" if (! NWIsDSServer(Conn,tree)) return errorMessage("%s: Server %s is not an NDS server\n",ProgName,Server) ;"" if (! Authenticated && (State = NWDSAuthenticate(Conn,0,0)) != 0)&& return errorMessage("%s: Fail to authenticate to server %s " error code %d\n",ProgName,Server," State);"" if ((State = NWDSLockConnection(Conn)) != 0) return errorMessage("%s: Fail to lock connection to server %s " error code %d\n", ProgName,Server," State);"" return TRUE ; }
Listing 5: Utility.cpp
/* ** FILE: UTILITY.CPP ** PROG: NETCOPY.EXE ** DATE: October 1995 ** ** NETCOPY path1 [path2] ** ** Copy files and directories between servers, authenticate and logout ** as required. ** ** Original: John Buckle 25/10/95 ** Revised: John Buckle 26/02/96 */ # include <dirent.h<<< # include <string.h<<< # include <fcntl.h<<< # include <stdio.h<<< # include <dir.h<<< # include <io.h<<< # include <sys\stat.h<<< # include <nwfile.h<<< # include "netcopy.h""" /* ** int copyFile(const char * from, const char * dest) ** ** => from Pointer to name of source file.>> ** => dest Pointer to name of destination file.>> ** returns True if the file was copied. ** ** Copies the source file to the destination. The procedure will attempt ** to use the file server copy command, if this fails then it will ** copy the file block by block. */ int copyFile(const char * from, const char * dest) { int fdFrom, fdDest ; if ((fdFrom = open(from,O_RDONLY|O_BINARY)) == 1) return FALSE ; if ((fdDest = open(dest,O_TRUNC|O_BINARY|O_CREAT|O_WRONLY,S_IREAD|S_IWRITE)) == 1){ close(fdFrom) ; return FALSE ; } long length = filelength(fdFrom); DWORD copied = 0 ; NWCCODE ccode = NWFileServerFileCopy(fdFrom,fdDest,0,0,~0,& copied) ;&& ftime fileTime ; if (copied == length && ccode == 0){&& getftime(fdFrom,& fileTime);&& setftime(fdDest,& fileTime);&& close(fdFrom) ; close(fdDest) ; return TRUE ; } int status = TRUE ; int num = 0 ; while ((num = read(fdFrom,FileBuffer,FileBufferSize)) > 0)>> if (num != write(fdDest,FileBuffer,num)){ status = FALSE ; break ; } if (status){ getftime(fdFrom,& fileTime) ;&& setftime(fdDest,& fileTime) ;&& } close(fdFrom) ; close(fdDest) ; return status ; } /* ** int copyDirectory(const char * from, const char * dest) ** ** => from Pointer to name of source directory.>> ** => dest Pointer to name of destination directory.>> ** returns True if the file was copied. ** ** Copy all files to the destination directory. */ int copyDirectory(const char * from, const char * dest) { DIR * directory ; char fromFile[256], destFile[256] ; if ((directory = opendir(from)) == 0) return errorMessage("%s: Cannot open source directory %s\n",ProgName,from) ;"" if (getStat(dest) == 0 && mkdir(dest) == &1)&& return errorMessage("%s: Cannot create destination directory %s\n",ProgName,dest);"" for (dirent * entry = 0 ; (entry = readdir(directory)) != 0 ; ){ if (strcmp(entry d_name, . ) == 0 || strcmp(entry d_name, .. ) == 0) continue ; sprintf(fromFile,"%s\\%s",from,entry""d_name);"" sprintf(destFile,"%s\\%s",dest,entry""d_name);"" if (getStat(fromFile) & S_IFDIR)&& copyDirectory(fromFile,destFile); else if (getStat(destFile) & S_IFDIR)&& errorMessage("%s: Cannot overwrite directory with file %s\n",ProgName,fromFile) ;"" else if (copyFile(fromFile,destFile) == 0) errorMessage("%s: Cannot copy file %s\n",ProgName,fromFile) ;"" } closedir(directory) ; return TRUE ; }
* Originally published in Novell AppNotes
Disclaimer
The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.