Hi All! I've made all patches except sanity.sh to incorporate %subj% into CVS. File full.diff contains unified patch.
This version supports both local and pserver mode. It has been tested under FreeBSD 4.3 and Cygnus. Regards, Andrey Aristarkhov BiTechnology
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Copyright (C) 2000-2002
* Andrey Aristarkhov <[EMAIL PROTECTED]>.
* All rights reserved
* Partial Copyright (C) 1996
* David L. Nugent. All rights reserved.
*
* $Id: user.c,v 1.8.2.6 2002/08/16 15:48:42 dron Exp $
*
* Frontend functions for CVS' passwords manupulation
*
*/
#include "user.h"
#include "cvs.h"
#ifndef safe_string
#define safe_string(s) s ? s : "null"
#endif
static char pathpwd[MAXPATHLEN];
static char * pwpath = pathpwd;
static int
pw_fileupdate(char const * filename, mode_t fmode, char const * newline, char const *
prefix, int pfxlen, int updmode)
{
int rc = 0;
int updated = PWD_CREATE;
int linesize = PWBUFSZ;
char *line;
FILE *outfp;
FILE *infp;
int outfd;
char file[MAXPATHLEN];
int infd;
if (pfxlen <= 1)
rc = EINVAL;
else {
infd = open(filename, O_RDWR | O_CREAT, fmode);
if (infd == -1)
rc = errno;
else {
infp = fdopen(infd, "r+");
if (infp == NULL) {
rc = errno; /* Assumes fopen(3) sets errno
from open(2) */
close(infd);
} else {
strcpy(file, filename);
strcat(file, ".new");
#ifdef __FreeBSD__
outfd = open(file, O_RDWR | O_CREAT | O_TRUNC |
O_EXLOCK, fmode);
#else
outfd = open(file, O_RDWR | O_CREAT | O_TRUNC , fmode);
#endif
if (outfd == -1)
rc = errno;
else {
outfp = fdopen(outfd, "w+");
if (outfp == NULL) {
rc = errno;
close(outfd);
} else {
updated = PWD_CREATE;
linesize = PWBUFSZ;
line = malloc(linesize);
while (fgets(line, linesize, infp) !=
NULL) {
char *p = strchr(line, '\n');
while ((p = strchr(line,
'\n')) == NULL) {
int l;
expand_string(&line,
&linesize, linesize + PWBUFSZ);
l = strlen(line);
if (fgets(line + l,
linesize - l, infp) == NULL)
break;
}
if (*line != '#' && *line !=
'\n') {
if (!updated &&
strncmp(line, prefix, pfxlen) == 0) {
updated =
updmode == PWD_UPDATE ? PWD_UPDATE : PWD_DELETE;
/*
* Only
actually write changes if updating
*/
if (updmode ==
PWD_UPDATE)
strcpy(line, newline);
else if
(updmode == PWD_DELETE)
continue;
}
}
fputs(line, outfp);
}
/*
* Now, we need to decide what to do:
If we are in
* update mode, and no record was
updated, then error If
* we are in insert mode, and record
already exists,
* then error
*/
if (updmode != updated)
/* -1 return means:
* update,delete=no user entry
* create=entry exists
*/
rc = -1;
else {
/*
* If adding a new record,
append it to the end
*/
if (updmode == PWD_CREATE)
fputs(newline, outfp);
/*
* Flush the file and check
for the result
*/
if (fflush(outfp) == EOF)
rc = errno; /*
Failed to update */
else {
/*
* Copy data back into
the
* original file and
truncate
*/
rewind(infp);
rewind(outfp);
while (fgets(line,
linesize, outfp) != NULL)
fputs(line,
infp);
/*
* If there was a
problem with copying
* we will just rename
'file.new'
* to 'file'.
* This is a gross
hack, but we may have
* corrupted the
original file
* Unfortunately, it
will lose the inode
* and hence the lock.
*/
if (fflush(infp) ==
EOF || ferror(infp))
rename(file,
filename);
else
ftruncate(infd, ftell(infp));
}
}
free(line);
fclose(outfp);
}
remove(file);
}
fclose(infp);
}
}
}
return rc;
}
static void setpwpath(const char * file) {
strcpy(pathpwd,file);
}
static char * getpwpath()
{
static char pathbuf[MAXPATHLEN];
strcpy(pathbuf,pathpwd);
return pathbuf;
}
/*
* Return values:
* -1 -- generic error
* 0 -- success
* 1 -- user is empty or NULL
* 2 -- passwd is empty or NULL
*/
static int
fmtpwentry(char **buf, const char *user, const char * passwd, const char * alias)
{
int l;
int has_alias;
char *pw;
if (user == NULL || strlen(user) == 0) {
return 1;
} else if (passwd == NULL || strlen(passwd) == 0) {
return 2;
}
if (alias == NULL || strlen(alias) == 0) {
has_alias = 0;
} else {
has_alias = strlen(alias);
}
l = strlen(passwd)+strlen(user) +has_alias+ 2;
pw = (char *)malloc(l);
if (has_alias) {
sprintf(pw,"%s:%s:%s\n",user,passwd,alias);
} else {
sprintf(pw,"%s:%s\n",user,passwd);
}
*buf = pw;
return 0;
}
static int
pw_update(const char * user, const char * passwd, const char * alias, int mode)
{
int rc = 0;
char pfx[32];
char * pwbuf;
int l = sprintf(pfx, "%s:", user);
/*
* Update passwd file
*/
if (mode != PWD_DELETE) {
rc = fmtpwentry(&pwbuf,user,passwd,alias);
if (rc != 0) {
return rc;
}
} else {
pwbuf = malloc(strlen(user)+2);
sprintf(pwbuf,"%s:",user);
}
rc = pw_fileupdate(getpwpath(), 0644, pwbuf, pfx, l, mode);
if (pwbuf != NULL) {
free(pwbuf);
}
return rc;
}
#ifndef CHARSET_EBCDIC
#define LF 10
#define CR 13
#else /*CHARSET_EBCDIC*/
#define LF '\n'
#define CR '\r'
#endif /*CHARSET_EBCDIC*/
static int getline(char *s, int n, FILE *f)
{
register int i = 0;
while (1) {
s[i] = (char) fgetc(f);
if (s[i] == CR) {
s[i] = fgetc(f);
}
if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
s[i] = '\0';
return (feof(f) ? 1 : 0);
}
++i;
}
}
#define MAX_STRING_LEN 256
static char * get_password(const char * user) {
static char pwd[MAX_STRING_LEN];
char line[MAX_STRING_LEN];
FILE * fpw=NULL;
char * token;
fpw = fopen (getpwpath(), "r");
if (fpw == NULL) {
return NULL;
}
memset(pwd,0,MAX_STRING_LEN);
while (!(getline (line, MAX_STRING_LEN, fpw))) {
if (line[0] == '#') {
continue;
}
token = strtok(line,":");
if (token == NULL) {
continue;
}
if (strcmp (user, token) != 0) {
continue;
}
/* User found */
token = strtok(NULL,":");
strcpy(pwd,token);
fclose(fpw);
return pwd;
}
fclose(fpw);
return NULL;
}
static char * get_alias(const char * user) {
static char alias[MAX_STRING_LEN];
char line[MAX_STRING_LEN];
FILE * fpw;
char * token;
fpw = fopen (getpwpath(), "r");
while (!(getline (line, sizeof (line), fpw))) {
if (line[0] == '#') {
continue;
}
token = strtok(line,":");
if (strcmp (user, token) != 0) {
continue;
}
/* User found */
token = strtok(NULL,":");
token = strtok(NULL,":");
if (token == NULL) {
return NULL;
}
strcpy(alias,token);
fclose(fpw);
return alias;
}
fclose(fpw);
return NULL;
}
/*extern char * crypt(const char *, const char *);*/
static char const chars[] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
static char * pw_crypt(const char *password)
{
int i;
char salt[12];
static char buf[256];
/*
* Calculate a salt value
*/
#ifdef __FreeBSD__
srandomdev();
#else
srandom((unsigned long) (time(NULL) ^ getpid()));
#endif
for (i = 0; i < 8; i++)
salt[i] = chars[random() % 63];
salt[i] = '\0';
return strcpy(buf, crypt(password, salt));
}
/*
* Return values:
* -1 - user exists
* 0 - user added, otherwise another code
*/
static int pw_add_user(const char * user, const char * passwd, const char * alias) {
char * newpasswd;
int res;
if (trace) {
printf("%s->pw_add_user: user=%s, passwd=%s,
alias=%s\n",CLIENT_SERVER_STR,safe_string(user),safe_string(passwd),safe_string(alias));
fflush(stdout);
}
if (get_password(user) != NULL) {
return -1;
}
if (passwd == NULL || strlen(passwd) == 0) {
newpasswd=xstrdup("*");
} else {
newpasswd = xstrdup(pw_crypt(passwd));
}
res = pw_update(user,newpasswd,alias,PWD_CREATE);
free(newpasswd);
return res;
}
/* -1 - user does not exists, otherwise 0
*/
static int pw_mod_user(const char * user, const char * passwd, const char * alias) {
char * newpasswd;
#ifdef NEVER
if (trace) {
printf("%s->pw_mod_user: user=%s, passwd=%s,
alias=%s\n",CLIENT_SERVER_STR,safe_string(user),safe_string(passwd),safe_string(alias));
fflush(stdout);
}
#endif
if ((newpasswd = get_password(user)) == NULL) {
return -1;
}
if (passwd != NULL) {
newpasswd = pw_crypt(passwd);
}
if (alias == NULL)
alias = get_alias(user);
return pw_update(user,newpasswd,alias,PWD_UPDATE);
}
/* -1 - user does not exists */
static int pw_del_user(const char * user) {
if (get_password(user) == NULL) {
return -1;
}
return pw_update(user,NULL,NULL,PWD_DELETE);
}
static const char *const password_usage[] =
{
"Usage: %s %s\n",
/* "\tIf no \"username\" is given password will be set for the current user\n",
"\t\"username\"\tUse it if you want to change password for the specified user\n"*/
"(Specify the --help global option for a list of other help options)\n",
NULL
};
static const char *const user_usage[] =
{
"Usage: %s %s <[-a | -m | -d] username> [-u alias] [-p | -P password]\n",
"\t-a|-m|-d\t'add', 'modify' or 'delete' user respectively\n",
"\t-u\tUse \"alias\" to specify system user for cvs-user.\n",
"\t-P\tUse \"password\" to specify user password in a command line OR\n",
"\t-p\tenter user password interactively\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
enum pwd_read_mode { PRM_NEW, PRM_ADMIN, PRM_CHECK };
static void verify_password_phrase(const char * password, unsigned char
complete_check) {
int plen, i;
if (!password) {
error(1,0,"Failed to read password");
}
if (!complete_check)
return;
plen = strlen(password);
for (i=0;i<plen;i++) {
if ((unsigned char)password[i] < 33 || (unsigned char)password[i] > 126) {
error(1,0,"Invalid character in password (0x%x). Password can contain only
printable characters.",password[i]);
}
}
if (plen < MIN_PASSWORD_LEN) {
error(1,0,"Password length can't be less than %d characters",MIN_PASSWORD_LEN);
}
if (plen > MAX_PASSWORD_LEN) {
error(1,0,"Password length can't be more than %d characters",MAX_PASSWORD_LEN);
}
}
/*
* Reads and verifies user's password from an input
* Return password for the user
*/
static char * read_user_password(const char * user, int pwd_read_mode) {
char prompt[128];
char * pwd, * pwd_verify;
static char password[_PASSWORD_LEN];
memset(password,0,_PASSWORD_LEN);
switch (pwd_read_mode) {
case PRM_NEW:
sprintf(prompt,"Enter New password for user '%s':",user);
/* We need to strdup because GETPASS uses static object */
pwd_verify = GETPASS(prompt);
verify_password_phrase(pwd_verify,TRUE);
pwd = strdup(pwd_verify);
memset(pwd_verify,0,strlen(pwd_verify));
sprintf(prompt,"Re-type New password for user '%s':",user);
pwd_verify = GETPASS(prompt);
verify_password_phrase(pwd_verify,FALSE);
if (strcmp(pwd,pwd_verify)) {
free(pwd);
error(1,0,"Passwords are different");
} else {
free(pwd);
strcpy(password,pwd_verify);
memset(pwd_verify,0,strlen(pwd_verify));
}
break;
case PRM_CHECK:
pwd = get_password(user);
sprintf(prompt,"Enter current password for user '%s': ",user);
pwd_verify = GETPASS(prompt);
verify_password_phrase(pwd_verify,FALSE);
if (strcmp(pwd,crypt(pwd_verify,pwd))==0) {
strcpy(password,pwd);
} else {
error(1,0,"Wrong password",user);
}
break;
case PRM_ADMIN:
pwd = get_password(CVS_ADMIN_USER);
if (pwd == NULL)
error(1,0,"Administrator's user accout not found. Please contact your CVS
admin.");
if (strcmp(pwd,"*")==0)
error(1,0,"Password for Administrator's user accout is not set. Please contact
your CVS admin.");
pwd_verify = GETPASS("Enter CVS Administrator password: ");
verify_password_phrase(pwd_verify,FALSE);
if (strcmp(pwd,crypt(pwd_verify,pwd))==0) {
strcpy(password,pwd);
} else {
error(1,0,"Administrator password is invalid");
}
break;
}
return password;
}
static void set_password_path(cvsroot_t * root) {
char passwdFile[MAXPATHLEN];
sprintf(passwdFile,"%s/%s/%s",root->directory,CVSROOTADM,CVSROOTADM_PASSWD);
setpwpath(passwdFile);
}
static char * get_current_user(cvsroot_t * root) {
char * user = root->username;
if (user == NULL) {
user=getcaller();
}
if (user==NULL) {
error(1,0,"Fatal error: Can't detect current user.");
} /*# It's impossible in pserver mode */
return user;
}
int password(int argc, char ** argv) {
char * user, * passwd, * passwd_to_send, * curr_password;
char prompt[64];
user = passwd = passwd_to_send = NULL;
/*
* Hacking "init" command
*/
if (argc == -2) { /* called after mkmodules() in init() */
char passwdFile[MAXPATHLEN];
sprintf(passwdFile,"%s/%s",argv[0],CVSROOTADM_PASSWD);
setpwpath(passwdFile);
/* If password faile exists, remove it */
/*remove(passwdFile);*/
/* constructing password. Need more relyable algorithm. */
sprintf(passwdFile,"admin%d",getpid());
error(0,0,"Creating administrator account '%s': password is
'%s'.",CVS_ADMIN_USER,passwdFile);
if (pw_add_user(CVS_ADMIN_USER,passwdFile,NULL) > 0) {
error(0,errno,"Failed to add administrator account");
}
#ifdef NEVER
if (trace) {
printf("%s->user=%s
added\n",CLIENT_SERVER_STR,safe_string(user));
fflush(stdout);
}
#endif
return 0;
}
/* End of init section */
if (!server_active && current_parsed_root->method != pserver_method) {
error (0, 0, "can only use `password' command with the 'pserver'
method");
error (1, 0, "CVSROOT: %s", current_parsed_root->original);
}
if (argc == -1)
usage(password_usage);
#ifdef SERVER_SUPPORT
if (server_active) {
if (argc < 2) {
printf("E protocol error: can't read password\n");
return 1;
}
if (argc > 2) {
printf("E protocol error: too many parameters\n");
return 1;
}
passwd = descramble(argv[1]);
}
#else
if (argc > 1 || argc == -1) {
usage(password_usage);
}
#endif
user = get_current_user(current_parsed_root);
if (!server_active) {
curr_password = get_cvs_password();
if (curr_password==NULL) {
error(1,0,"You are not logged into CVSROOT=%s. Try to login
first.",current_parsed_root->original);
}
sprintf(prompt,"Enter current password for user '%s': ",user);
passwd = GETPASS(prompt);
curr_password = descramble(curr_password);
if (strcmp(curr_password,passwd)!=0) {
error(1,0,"Wrong password. Command aborted.");
}
memset(curr_password,0,strlen(curr_password));
free(curr_password);
memset(passwd,0,strlen(passwd));
passwd = read_user_password(user,PRM_NEW);
passwd_to_send = scramble(passwd);
#ifdef CLIENT_SUPPORT
start_server ();
ign_setup ();
send_arg (passwd_to_send);
send_to_server("password\012",0);
memset(passwd_to_send,0,strlen(passwd_to_send));
free(passwd_to_send);
return get_responses_and_close();
#endif /* CLIENT_SUPPORT */
}
#ifdef SERVER_SUPPORT
set_password_path(current_parsed_root);
if (pw_mod_user(user,passwd,NULL)==-1) {
free(passwd);
error(0,0,"User '%s'not found",user);
return 1;
}
return 0;
#endif
}
#define checkOper(op) if (##op!= PWD_UNDEFINED) {\
error(0,0,"Choose one of of options -a | -m | -d");\
usage(user_usage);\
}
int user(int argc, char ** argv) {
enum updtype op;
char * user, * curr_user, * passwd, * alias, * curr_password;
char * cvsroot;
int res;
char c;
unsigned char hasPassword, needPassword;
user = alias = passwd = NULL;
op = PWD_UNDEFINED;
hasPassword = needPassword = 0;
#ifdef NEVER
if (trace) {
printf("%s->cvs user: server_active: %d, argc=%d, argv[0]=%s,
argv[1]=%s\n",CLIENT_SERVER_STR,server_active,argc,safe_string(argv[0]),safe_string(argv[1]));
fflush(stdout);
}
#endif
/* parse args */
if (argc == -1)
usage(user_usage);
optind = 0;
while ((c = getopt (argc, argv, "+a:d:m:u:pP:")) != -1)
{
switch (c) {
case 'a':
checkOper(op);
op = PWD_CREATE;
user = optarg;
break;
case 'm':
checkOper(op);
op = PWD_UPDATE;
user = optarg;
break;
case 'd':
checkOper(op);
op = PWD_DELETE;
user = optarg;
break;
case 'u':
alias = optarg;
break;
case 'P':
hasPassword = 1;
passwd = optarg;
break;
case 'p':
needPassword = 1;
break;
case '?':
default:
usage (user_usage);
break;
}
}
argc -= optind;
argv += optind;
if (op == PWD_UNDEFINED) {
usage(user_usage);
}
if (hasPassword && needPassword) {
error(0,0,"-p and -P options are mutually exclusive");
usage(user_usage);
}
/* end of parse args */
curr_user = get_current_user(current_parsed_root);
if ( strcmp(curr_user, CVS_ADMIN_USER) != 0) {
error(1,0,"You are not an administrator");
}
if (!server_active) {
char * tmppass;
curr_password = get_cvs_password();
if (curr_password==NULL) {
error(1,0,"You are not logged into CVSROOT=%s. Try to login
first.",current_parsed_root->original);
}
tmppass = GETPASS("Enter CVS Administrator password: ");
if (!tmppass || strlen(tmppass)==0) {
error(1,0,"Failed to read password. Command aborted.");
}
curr_password = descramble(curr_password);
if (trace) {
printf("OLD: %s, CUR: %s\n",curr_password,tmppass);
fflush(stdout);
}
if (strcmp(curr_password,tmppass)!=0) {
memset(curr_password,0,strlen(curr_password));
free(curr_password);
error(1,0,"Wrong password. Command aborted.");
}
memset(curr_password,0,strlen(curr_password));
free(curr_password);
memset(tmppass,0,strlen(tmppass));
if (needPassword) {
passwd = read_user_password(user,PRM_NEW);
}
#ifdef CLIENT_SUPPORT
if (trace) {
printf("%s->cvs user: operation: %d, user=%s, passwd=%s(%d),
alias=%s\n",CLIENT_SERVER_STR,op,safe_string(user),safe_string(passwd),needPassword |
hasPassword,safe_string(alias));
fflush(stdout);
}
start_server ();
ign_setup ();
if (trace) {
printf("%s->sending options\n",CLIENT_SERVER_STR);
fflush(stdout);
}
if (op==PWD_CREATE) {
send_arg ("-a");
} else if (op==PWD_UPDATE) {
send_arg ("-m");
} else if (op==PWD_DELETE) {
send_arg ("-d");
}
send_arg(user);
if (alias != NULL) {
send_arg("-u");
send_arg(alias);
}
if (needPassword | hasPassword) {
curr_password = scramble(passwd);
send_arg("-P");
send_arg (curr_password);
} else {
curr_password = NULL;
}
send_to_server("admin_user\012",0);
if (curr_password) {
free(curr_password);
memset(curr_password,0,strlen(curr_password));
}
if (passwd)
memset(passwd,0,strlen(passwd));
if (trace) {
printf("%s->waiting response\n",CLIENT_SERVER_STR);
fflush(stdout);
}
return get_responses_and_close();
#endif /* CLIENT_SUPPORT */
} else {/* (!server_active) */
#ifdef SERVER_SUPPORT
if (hasPassword)
passwd = descramble(passwd); /* Client scrmble it before sending */
else
passwd = NULL;
#endif
}
set_password_path(current_parsed_root);
if (trace) {
printf("%s->cvs user: operation: %d, user=%s, passwd=%s,
alias=%s\n",CLIENT_SERVER_STR,op,safe_string(user),safe_string(passwd),safe_string(alias));
fflush(stdout);
}
switch (op) {
case PWD_CREATE:
res = pw_add_user(user,passwd,alias);
if (res == -1) {
error(1,0,"User '%s' already exists.",user);
}
if (res != 0) {
error(1,errno,"Can't add user '%s' - Fatal internal error.",user);
}
error(0,0,"User '%s' successefully added.",user);
break;
case PWD_UPDATE:
res = pw_mod_user(user,passwd,alias);
if (res == -1) {
error(1,0,"User '%s' not found.",user);
}
if (res != 0) {
error(1,errno,"Can't add user '%s' - Fatal internal error.",user);
}
error(0,0,"User '%s' successefully updated.",user);
break;
case PWD_DELETE:
if (pw_del_user(user) == -1) {
error(1,0,"Can't delete user '%s' - user not found.",user);
} else {
error(0,0,"User '%s' successefully deleted.",user);
}
break;
}
if (server_active && passwd!=NULL)
free(passwd);
return 0;
}
src_server.c.diff
Description: Binary data
src_mkmodules.c.diff
Description: Binary data
src_Makefile.am.diff
Description: Binary data
src_main.c.diff
Description: Binary data
src_login.c.diff
Description: Binary data
src_cvs.h.diff
Description: Binary data
src_admin.c.diff
Description: Binary data
full.diff
Description: Binary data
doc_cvs.texinfo.diff
Description: Binary data
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Copyright (C) 2000-2002
* Andrey Aristarkhov <[EMAIL PROTECTED]>.
* All rights reserved
* Partial Copyright (C) 1996
* David L. Nugent. All rights reserved.
*
* $Id: user.h,v 1.2 2002/08/14 13:00:32 dron Exp $
*
* Header file for user & password file manipulation functions
* within CVS repository
*/
#ifndef CVS_USER_H_
#define CVS_USER_H_
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#ifdef OS_FREEBSD
#include <sys/cdefs.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
int password (int argc, char **argv);
int user (int argc, char **argv);
enum updtype { PWD_CREATE, PWD_UPDATE, PWD_DELETE, PWD_UNDEFINED };
#define CVS_ADMIN_USER "admin"
#define PWBUFSZ 1024
#ifndef _PASSWORD_LEN
#define _PASSSWORD_LEN
#endif
#define MAX_PASSWORD_LEN _PASSWORD_LEN
#define MIN_PASSWORD_LEN 6
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef HAVE_GETPASSPHRASE
#define GETPASS getpassphrase
#else
#define GETPASS getpass
#endif
#ifdef __cplucplus
}
#endif
#endif /* CVS_USER_H */
