blob: bbd67bafd60a23c6b18fcad7aa052203ee62937a [file] [log] [blame]
/*
* Copyright (c) 1989, 1990, 1991 by the University of Washington
*
* For copying and distribution information, please see the file
* <copyright.h>.
*
* v1.2.2 - 11/19/91 (mmt) - added MSDOS & OS2 stuff
* v1.2.1 - 10/23/91 (bpk) - added missing code
* v1.2.0 - 09/17/91 (bpk) - added new get_vdir code
* v1.1.1 - 08/30/91 (bpk) - cast rindex()
*/
#include <copyright.h>
#include <stdio.h>
#include <pfs.h>
#include <pprot.h>
#include <perrno.h>
#include <pcompat.h>
#include <pauthent.h>
#include <pmachine.h>
#ifdef NEED_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
#ifdef DEBUG
extern int pfs_debug;
#endif
extern int pwarn;
extern char p_warn_string[];
extern int perrno;
extern char p_err_string[];
extern int wcmatch(char *s,char *template);
extern int vl_insert(VLINK vl,VDIR1 vd,int allow_conflict);
extern int ul_insert(VLINK ul,VDIR1 vd,VLINK p);
extern int scan_error(char *erst);
/*
* get_vdir - Get contents of a directory given its location
*
* GET_VDIR takes a directory location, a list of desired
* components, a pointer to a directory structure to be
* filled in, and flags. It then queries the appropriate
* directory server and retrieves the desired information.
*
* ARGS: dhost - Host on which directory resides
* dfile - Directory on that host
* components - The names from the directory we want
* dir - Structure to be filled in
* flags - Options. See FLAGS
* filters - filters to be applied to result
* acomp - Pointer to remaining components
*
* FLAGS: GVD_UNION - Do not expand union links
* GVD_EXPAND - Expand union links locally
* GVD_REMEXP - Request remote expansion (& local if refused)
* GVD_LREMEXP - Request remote expansion of local union links
* GVD_VERIFY - Only verify that args are for a directory
* GVD_ATTRIB - Request attributes from directory server
* GVD_NOSORT - Do not sort links when adding to directory
*
* RETURNS: PSUCCESS (0) or error code
* On some codes addition information in p_err_string
*
* NOTES: If acomp is non-null the string it points to might be modified
*
* If the directory passed as an argument already has
* links or union links, then those lists will be freed
* before the new contents are filled in.
*
* If a filter is passed to the procedure, and application of
* the filter results in additional union link, then those links
* will (or will not) be expanded as specified in the FLAGS field.
*
* If the list of components in NULL, or the null string, then
* get_vdir will return all links in the requested directory.
*
* BUGS: Doesn't process union links yet
* Doesn't process errors returned from server
* Doesn't expand union links if requested to
*/
int get_vdir(char *dhost,char *dfile,char *components,VDIR1 dir,long flags,
VLINK filters,char *acomp)
{
PTEXT request; /* Text of request to dir server */
PTEXT resp; /* Response from dir server */
char ulcomp[MAX_VPATH];/* Work space for new current component */
char *comp = components;
VLINK cur_link = NULL;/* Current link being filled in */
VLINK exp = NULL; /* The current ulink being expanded */
VLINK pul = NULL; /* Prev union link (insert new one after it) */
VLINK l; /* Temp link pointer */
int mcomp; /* Flag - check multiple components */
int unresp; /* Flag - received unresolved response */
int getattrib = 0; /* Get attributes from server */
int vl_insert_flag; /* Flags to vl_insert */
int fwdcnt = MAX_FWD_DEPTH;
int no_links = 0; /* Count of number of links found */
char options[40]; /* LIST option */
char *opt; /* After leading + */
PAUTH authinfo;
/* Treat null string like NULL (return entire directory) */
if(!components || !*components) comp = NULL;
if(acomp && !filters) mcomp = 1;
else mcomp = 0;
if(flags&GVD_ATTRIB) {
getattrib++;
flags &= (~GVD_ATTRIB);
}
if(flags&GVD_NOSORT) vl_insert_flag = VLI_NOSORT;
else vl_insert_flag = VLI_ALLOW_CONF;
flags &= (~GVD_NOSORT);
if(filters) comp = NULL;
perrno = 0;
authinfo = get_pauth(PFSA_UNAUTHENTICATED);
*options = '\0';
if(getattrib) {
strcat(options,"+ATTRIBUTES");
flags &= (~GVD_ATTRIB);
}
if(!filters) { /* Can't do remote expansion if filters to be applied */
if(flags == GVD_REMEXP) strcat(options,"+EXPAND");
if(flags == GVD_LREMEXP) strcat(options,"+LEXPAND");
}
/* If all we are doing is verifying that dfile is a directory */
/* then we do not want a big response from the directory */
/* server. A NOT-FOUND is sufficient. */
if(flags == GVD_VERIFY)
#ifdef NEWVERIFYOPT
strcat(options,"+VERIFY");
#else
comp = "%#$PRobably_nOn_existaNT$#%";
#endif
if(*options) opt = options+1;
else opt = "''";
startover:
request = ptalloc();
sprintf(request->start,
"VERSION %d\nAUTHENTICATOR %s %s\nDIRECTORY ASCII %s\nLIST %s COMPONENTS %s%s%s\n",
VFPROT_VNO, authinfo->auth_type, authinfo->authenticator,
dfile,opt, (comp ? comp : ""), (mcomp ? "/" : ""),
(mcomp ? acomp : ""));
request->length = strlen(request->start);
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Sending message to dirsrv:\n%s",request->start);
#endif
#if defined(MSDOS)
resp = dirsend(request,dhost,0L);
#else
resp = dirsend(request,dhost,0);
#endif
#ifdef DEBUG
if(pfs_debug && (resp == NULL)) {
fprintf(stderr,"Dirsend failed: %d\n",perrno);
}
#endif
/* If we don't get a response, then if the requested */
/* directory, return error, if a ulink, mark it unexpanded */
if(resp == NULL) {
if(exp) exp->expanded = FAILED;
else return(perrno);
}
unresp = 0;
/* Here we must parse reponse and put in directory */
/* While looking at each packet */
while(resp) {
PTEXT vtmp;
char *line;
vtmp = resp;
#ifdef DEBUG
if(pfs_debug > 3) fprintf(stderr,"%s\n",resp->start);
#endif
/* Look at each line in packet */
for(line = resp->start;line != NULL;line = nxtline(line)) {
switch (*line) {
/* Temporary variables to hold link info */
char l_linktype;
char l_name[MAX_DIR_LINESIZE];
char l_type[MAX_DIR_LINESIZE];
char l_htype[MAX_DIR_LINESIZE];
char l_host[MAX_DIR_LINESIZE];
char l_ntype[MAX_DIR_LINESIZE];
char l_fname[MAX_DIR_LINESIZE];
int l_version;
char t_unresolved[MAX_DIR_LINESIZE];
int l_magic;
int tmp;
case 'L': /* LINK or LINK-INFO */
if(strncmp(line,"LINK-INFO",9) == 0) {
PATTRIB at;
PATTRIB last_at;
at = parse_attribute(line);
if(!at) break;
/* Cant have link info without a link */
if(!cur_link) {
perrno = DIRSRV_BAD_FORMAT;
atfree(at);
break;
}
if(cur_link->lattrib) {
last_at = cur_link->lattrib;
while(last_at->next) last_at = last_at->next;
at->previous = last_at;
last_at->next = at;
}
else {
cur_link->lattrib = at;
at->previous = NULL;
}
break;
}
/* Not LINK-INFO, must be LINK - if not check for error */
if(strncmp(line,"LINK",4) != 0) goto scanerr;
/* If only verifying, don't want to change dir */
if(flags == GVD_VERIFY) {
break;
}
/* If first link and some links in dir, free them */
if(!no_links++) {
if(dir->links) vllfree(dir->links); dir->links=NULL;
if(dir->ulinks) vllfree(dir->ulinks); dir->ulinks=NULL;
}
cur_link = vlalloc();
/* parse and insert file info */
tmp = sscanf(line,"LINK %c %s %s %s %s %s %s %d %d", &l_linktype,
l_type, l_name, l_htype, l_host,
l_ntype, l_fname, &(cur_link->version),
&(cur_link->f_magic_no));
if(tmp != 9) {
perrno = DIRSRV_BAD_FORMAT;
vlfree(cur_link);
break;
}
cur_link->linktype = l_linktype;
cur_link->type = stcopyr(l_type,cur_link->type);
cur_link->name = stcopyr(unquote(l_name),cur_link->name);
cur_link->hosttype = stcopyr(l_htype,cur_link->hosttype);
cur_link->host = stcopyr(l_host,cur_link->host);
cur_link->nametype = stcopyr(l_ntype,cur_link->nametype);
cur_link->filename = stcopyr(l_fname,cur_link->filename);
/* Double check to make sure we don't get */
/* back unwanted components */
/* OK to keep if special (URP) links */
/* or if mcomp specified */
if(!mcomp && (cur_link->linktype == 'L') &&
(!wcmatch(cur_link->name,comp))) {
vlfree(cur_link);
break;
}
/* If other optional info was sent back, it must */
/* also be parsed before inserting link *** */
if(cur_link->linktype == 'L')
vl_insert(cur_link,dir,vl_insert_flag);
else {
tmp = ul_insert(cur_link,dir,pul);
/* If inserted after pul, next one after cur_link */
if(pul && (!tmp || (tmp == UL_INSERT_SUPERSEDING)))
pul = cur_link;
}
break;
case 'F': /* FILTER, FAILURE or FORWARDED*/
/* FORWARDED */
if(strncmp(line,"FORWARDED",9) == 0) {
if(fwdcnt-- <= 0) {
ptlfree(resp);
perrno = PFS_MAX_FWD_DEPTH;
return(perrno);
}
/* parse and start over */
tmp = sscanf(line,"FORWARDED %s %s %s %s %d %d",
l_htype,l_host,l_ntype,l_fname,
&l_version, &l_magic);
dhost = stcopy(l_host);
dfile = stcopy(l_fname);
if(tmp < 4) {
perrno = DIRSRV_BAD_FORMAT;
break;
}
ptlfree(resp);
goto startover;
}
if(strncmp(line,"FILTER",6) != 0) goto scanerr;
break;
case 'M': /* MULTI-PACKET (processed by dirsend) */
case 'P': /* PACKET (processed by dirsend) */
break;
case 'N': /* NOT-A-DIRECTORY or NONE-FOUND */
/* NONE-FOUND, we just have no links to insert */
/* It is not an error, but we must clear any */
/* old links in the directory arg */
if(strncmp(line,"NONE-FOUND",10) == 0) {
/* If only verifying, don't want to change dir */
if(flags == GVD_VERIFY) {
break;
}
/* If first link and some links in dir, free them */
if(!no_links++) {
if(dir->links) vllfree(dir->links);
if(dir->ulinks) vllfree(dir->ulinks);
dir->links = NULL;
dir->ulinks = NULL;
}
break;
}
/* If NOT-A-DIRECTORY or anything else, scan error */
goto scanerr;
case 'U': /* UNRESOLVED */
if(strncmp(line,"UNRESOLVED",10) != 0) {
goto scanerr;
}
tmp = sscanf(line,"UNRESOLVED %s", t_unresolved);
if(tmp < 1) {
perrno = DIRSRV_BAD_FORMAT;
break;
}
/* If multiple components were resolved */
if(strlen(t_unresolved) < strlen(acomp)) {
strcpy(ulcomp,acomp);
/* ulcomp is the components that were resolved */
*(ulcomp+strlen(acomp)-strlen(t_unresolved)-1) = '\0';
/* Comp gets the last component resolved */
comp = strrchr(ulcomp,(int) '/');
if(comp) comp++;
else comp = ulcomp;
/* Let rd_vdir know what remains */
strcpy(acomp,t_unresolved);
}
unresp = 1;
break;
case 'V': /* VERSION-NOT-SUPPORTED */
if(strncmp(line,"VERSION-NOT-SUPPORTED",21) == 0) {
perrno = DIRSRV_BAD_VERS;
return(perrno);
}
goto scanerr;
scanerr:
default:
if(*line && (tmp = scan_error(line))) {
ptlfree(resp);
return(tmp);
}
break;
}
}
resp = resp->next;
ptfree(vtmp);
}
/* We sent multiple components and weren't told any */
/* were unresolved */
if(mcomp && !unresp) {
/* ulcomp is the components that were resolved */
strcpy(ulcomp,acomp);
/* Comp gets the last component resolved */
comp = strrchr(ulcomp,(int) '/');
if(comp) comp++;
else comp = ulcomp;
/* If we have union links to resolve, only one component remains */
mcomp = 0;
/* Let rd_vdir know what remains */
*acomp = '\0';
}
/* If only verifying, we already know it is a directory */
if(flags == GVD_VERIFY) return(PSUCCESS);
/* Don't return if matching was delayed by the need to filter */
/* if FIND specified, and dir->links is non null, then we have */
/* found a match, and should return. */
if((flags & GVD_FIND) && dir->links && (!filters))
return(PSUCCESS);
/* If expand specified, and ulinks must be expanded, making sure */
/* that the order of the links is maintained properly */
expand_ulinks:
if((flags != GVD_UNION) && (flags != GVD_VERIFY)) {
l = dir->ulinks;
/* Find first unexpanded ulink */
while(l && l->expanded && (l->linktype == 'U')) l = l->next;
/* Only expand if a FILE or DIRECTORY - Mark as */
/* failed otherwise */
/* We must still add support for symbolic ulinks */
if(l) {
if ((strcmp(l->type,"DIRECTORY") == 0) ||
(strcmp(l->type,"FILE") == 0)) {
l->expanded = TRUE;
exp = l;
pul = l;
dhost = l->host;
dfile = l->filename;
goto startover; /* was get_contents; */
}
else l->expanded = FAILED;
}
}
/* Double check to make sure we don't get */
/* back unwanted components */
/* OK to keep if special (URP) links */
if(components && *components) {
l = dir->links;
while(l) {
VLINK ol;
if((l->linktype == 'L') && (!wcmatch(l->name,components))) {
if(l == dir->links)
dir->links = l->next;
else l->previous->next = l->next;
if(l->next) l->next->previous = l->previous;
ol = l;
l = l->next;
vlfree(ol);
}
else l = l->next;
}
}
return(PSUCCESS);
}