archie/prospero/server/dirsrv_v1.c
2024-05-27 16:13:40 +02:00

1136 lines
39 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 1992, 1993, 1994 by the University of Southern California
*
* For copying and distribution information, please see the file
* <usc-license.h>
*/
/*
* Changed by swa, Jan 1994:
* This file will now only support the DIRECTORY, VERSION, AUTHENTICATOR,
* and LIST commands. These are all that is needed to support old Archie
* clients.
*/
/* Changed by swa, Mar, 1994: Fixed prototypes to be posix. */
#include <usc-license.h>
#include <netdb.h>
#include <sgtty.h>
#include <posix_signal.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h> /* needed by SCO */
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <pmachine.h>
#include <pserver.h>
#ifdef SERVER_SUPPORT_V1 /* This entire file exists only so that the
server can support Version 1 of the Prospero
Protocol. */
#include <ardp.h>
#include <pfs.h>
#include <psrv.h>
#include <plog.h>
#include <pprot.h>
#include <perrno.h>
#include "dirsrv.h"
/* PPROT.H used to include this definition, but it doesn't any more. It's now
moved. */
/* This macro is now officially vestigial. */
/* Replacement for strtok that doesn't keep state. Both the variable */
/* S and the variable S_next must be defined. To initialize, assign */
/* the string to be stepped through to S_next, then call get_token on */
/* S. The first token will be in S, and S_next will point to the next */
/* token. Like strtok, this macro does modify the string passed to it */
#define get_token(S,C) \
do { \
S = S##_next; \
if(S) { \
while(*S == C) S++; \
S##_next = strchr(S,C); \
if(S##_next) *(S##_next++) = '\0'; \
if(!*S) S = NULL; \
} \
} while (0)
static char *quote();
static char *v1_check_nfs();
static int v1_externalize(VLINK vl);
static int reply_v1(RREQ req, char *buf);
static void flush_pending_v1_reply(void);
static int creply_v1(RREQ req, char *buf);
static int replyf_v1(RREQ req, char *format, ...);
static int creplyf_v1(RREQ req, char *format, ...);
static int vreplyf_v1(RREQ req, char *format, va_list ap);
static int error_reply_v1(RREQ req, char *format, ...);
static int haswhite(char *s);
static int hasnl(char *s);
VLINK check_fwd();
static v1_cmd_lookup();
extern char *unquote(), *unquoten();
static TOKEN tokenize(char *);
/* link TARGET of SYMBOLIC must be changed to SYM-LINK for V1 protocol. */
#define symlinkify(foo) (((foo)[0] == 'S') ? "SYM-LINK" : (foo))
/* This will be called by dirsrv() to process a Protocol Version 1 format
packet. It is old code which can have its internal buffers easily
over-written, but that's life. It also won't return all of the information
that the newer versions of the protocol will. Tough noogies. */
int
dirsrv_v1(RREQ req, char *command_next)
{
long client_host;
/* The following are set in one line and used by subsequent */
/* lines of the same message */
int client_version = MAX_VERSION; /* Protocol version nbr */
char authent[160]; /* Authentication data */
char client_id[160]; /* Authenticated username */
char client_dir[MAXPATHLEN]; /* Current directory */
long dir_version = -1; /* Directory version nbr */
long dir_magic_no = -1; /* Directory magic number */
#ifdef ARCHIE
int max_list_commands = 5; /* Max lists in request */
#endif /* ARCHIE */
/* The following are used while processing the current line */
char *command; /* The current line */
/* command_next is The next line */
int cmd_code; /* Operation code */
PFILE fi; /* individual lines */
VLINK fl; /* List of forwarding pointers */
VLINK fp; /* The current fp */
char *this_comp; /* component being worked on*/
TOKEN additional_comps; /* additional components to be processed. */
#if 0
AUTOSTAT_TYPEP(TOKEN,tokenlist_to_freep);
#endif
#if 0
char *components; /* Components to be processed */
char *remcomp; /* Remaining components */
#endif
char localexp; /* OK to exp ul for remcomp */
VLINK uexp; /* Current link being expanded */
char attribfl; /* Send back atribues in list */
int item_count = 0; /* Count of returned items */
/* Temporaries */
char dir_type[40]; /* Type or dir name (ASCII) */
char *amarg; /* Arguments for access method */
VLINK clink; /* For stepping through links */
VLINK crep; /* For stepping through replicas*/
FILTER cfil; /* For stepping through filters */
PATTRIB ca; /* Current Attribute */
char *suffix; /* Trailing component(s) */
int rsinfo_ret; /* Ret Val from dsrfinfo */
int verify_dir; /* Only verifying the directory */
int retval;
int tmp;
int i;
int dsdb_options = 0;
int lpriv; /* LPRIV option for CREATE-DIR */
ACL wacl; /* Working access control list */
int laclchkl; /* Cached ACL check */
int daclchkl; /* Cached ACL check */
int laclchkr; /* Cached ACL check */
int daclchkr; /* Cached ACL check */
int aclchk; /* Cached ACL check */
ACL nacl; /* New ACL entry */
/* Temporaries for use by sscanf */
char t_ltype;
char t_name[MAX_DIR_LINESIZE];
char t_type[MAX_DIR_LINESIZE];
char t_htype[MAX_DIR_LINESIZE];
char t_host[MAX_DIR_LINESIZE];
char t_ntype[MAX_DIR_LINESIZE];
char t_fname[MAX_DIR_LINESIZE];
char t_options[MAX_DIR_LINESIZE];
char t_acetype[MAX_DIR_LINESIZE];
char t_atype[MAX_DIR_LINESIZE];
char t_rights[MAX_DIR_LINESIZE];
char t_principals[MAX_DIR_LINESIZE];
int t_num;
int n_options;
char insrights[MAX_DIR_LINESIZE];
char qatype[MAX_DIR_LINESIZE];
char qrights[MAX_DIR_LINESIZE];
struct dsrobject_list_options listopts;
P_OBJECT ob = NULL; /* Used in LIST only. */
if(req->peer_ardp_version > -1) req->peer_ardp_version = -2;
client_host = req->peer.sin_addr.s_addr;
*client_dir = '\0';
strcpy(authent,"NONE");
strcpy(client_id,"");
get_token(command,'\n'); /* Defined above */
while(command) {
cmd_code = v1_cmd_lookup(command);
switch(cmd_code) {
case VERSION:
tmp = sscanf(command,"VERSION %d",&client_version);
if(tmp != 1) {
replyf_v1(req,"VERSION %d %s\n", MAX_VERSION,PFS_SW_ID);
} else {
if (client_version != 1)
creplyf_v1(req,"ERROR Cannot specify two different \
versions at the same time.\n",command);
plog(L_DIR_ERR, req, "Received VERSION 1 and VERSION %d \
messages in same Prospero request.", client_version);
RETURNPFAILURE;
}
break;
case AUTHENTICATOR: {
char auth_type[40]; /* Type of Authentication */
PAUTH patmp;
tmp = sscanf(command,"AUTHENTICATOR %s %s",auth_type,authent);
if(tmp != 2) {
creplyf_v1(req,"ERROR Invalid arguments: %s\n",command);
plog(L_DIR_PERR, req,
"Invalid AUTHENTICATOR command: %s", command);
RETURNPFAILURE;
}
if(strcmp(auth_type,"UNAUTHENTICATED")) {
creplyf_v1(req,"ERROR authentication type %s not supported\n",
auth_type);
plog(L_DIR_ERR,req,"Invalid auth-type %s: %s",
auth_type,command);
RETURNPFAILURE;
}
/* This memory freed by rdgram transmit() function. */
patmp = paalloc();
patmp->next = req->auth_info;
req->auth_info = patmp;
strcpy(client_id,authent);
req->client_name = stcopyr(authent,req->client_name);
patmp->principals = tkalloc(client_id);
patmp->ainfo_type = PFSA_UNAUTHENTICATED;
}
break;
case DIRECTORY:
dir_version = 0;
dir_magic_no = 0;
tmp = sscanf(command,"DIRECTORY %s %s %ld %ld",
dir_type,client_dir,&dir_version,&dir_magic_no);
if(tmp < 2) {
creplyf_v1(req,"ERROR Invalid arguments: %s\n",command);
plog(L_DIR_PERR,req,
"Invalid DIRECTORY command: %s",command);
RETURNPFAILURE;
}
if(strcmp(dir_type,"ASCII")) {
creplyf_v1(req,"ERROR id-type %s not supported\n",dir_type);
plog(L_DIR_ERR,req,"Invalid id-type: %s",
command);
RETURNPFAILURE;
}
if(check_handle(client_dir) == FALSE) {
creply_v1(req,"FAILURE NOT-AUTHORIZED\n");
plog(L_AUTH_ERR,req,
"Invalid directory name: %s",client_dir);
RETURNPFAILURE;
}
break;
/* This is actually part of the LIST command. */
/* This is part we don't bother really making work. */
more_comps:
/* Set the directory for the next component */
/* At this point, clink contains the link for the next */
/* directory, and the directory itself is still filled */
/* in. We should save away the directory information, */
/* then free what remains */
dir_version = clink->version;
dir_magic_no = clink->f_magic_no;
strcpy(dir_type,clink->hsonametype);
strcpy(client_dir,clink->hsoname);
if(strcmp(dir_type,"ASCII")) {
creplyf_v1(req,"ERROR id-type %s not supported\n",dir_type);
plog(L_DIR_ERR,req,"Invalid id-type: %s",
command);
obfree(ob);
ob = NULL;
RETURNPFAILURE;
}
if(check_handle(client_dir) == FALSE) {
creply_v1(req,"FAILURE NOT-AUTHORIZED\n");
plog(L_AUTH_ERR,req,
"Invalid directory name: %s",client_dir);
obfree(ob);
ob = NULL;
RETURNPFAILURE;
}
assert(additional_comps && additional_comps->token);
this_comp = additional_comps->token;
additional_comps = additional_comps->next;
obfree(ob);
ob = oballoc(); /* reinitialize a clean object to continue
with. */
goto continue_list;
case LIST:
list_count++;
#ifdef ARCHIE
if(max_list_commands-- <= 0) {
creply_v1(req,"FAILURE NOT-AUTHORIZED Too many list commands in a single request\n");
plog(L_AUTH_ERR,req,"Too many list commands");
RETURNPFAILURE;
}
#endif /* ARCHIE */
if (ob) {
obfree(ob);
}
ob = oballoc();
tmp = sscanf(command,"LIST %s COMPONENTS %[^\n]",
t_options, t_name);
if (tmp < 1) {
plog(L_DIR_PERR,req,
"Misformatted V1 LIST command: %s",command);
creplyf_v1(req,"ERROR mis-formatted LIST command: %s\n",command);
obfree(ob);
ob = NULL;
RETURNPFAILURE;
}
/* Now we know that t_options were set. */
/* If no options, parse again */
if(strcmp(t_options,"COMPONENTS")==0) {
tmp = 1 + sscanf(command,"LIST COMPONENTS %[^\n]", t_name);
}
/* t_name now will contain a meaningful components list IFF
(tmp == 2) */
if((tmp < 2)
|| ((additional_comps = p__slashpath2tkl(t_name))
== NULL)) {
this_comp = "*";
additional_comps = NULL;
strcpy(t_name, "*"); /* do this for the sake of the log message
below. */
} else {
/* Will be freed upon receiving the next new LIST command in
this particular thread. This thereby stops any potential
memory leak. */
#ifdef PFS_THREADS
if (*tokenlist_to_freep) tklfree(*tokenlist_to_freep);
*tokenlist_to_freep = additional_comps;
#endif
/* additional_comps must be non-NULL, by what's above. */
this_comp = additional_comps->token;
additional_comps = additional_comps->next;
}
/* t_name now contains a meaningful name, for the log message
below. */
if(strstr(t_options,"VERIFY")) verify_dir = 1;
else verify_dir = 0;
#ifndef DONTSUPPORTOLD
if(strequal(this_comp,"%#$PRobably_nOn_existaNT$#%")) {
this_comp = "*";
verify_dir = 1;
}
#endif
/* If EXPAND specified, remeber that fact */
if(strstr(t_options,"EXPAND") || strstr(t_options,"LEXPAND"))
localexp = 2;
else localexp = 0;
if(strstr(t_options,"ATTRIBUTES")) attribfl = DSRD_ATTRIBUTES;
else attribfl = 0;
plog(L_DIR_REQUEST, req, "L%s %s %s",
(verify_dir ? "V" : " "), client_dir, t_name);
/* Here's where we start to resolve additional components */
continue_list:
uexp = NULL;
/* If only expanding last component, clear the flag */
if(localexp == 1) localexp = 0;
/* If remaining components, expand for this component only */
if(additional_comps && !localexp) localexp = 1;
exp_ulink:
p_clear_errors();
/* From new V5 list.c */
listopts.thiscompp = &this_comp;
listopts.remcompp = &additional_comps;
listopts.requested_attrs = "#INTERESTING";
listopts.filters = NULL; /* no server-side filters in V1 */
retval = dsrobject(req, dir_type, client_dir, 0, dir_magic_no,
verify_dir? DRO_VERIFY : 0, &listopts, ob);
/* If forwarded, just say so and go on. No need to continue. */
if (retval == DSRFINFO_FORWARDED) {
replyf_v1(req,"FORWARDED\n");
break;
#if 0
if (remcomp && *remcomp) {
fl = dir->f_info->forward; dir->f_info->forward = NULL;
fp = check_fwd(fl,client_dir,dir_magic_no);
vdir_freelinks(dir);
if(fp) {
if (haswhite(fp->hosttype)
|| haswhite(fp->host) ||
haswhite(fp->hsonametype) ||
haswhite(fp->hsoname)) {
flush_pending_v1_reply();
creply_v1(req, "FAILURE OUT-OF-DATE Cannot \
represent place object was forwarded to in v1 of Prospero protocol.\n");
RETURNPFAILURE;
} else
replyf_v1(req,"LINK L DIRECTORY %s %s %s %s %s %d %d\n",
quote(components),
fp->hosttype,fp->host,fp->hsonametype,
fp->hsoname, fp->version,fp->f_magic_no);
p__tkl_back_2slashpath(additional_comps, t_name)
replyf_v1(req, "UNRESOLVED %s/%s\n", this_comp,
t_name);
}
else replyf_v1(req,"FORWARDED\n");
vllfree(fl);
break;
} else {
goto dforwarded;
}
#endif
}
/* If not a directory, say so */
if(retval == DSRDIR_NOT_A_DIRECTORY) {
creply_v1(req,"FAILURE NOT-A-DIRECTORY\n");
obfree(ob);
ob = NULL;
RETURNPFAILURE;
}
/* If unauthorized, say so */
if(retval == DIRSRV_NOT_AUTHORIZED) {
if(p_err_string && *p_err_string)
creplyf_v1(req,"FAILURE NOT-AUTHORIZED %s\n",p_err_string);
else creplyf_v1(req,"FAILURE NOT-AUTHORIZED\n");
obfree(ob);
ob = NULL;
RETURNPFAILURE;
}
/* If some other failure, say so */
if(retval) {
creplyf_v1(req,"FAILURE SERVER-FAILED\n");
obfree(ob);
ob = NULL;
RETURNPFAILURE;
}
/* Cache the default answers for ACL checks */
daclchkl = srv_check_acl(ob->acl, ob->acl, req, "l",
SCA_LINKDIR,client_dir,NULL);
daclchkr = srv_check_acl(ob->acl, ob->acl, req, "r",
SCA_LINKDIR,client_dir,NULL);
/* Here we must send back the links, excluding those that do */
/* not match the component name. For each link, we must also */
/* send back any replicas or links with conflicting names */
clink = ob->links;
while(clink) {
crep = clink;
while(crep) {
/* If ->expanded set means we already returned it */
if(crep->expanded) {
crep = crep->next;
continue;
}
/* Check individual ACL only if necessary */
laclchkl = daclchkl; laclchkr = daclchkr;
if(crep->acl) {
laclchkl = srv_check_acl(crep->acl,ob->acl,req,"l",
SCA_LINK,NULL,NULL);
laclchkr = srv_check_acl(crep->acl,ob->acl,req,"r",
SCA_LINK,NULL,NULL);
}
if(!verify_dir && wcmatch(crep->name,this_comp) &&
(laclchkl || (laclchkr &&
(strcmp(crep->name,this_comp)==0)))) {
if(laclchkr) {
#if 0
/* servers not required to resolve multi-comp names. */
if(remcomp && !strcmp(crep->host,hostwport) &&
!(crep->filters) && !item_count) {
/* If components remain on this host */
/* don't reply, but continue searching */
goto more_comps;
}
#endif
/* Only display this link if it can be converted to
V1 EXTERNAL format. */
if((*crep->target == 'E'
&& v1_externalize(crep) != PSUCCESS) ||
haswhite(crep->target) ||
haswhite(crep->name) ||
haswhite(crep->hosttype) ||
haswhite(crep->host) ||
haswhite(crep->hsonametype) ||
haswhite(crep->hsoname)) {
flush_pending_v1_reply();
goto next_crep;
}
replyf_v1(req,"LINK L %s %s %s %s %s %s %d %d\n",
symlinkify(crep->target),
quote(crep->name),
crep->hosttype, crep->host,
crep->hsonametype, crep->hsoname,
crep->version,crep->f_magic_no);
item_count++;
} else {
if (haswhite(crep->name)) {
flush_pending_v1_reply();
goto next_crep;
}
replyf_v1(req, "LINK L NULL %s NULL NULL NULL NULL 0 0\n", quote(crep->name));
item_count++;
}
/* Using ->expanded to indicate returned */
crep->expanded = TRUE;
/* If link attributes are to be returned, do so */
/* For now, only link attributes returned */
ca = crep->lattrib;
while(ca && attribfl) {
/* For now return all attributes. To be done: */
/* return only those requested */
TOKEN tk;
if(ca->avtype == ATR_SEQUENCE
&& ca->value.sequence
/* skip access-method attribute, since it's
not used. */
&& !strequal(ca->aname, "ACCESS-METHOD")) {
if (haswhite(ca->aname)) {
flush_pending_v1_reply();
goto listnextat;
}
else
replyf_v1(req,"LINK-INFO %s %s ASCII",
((ca->precedence==ATR_PREC_LINK) ? "LINK":
((ca->precedence==ATR_PREC_REPLACE)? "REPLACEMENT":
((ca->precedence==ATR_PREC_ADD) ? "ADDITIONAL":
"CACHED"))),
ca->aname);
for (tk = ca->value.sequence; tk; tk = tk->next)
if (hasnl(tk->token)) {
flush_pending_v1_reply();
goto listnextat;
} else
replyf_v1(req, " %s", tk->token);
reply_v1(req, "\n");
}
listnextat:
ca = ca->next;
}
#if 0 /* We don't need to support this for backwards
compatability. */
/* if there are any filters, send them back too */
cfil = crep->filters;
while(cfil && laclchkr) {
filter_reply_v1(req, cfil, 0);
cfil = cfil->next;
}
#endif
}
/* Replicas are linked through next, not replicas */
/* But the primary link is linked to the replica */
/* list through replicas */
next_crep:
if(crep == clink) crep = crep->replicas;
else crep = crep->next;
}
clink = clink->next;
}
/* here we must send back the unexpanded union links */
clink = ob->ulinks;
while(clink && !verify_dir) {
if(!clink->expanded &&
srv_check_acl(clink->acl,ob->acl,req,"r",
SCA_LINK,NULL,NULL)) {
if(localexp && !(clink->filters) &&
!strcmp(clink->host,hostwport)) {
/* Set the directory for the next component */
/* At this point, clink contains the link */
/* for the next directory */
dir_version = clink->version;
dir_magic_no = clink->f_magic_no;
strcpy(dir_type,clink->hsonametype);
strcpy(client_dir,clink->hsoname);
if(strcmp(dir_type,"ASCII")) {
creplyf_v1(req,"ERROR id-type %s not supported\n",
dir_type);
plog(L_DIR_ERR,req,
"Invalid id-type: %s", command);
obfree(ob);
ob = NULL;
RETURNPFAILURE;
}
if(check_handle(client_dir) == FALSE) {
creplyf_v1(req,"FAILURE NOT-AUTHORIZED\n");
plog(L_AUTH_ERR,req,
"Invalid directory name: %s",client_dir);
obfree(ob);
ob = NULL;
RETURNPFAILURE;
}
clink->expanded = TRUE;
uexp = clink;
goto exp_ulink;
}
/* Don't do any more expanding */
localexp = 0;
/* Union links; don't need to v1_externalize. */
if (haswhite(clink->target) ||
haswhite(clink->name) ||
haswhite(clink->hosttype) ||
haswhite(clink->host) ||
haswhite(clink->hsonametype) ||
haswhite(clink->hsoname)) {
flush_pending_v1_reply();
goto listnextulink;
} else {
replyf_v1(req,"LINK %c %s %s %s %s %s %s %d %d\n",
clink->linktype,
symlinkify(clink->target), quote(clink->name),
clink->hosttype, clink->host,
clink->hsonametype,clink->hsoname,
clink->version,clink->f_magic_no);
item_count++;
}
/* if there are any filters */
/* V5 filters not representable in V1 format. */
}
listnextulink:
clink = clink->next;
}
/* If none, match, say so */
if(!item_count)
replyf_v1(req,"NONE-FOUND\n");
/* Otherwise, if components remain say so */
else if(additional_comps) {
p__tkl_back_2slashpath(additional_comps, t_name);
replyf_v1(req, "UNRESOLVED %s\n", t_name);
}
obfree(ob);
ob = NULL;
break;
case OBSOLETE:
creplyf_v1(req, "FAILURE OUT-OF-DATE Please update your clients \
to Prospero version 5.\n");
RETURNPFAILURE;
break;
default:
plog(L_DIR_PERR,req,
"Unknown message: %s",command);
creplyf_v1(req,"FAILURE UNIMPLEMENTED %s\n",command);
RETURNPFAILURE;
}
get_token(command,'\n'); /* Defined above */
continue;
dforwarded:
forwarded:
replyf_v1(req,"FORWARDED\n");
/* Forwarding is not used in the current ARCHIE clients, so skip the
fancy stuff. . */
#if 0
dforwarded:
fl = dir->f_info->forward; dir->f_info->forward = NULL;
fp = check_fwd(fl,client_dir,dir_magic_no);
vdir_freelinks(dir);
forwarded:
if(fp) {
if (haswhite(fp->hosttype) || haswhite(fp->host) ||
haswhite(fp->hsonametype) || haswhite(fp->hsoname)) {
creply_v1(req, "FAILURE OUT-OF-DATE Cannot \
represent place object was forwarded to in v1 of Prospero protocol.\n");
RETURNPFAILURE;
}
replyf_v1(req,"FORWARDED %s %s %s %s %d %d\n",
fp->hosttype,fp->host,fp->hsonametype,fp->hsoname,
fp->version,fp->f_magic_no);
}
else replyf_v1(req,"FORWARDED\n");
vllfree(fl);
#endif
get_token(command,'\n'); /* Defined above */
}
creply_v1(req,NULL);
return(PSUCCESS);
}
/*
* v1_cmd_lookup - lookup the command name and return integer
*
* V1_CMD_LOOKUP takes a pointer to a string containing a command.
* It then looks up the first word found in the string and
* returns an int that can be used in a switch to dispatch
* to the correct routines.
*
* This has been optimzed for the swerver side of the VFS protocol.
*/
static
int
v1_cmd_lookup(char *cmd)
{
switch(*cmd) {
case 'A':
if(!strncmp(cmd,"AUTHENTICATOR",13))
return(AUTHENTICATOR);
else return(UNIMPLEMENTED);
#ifndef PSRV_READ_ONLY
case 'C':
if(!strncmp(cmd,"CREATE-OBJECT",11))
return OBSOLETE;
else if(!strncmp(cmd,"CREATE-LINK",11))
return OBSOLETE;
else if(!strncmp(cmd,"CREATE-DIRECTORY",16))
return OBSOLETE;
else return(UNIMPLEMENTED);
#endif
case 'D':
if(!strncmp(cmd,"DELETE-LINK",11))
#ifndef PSRV_READ_ONLY
return OBSOLETE;
#else
return(UNIMPLEMENTED);
#endif
else if(!strncmp(cmd,"DIRECTORY",9))
return(DIRECTORY);
else return(UNIMPLEMENTED);
case 'E':
if(!strncmp(cmd,"EDIT-OBJECT-INFO",14))
return OBSOLETE;
else return(UNIMPLEMENTED);
case 'G':
if(!strncmp(cmd,"GET-OBJECT-INFO",13))
return OBSOLETE;
else return(UNIMPLEMENTED);
case 'L':
if(!strncmp(cmd,"LIST-ACL",8))
return OBSOLETE;
else if(!strncmp(cmd,"LIST",4))
return(LIST);
else return(UNIMPLEMENTED);
case 'M':
if(!strncmp(cmd,"MODIFY-ACL",10))
return OBSOLETE;
else if(!strncmp(cmd,"MODIFY-LINK",11))
return OBSOLETE;
else return(UNIMPLEMENTED);
case 'P':
if(!strncmp(cmd,"PACKET",1))
return(PACKET);
else return(UNIMPLEMENTED);
case 'R':
if(!strncmp(cmd,"RESTART",7))
return OBSOLETE;
else return(UNIMPLEMENTED);
case 'S':
if(!strncmp(cmd,"STATUS",6))
return OBSOLETE;
else return(UNIMPLEMENTED);
case 'T':
if(!strncmp(cmd,"TERMINATE",9))
return OBSOLETE;
else return(UNIMPLEMENTED);
case 'U':
if(!strncmp(cmd,"UPDATE",6))
return OBSOLETE;
else return(UNIMPLEMENTED);
case 'V':
if(!strncmp(cmd,"VERSION",7))
return(VERSION);
else return(UNIMPLEMENTED);
default:
return(UNIMPLEMENTED);
}
}
/* This is the old library file quote.c. It is vestigial except for
dirsrv_v1(). */
/*
* quote - quote string if necessary
*
* QUOTE takes a string and quotes it if it contains special
* characters. The null string is also quoted.
*
* ARGS: s - string to be quoted
*
* RETURNS: The quoted string. If quoting is required, the string
* appears in static storage, and must be copied if
* it is to last beyond the next call to quote.
* If s is null, or the null string, '' is returned.
*
* BUGS: Still needs to check for additional illegal embedded characters
*/
static
char *
quote(char *s /* String to be quoted */)
{
#ifdef PFS_THREADS
AUTOSTAT_CHARPP(quotedp);
#else
static char *quoted;
#endif
char *qp;
char *sp = s;
int needsquoting = 0;
#ifdef PFS_THREADS
if (p__bstsize(*quotedp) == 0) *quotedp = stalloc(200);
qp = *quotedp;
#else
if (p__bstsize(quoted) == 0) quoted = stalloc(200);
qp = quoted;
#endif
*qp++ = '\'';
if(!sp) sp = "";
while(*sp) {
if ((*sp < '#') || (*sp > '~')) needsquoting++;
if (*sp == '\'') *(qp++) = '\'';
*(qp++) = *sp++;
#ifdef PFS_THREADS
if (qp > *quotedp + p__bstsize(*quotedp) - 2) {
int newsize = p__bstsize(*quotedp) + 200;
stfree(*quotedp);
*quotedp = stalloc(newsize);
return quote(s);
}
#else
if (qp > quoted + p__bstsize(quoted) - 2) {
int newsize = p__bstsize(quoted) + 200;
stfree(quoted);
quoted = stalloc(newsize);
return quote(s);
}
#endif
}
*(qp++) = '\'';
*(qp++) = '\0';
#ifdef PFS_THREADS
if((qp == *quotedp + 3) || needsquoting) return(*quotedp);
#else
if((qp == quoted + 3) || needsquoting) return(quoted);
#endif
return(s);
}
/* Convert an external link from Version 5 link format to Version 1 format. */
static int
v1_externalize(VLINK vl)
{
PATTRIB at;
TOKEN am; /* access method itself. */
if (vl->target && strequal(vl->target, "EXTERNAL")) {
/* Look for an AFTP access method. */
for (at = vl->lattrib; at; at = at->next) {
if (strequal(at->aname, "ACCESS-METHOD")
&& length((am = at->value.sequence)) == 6
&& strequal(am->token, "AFTP")) {
/* Make sure hosttype, etc, are all correctly set. */
if (*elt(am, 1))
vl->hosttype = stcopyr(elt(am,1), vl->hosttype);
if (*elt(am,2))
vl->host = stcopyr(elt(am,2), vl->host);
if (*elt(am,3))
vl->hsonametype = stcopyr(elt(am,3), vl->hsonametype);
if (*elt(am,4))
vl->hsoname = stcopyr(elt(am,4), vl->hsoname);
if(strequal(elt(am,5), "BINARY")) {
vl->target = stcopyr("EXTERNAL(AFTP,BINARY)", vl->target);
return PSUCCESS;
} else if (strequal(elt(am,5), "TEXT")) {
vl->target = stcopyr("EXTERNAL(AFTP,TEXT)", vl->target);
return PSUCCESS;
} else {
/* That's really weird. Must be BINARY or TEXT. Oh well,
we're stuck now; go on. */
continue;
}
}
} /* for each attribute */
RETURNPFAILURE; /* couldn't find an access-method. How odd. */
} else
return PSUCCESS;
}
/* copied from shadowv1v5cv5.c. Really should go into a separate v1compat.c
file. */
static TOKEN
tokenize(s)
char *s;
{
TOKEN retval;
char buf[MAX_DIR_LINESIZE];
int tmp = qsscanf(s, "%!!s %r", buf, sizeof buf, &s);
if (tmp < 0)
internal_error("Buffer overflow!");
if (tmp == 0)
return NULL;
retval = tkalloc(buf);
if (tmp == 2)
retval->next = tokenize(s);
return retval;
}
/* These reply functions make sure that we don't split lines across packets
in replies to old clients, since the older version of the client library
could not handle lines split across packets.
If a line is too long, they set the warning message final_warning.
Then, when the reply is completed, the warning message is sent, if possible.
*/
static char reply_buf[ARDP_PTXT_LEN + 1]; /* buffer. */
static int reply_buflen = 0; /* length starts at 0. */
/* Set to -1 if line was too long & has already
been exceeded, but waiting for final
newline. */
/* NEWLINE NULL terminated string if a warning message present; otherwise just
a NULL in the buffer. Note that warnings were limited to 100 bytes in
Beta.4.2e client; so we can't send a longer one than that. */
static char final_warning[100] = "";
/* This code assumes that if a newline is present in buf, it will be the last
character of buf. */
static int
reply_v1(RREQ req, char *buf)
{
if (!buf) return PSUCCESS;
assert(!strchr(buf, '\n') || strchr(buf, '\n')[1] == '\0');
if (reply_buflen == -1) {
if (strchr(buf, '\n'))
flush_pending_v1_reply();
return PSUCCESS;
}
while (*buf) {
reply_buf[reply_buflen++] = *buf++;
if (reply_buflen > ARDP_PTXT_LEN) {
reply_buflen = -1;
reply_buf[ARDP_PTXT_LEN] = '\0'; /* for the debugger's convenience.
*/
return PSUCCESS;
}
}
reply_buf[reply_buflen] = '\0'; /* cap it off nicely; always
null-terminated for debugger's
convenience and for our own at end.*/
if (reply_buflen > 0 && reply_buf[reply_buflen - 1] == '\n') {
int tmp;
/* Just need ARDP_A2R_NOSPLITL, but this compensates for a bug. */
tmp = ardp_reply(req, ARDP_R_INCOMPLETE| ARDP_A2R_NOSPLITBL,
reply_buf);
reply_buflen = 0;
return tmp;
}
return PSUCCESS;
}
/* Flush the pending v1 reply; set the warning message */
/* With or without trailing newline is ok; special code in creply_v1 handles
this. */
static
void
flush_pending_v1_reply(void)
{
reply_buf[reply_buflen = 0] = '\0';
/* Keep it to < 100 characters! */
qsprintf(final_warning, sizeof final_warning,
"WARNING OUT-OF-DATE Some information you wanted can't be sent. \
Upgrade to Prospero v5.\n");
}
static int
creply_v1(RREQ req, char *buf)
{
reply_v1(req, buf);
/* There should be no incomplete line pending. */
/* Just in case... */
if (reply_buflen) reply_v1(req, "\n");
assert(!reply_buflen);
/* Just need ARDP_A2R_NOSPLITL, but this compensates for a bug. */
if (*final_warning) {
int tmp = ardp_reply(req, ARDP_A2R_NOSPLITBL | ARDP_R_COMPLETE,
final_warning);
*final_warning = '\0';
return tmp;
}
return ardp_reply(req, ARDP_A2R_NOSPLITBL | ARDP_R_COMPLETE,
(char *) NULL);
}
static int
replyf_v1(RREQ req, char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = vreplyf_v1(req, format, ap);
va_end(ap);
return retval;
}
static int
creplyf_v1(RREQ req, char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = vreplyf_v1(req, format, ap);
va_end(ap);
if (!retval) creply_v1(req, (char *) NULL); /* complete the request */
return retval;
}
static int
vreplyf_v1(RREQ req, char *format, va_list ap)
{
#ifdef PFS_THREADS
AUTOSTAT_CHARPP(bufp);
AUTOSTAT_INTP(bufsizp);
#else
static char *buf = NULL;
static int bufsiz = 0;
#endif
int n; /* # of characters possibly written.
# of characters in string in buf */
#ifdef PFS_THREADS
*bufp = vqsprintf_stcopyr(*bufp, format, ap);
/* Perform the reply and pass through any error codes */
return reply_v1(req, *bufp);
#else
buf = vqsprintf_stcopyr(buf, format, ap);
/* Perform the reply and pass through any error codes */
return reply_v1(req, buf);
#endif
}
/* Lifted from lib/psrv/error_reply.c */
static int
error_reply_v1(RREQ req, char *format, ...)
{
va_list ap;
char *bufp;
va_start(ap, format);
bufp = vplog(L_DIR_PERR, req, format, ap); /* return formatted string */
reply_v1(req, "ERROR ");
reply_v1(req, bufp);
creply_v1(req, "\n");
va_end(ap);
RETURNPFAILURE;
}
#include <ctype.h>
static int
haswhite(char *s)
{
for ( ; *s; ++s) {
if (isspace(*s)) return 1;
}
return 0;
}
static int
hasnl(char *s)
{
for ( ; *s; ++s) {
if (*s == '\n') return 1;
}
return 0;
}
#endif /* SERVER_SUPPORT_V1 */