Novell is now a part of Micro Focus

Using NDS Connection APIs To Write Enterprise Enabled Applications

Articles and Tips: article

JOHN BUCKLE
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.

© Copyright Micro Focus or one of its affiliates