#include "cvs.h"
#include "getline.h"
#ifdef AUTH_CLIENT_SUPPORT
#ifndef CVS_PASSWORD_FILE
#define CVS_PASSWORD_FILE ".cvspass"
#endif
static char *cvs_password = NULL;
static char *construct_cvspass_filename PROTO ((void));
static char *
construct_cvspass_filename ()
{
char *homedir;
char *passfile;
if ((passfile = getenv ("CVS_PASSFILE")) != NULL)
return xstrdup (passfile);
homedir = get_homedir ();
if (! homedir)
{
error (1, 0, "could not find out home directory");
return (char *) NULL;
}
passfile = strcat_filename_onto_homedir (homedir, CVS_PASSWORD_FILE);
if (isfile (passfile))
chmod (passfile, 0600);
return passfile;
}
static char *
password_entry_parseline (cvsroot_canonical, warn, linenumber, linebuf)
const char *cvsroot_canonical;
const unsigned char warn;
const int linenumber;
char *linebuf;
{
char *password = NULL;
char *p;
if (*linebuf == '/')
{
char *q;
unsigned long int entry_version;
if (isspace(*(linebuf + 1)))
q = linebuf + 1;
else
entry_version = strtoul (linebuf + 1, &q, 10);
if (q == linebuf + 1)
entry_version = 0;
else
q++;
switch (entry_version)
{
case 1:
p = strchr (q, ' ');
if (p == NULL)
{
if (warn && !really_quiet)
error (0, 0, "warning: skipping invalid entry in password file at line %d",
linenumber);
}
else
{
*p = '\0';
if (strcmp (cvsroot_canonical, q) == 0)
password = p + 1;
*p = ' ';
}
break;
case ULONG_MAX:
if (warn && !really_quiet)
{
error (0, errno, "warning: unable to convert version number in password file at line %d",
linenumber);
error (0, 0, "skipping entry");
}
break;
case 0:
if (warn && !really_quiet)
error (0, 0, "warning: skipping entry with invalid version string in password file at line %d",
linenumber);
break;
default:
if (warn && !really_quiet)
error (0, 0, "warning: skipping entry with unknown version (%lu) in password file at line %d",
entry_version, linenumber);
break;
}
}
else
{
cvsroot_t *tmp_root;
char *tmp_root_canonical;
p = strchr (linebuf, ' ');
if (p == NULL)
{
if (warn && !really_quiet)
error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber);
return NULL;;
}
*p = '\0';
if ((tmp_root = parse_cvsroot (linebuf)) == NULL)
{
if (warn && !really_quiet)
error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber);
*p = ' ';
return NULL;
}
*p = ' ';
tmp_root_canonical = normalize_cvsroot (tmp_root);
if (strcmp (cvsroot_canonical, tmp_root_canonical) == 0)
password = p + 1;
free (tmp_root_canonical);
free_cvsroot_t (tmp_root);
}
return password;
}
typedef enum password_entry_operation_e {
password_entry_lookup,
password_entry_delete,
password_entry_add
} password_entry_operation_t;
static char *
password_entry_operation (operation, root, newpassword)
password_entry_operation_t operation;
cvsroot_t *root;
char *newpassword;
{
char *passfile;
FILE *fp;
char *cvsroot_canonical = NULL;
char *password = NULL;
int line_length;
long line = -1;
char *linebuf = NULL;
size_t linebuf_len;
char *p;
int save_errno = 0;
if (root->method != pserver_method)
{
error (0, 0, "\
internal error: can only call password_entry_operation with pserver method");
error (1, 0, "CVSROOT: %s", root->original);
}
cvsroot_canonical = normalize_cvsroot (root);
passfile = construct_cvspass_filename ();
fp = CVS_FOPEN (passfile, "r");
if (fp == NULL)
{
error (0, errno, "warning: failed to open %s for reading", passfile);
goto process;
}
line = 0;
while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0)
{
line++;
password = password_entry_parseline (cvsroot_canonical, 1, line,
linebuf);
if (password != NULL)
break;
}
if (line_length < 0 && !feof (fp))
{
error (0, errno, "cannot read %s", passfile);
goto error_exit;
}
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", passfile);
fp = NULL;
chmod (passfile, 0600);
if (password != NULL)
{
p = strchr (password, '\n');
if (p != NULL)
*p = '\0';
password = xstrdup (password);
}
process:
if (operation == password_entry_lookup)
goto out;
if (operation == password_entry_delete && password == NULL)
{
error (0, 0, "Entry not found.");
goto out;
}
if (!noexec && password != NULL && (operation == password_entry_delete
|| (operation == password_entry_add
&& strcmp (password, newpassword))))
{
long found_at = line;
char *tmp_name;
FILE *tmp_fp;
fp = CVS_FOPEN (passfile, "r");
if (fp == NULL)
error (1, errno, "failed to open %s for reading", passfile);
if ((tmp_fp = cvs_temp_file (&tmp_name)) == NULL)
error (1, errno, "unable to open temp file %s", tmp_name);
line = 0;
while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0)
{
line++;
if (line < found_at
|| (line != found_at
&& !password_entry_parseline (cvsroot_canonical, 0, line,
linebuf)))
{
if (fprintf (tmp_fp, "%s", linebuf) == EOF)
{
error (0, errno, "fatal error: cannot write %s", tmp_name);
if (fclose (tmp_fp) == EOF)
error (0, errno, "cannot close %s", tmp_name);
if (CVS_UNLINK (tmp_name) < 0)
error (0, errno, "cannot remove %s", tmp_name);
error (1, 0, "exiting");
}
}
}
if (line_length < 0 && !feof (fp))
{
error (0, errno, "cannot read %s", passfile);
goto error_exit;
}
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", passfile);
if (fclose (tmp_fp) < 0)
error (0, errno, "cannot close %s", tmp_name);
copy_file (tmp_name, passfile);
if (CVS_UNLINK (tmp_name) < 0)
error (0, errno, "cannot remove %s", tmp_name);
free (tmp_name);
}
if (!noexec && operation == password_entry_add
&& (password == NULL || strcmp (password, newpassword)))
{
if ((fp = CVS_FOPEN (passfile, "a")) == NULL)
error (1, errno, "could not open %s for writing", passfile);
if (fprintf (fp, "/1 %s %s\n", cvsroot_canonical, newpassword) == EOF)
error (1, errno, "cannot write %s", passfile);
if (fclose (fp) < 0)
error (1, errno, "cannot close %s", passfile);
}
chmod (passfile, 0600);
if (password)
{
free (password);
password = NULL;
}
if (linebuf)
free (linebuf);
out:
free (cvsroot_canonical);
free (passfile);
return password;
error_exit:
if (operation != password_entry_lookup)
error (1, 0, "fatal error: exiting");
save_errno = errno;
if (fp != NULL)
{
chmod (passfile, 0600);
if(fclose (fp) < 0)
error (0, errno, "cannot close %s", passfile);
}
if (linebuf)
free (linebuf);
if (cvsroot_canonical)
free (cvsroot_canonical);
free (passfile);
errno = save_errno;
return NULL;
}
static const char *const login_usage[] =
{
"Usage: %s %s\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
int
login (argc, argv)
int argc;
char **argv;
{
char *typed_password;
char *cvsroot_canonical;
if (argc < 0)
usage (login_usage);
if (current_parsed_root->method != pserver_method)
{
error (0, 0, "can only use `login' command with the 'pserver' method");
error (1, 0, "CVSROOT: %s", current_parsed_root->original);
}
cvsroot_canonical = normalize_cvsroot(current_parsed_root);
printf ("Logging in to %s\n", cvsroot_canonical);
fflush (stdout);
if (current_parsed_root->password)
{
typed_password = scramble (current_parsed_root->password);
}
else
{
char *tmp;
tmp = getpass ("CVS password: ");
if (!tmp) error (1, errno, "login: Failed to read password.");
typed_password = scramble (tmp);
memset (tmp, 0, strlen (tmp));
}
cvs_password = xstrdup (typed_password);
connect_to_pserver (current_parsed_root, NULL, NULL, 1, 0);
password_entry_operation (password_entry_add, current_parsed_root,
typed_password);
memset (typed_password, 0, strlen (typed_password));
free (typed_password);
free (cvs_password);
free (cvsroot_canonical);
cvs_password = NULL;
return 0;
}
char *
get_cvs_password ()
{
if (current_parsed_root->password)
return scramble (current_parsed_root->password);
if (cvs_password)
return cvs_password;
if (getenv ("CVS_PASSWORD") != NULL)
{
error (0, 0, "CVS_PASSWORD is no longer supported; ignored");
}
if (current_parsed_root->method != pserver_method)
{
error (0, 0, "can only call get_cvs_password with pserver method");
error (1, 0, "CVSROOT: %s", current_parsed_root->original);
}
return password_entry_operation (password_entry_lookup,
current_parsed_root, NULL);
}
static const char *const logout_usage[] =
{
"Usage: %s %s\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
int
logout (argc, argv)
int argc;
char **argv;
{
char *cvsroot_canonical;
if (argc < 0)
usage (logout_usage);
if (current_parsed_root->method != pserver_method)
{
error (0, 0, "can only use pserver method with `logout' command");
error (1, 0, "CVSROOT: %s", current_parsed_root->original);
}
if (!quiet)
{
cvsroot_canonical = normalize_cvsroot(current_parsed_root);
printf ("Logging out of %s\n", cvsroot_canonical);
fflush (stdout);
free (cvsroot_canonical);
}
password_entry_operation (password_entry_delete, current_parsed_root, NULL);
return 0;
}
#endif