diff options
author | Peter Wemm <peter@FreeBSD.org> | 2001-10-28 21:32:14 +0000 |
---|---|---|
committer | Peter Wemm <peter@FreeBSD.org> | 2001-10-28 21:32:14 +0000 |
commit | 168faa4bcd6f265eb905024ec71b52836bed921d (patch) | |
tree | aae597ee0402894f2932f99a0fc23a9e95d7997c /contrib/cvs/src/login.c | |
parent | ecebe176bfcf8c557054a43352b09bf6d7a3bdc7 (diff) | |
download | src-168faa4bcd6f265eb905024ec71b52836bed921d.tar.gz src-168faa4bcd6f265eb905024ec71b52836bed921d.zip |
MFC: cvs-1.11.1-p1
Notes
Notes:
svn path=/stable/4/; revision=85643
Diffstat (limited to 'contrib/cvs/src/login.c')
-rw-r--r-- | contrib/cvs/src/login.c | 758 |
1 files changed, 454 insertions, 304 deletions
diff --git a/contrib/cvs/src/login.c b/contrib/cvs/src/login.c index cc564c066837..df6abe181073 100644 --- a/contrib/cvs/src/login.c +++ b/contrib/cvs/src/login.c @@ -87,26 +87,183 @@ construct_cvspass_filename () return passfile; } -static const char *const login_usage[] = + + +/* + * static char * + * password_entry_parseline ( + * const char *cvsroot_canonical, + * const unsigned char warn, + * const int linenumber, + * char *linebuf + * ); + * + * Internal function used by password_entry_operation. Parse a single line + * from a ~/.cvsroot password file and return a pointer to the password if the + * line refers to the same cvsroot as cvsroot_canonical + * + * INPUTS + * cvsroot_canonical the root we are looking for + * warn Boolean: print warnings for invalid lines? + * linenumber the line number for error messages + * linebuf the current line + * + * RETURNS + * NULL if the line doesn't match + * char *password as a pointer into linebuf + * + * NOTES + * This function temporarily alters linebuf, so it isn't thread safe when + * called on the same linebuf + */ +static char * +password_entry_parseline (cvsroot_canonical, warn, linenumber, linebuf) + const char *cvsroot_canonical; + const unsigned char warn; + const int linenumber; + char *linebuf; { - "Usage: %s %s\n", - "(Specify the --help global option for a list of other help options)\n", - NULL -}; + char *password = NULL; + char *p; -/* Prompt for a password, and store it in the file "CVS/.cvspass". + /* look for '^/' */ + if (*linebuf == '/') + { + /* Yes: slurp '^/\d+\D' and parse the rest of the line according to version number */ + char *q; + unsigned long int entry_version; + + if (isspace(*(linebuf + 1))) + /* special case since strtoul ignores leading white space */ + entry_version = 0; + else + entry_version = strtoul (linebuf + 1, &q, 10); + + if (q == linebuf + 1) + /* no valid digits found by strtoul */ + entry_version = 0; + else + /* assume a delimiting seperator */ + q++; + + switch (entry_version) + { + case 1: + /* this means the same normalize_cvsroot we are using was + * used to create this entry. strcmp is good enough for + * us. + */ + 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 + { + /* No: assume: + * + * ^cvsroot Aencoded_password$ + * + * as header comment specifies and parse accordingly + */ + 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; +} + + + +/* + * static char * + * password_entry_operation ( + * password_entry_operation_t operation, + * cvsroot_t *root, + * char *newpassword + * ); + * + * Search the password file and depending on the value of operation: + * + * Mode Action + * password_entry_lookup Return the password + * password_entry_delete Delete the entry from the file, if it exists + * password_entry_add Replace the line with the new one, else append it * * Because the user might be accessing multiple repositories, with * different passwords for each one, the format of ~/.cvspass is: * - * user@host:/path Acleartext_password - * user@host:/path Acleartext_password + * [user@]host:[port]/path Aencoded_password + * [user@]host:[port]/path Aencoded_password * ... * - * Of course, the "user@" might be left off -- it's just based on the - * value of CVSroot. + * New entries are always of the form: + * + * /1 user@host:port/path Aencoded_password * - * The "A" before "cleartext_password" is a literal capital A. It's a + * but the old format is supported for backwards compatibility. + * The entry version string wasn't strictly necessary, but it avoids the + * overhead of parsing some entries since we know it is already in canonical + * form and allows room for expansion later, say, if we want to allow spaces + * and/or other characters to be escaped in the string. Also, the new entries + * would have been ignored by old versions of CVS anyhow since those versions + * didn't know how to parse a port number. + * + * The "A" before "encoded_password" is a literal capital A. It's a * version number indicating which form of scrambling we're doing on * the password -- someday we might provide something more secure than * the trivial encoding we do now, and when that day comes, it would @@ -116,201 +273,318 @@ static const char *const login_usage[] = * it from being read by others. Unlike .netrc, we will not be * fascist about it, at most issuing a warning, and never refusing to * work. + * + * INPUTS + * operation operation to perform + * root cvsroot_t to look up + * newpassword prescrambled new password, for password_entry_add_mode + * + * RETURNS + * -1 if password_entry_lookup_mode not specified + * NULL on failed lookup + * pointer to a copy of the password string otherwise, which the caller is + * responsible for disposing of */ -int -login (argc, argv) - int argc; - char **argv; + +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 *typed_password, *found_password; - char *linebuf = (char *) NULL; - size_t linebuf_len; - int root_len, already_entered = 0; + char *cvsroot_canonical = NULL; + char *password = NULL; int line_length; + long line; + char *linebuf = NULL; + size_t linebuf_len; + char *p; + int save_errno = 0; - if (argc < 0) - usage (login_usage); + 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); + } + + /* Yes, the method below reads the user's password file twice when we have + * to delete an entry. It's inefficient, but we're not talking about a gig of + * data here. + */ - if (CVSroot_method != pserver_method) + passfile = construct_cvspass_filename (); + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) { - error (0, 0, "can only use pserver method with `login' command"); - error (1, 0, "CVSROOT: %s", CVSroot_original); + error (0, errno, "failed to open %s for reading", passfile); + goto error_exit; } - if (! CVSroot_username) + cvsroot_canonical = normalize_cvsroot (root); + + /* Check each line to see if we have this entry already. */ + line = 0; + while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + line++; + password = password_entry_parseline(cvsroot_canonical, 1, line, linebuf); + if (password != NULL) + /* this is it! break out and deal with linebuf */ + break; + } + if (line_length < 0 && !feof (fp)) { - error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", - CVSroot_original); - error (1, 0, "Please make sure to specify \"user@host\"!"); + error (0, errno, "cannot read %s", passfile); + goto error_exit; } + if (fclose (fp) < 0) + /* not fatal, unless it cascades */ + error (0, errno, "cannot close %s", passfile); + fp = NULL; - printf ("(Logging in to %s@%s)\n", CVSroot_username, CVSroot_hostname); - fflush (stdout); + /* Utter, total, raving paranoia, I know. */ + chmod (passfile, 0600); - passfile = construct_cvspass_filename (); - typed_password = GETPASS ("CVS password: "); - typed_password = scramble (typed_password); + /* a copy to return or keep around so we can reuse linebuf */ + if (password != NULL) + { + /* chomp the EOL */ + p = strchr (password, '\n'); + if (p != NULL) + *p = '\0'; + password = xstrdup (password); + } - /* Force get_cvs_password() to use this one (when the client - * confirms the new password with the server), instead of - * consulting the file. We make a new copy because cvs_password - * will get zeroed by connect_to_server(). */ + /* might as well return now */ + if (operation == password_entry_lookup) + goto out; - cvs_password = xstrdup (typed_password); + /* same here */ + if (operation == password_entry_delete && password == NULL) + { + error (0, 0, "Entry not found."); + goto out; + } - connect_to_pserver (NULL, NULL, 1, 0); + /* okay, file errors can simply be fatal from now on since we don't do + * anything else if we're in lookup mode + */ - /* IF we have a password for this "[user@]host:/path" already - * THEN - * IF it's the same as the password we read from the prompt - * THEN - * do nothing - * ELSE - * replace the old password with the new one - * ELSE - * append new entry to the end of the file. + /* copy the file with the entry deleted unless we're in add + * mode and the line we found contains the same password we're supposed to + * add */ + 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; - root_len = strlen (CVSroot_original); + /* open the original file again */ + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) + error (1, errno, "failed to open %s for reading", passfile); - /* Yes, the method below reads the user's password file twice. It's - inefficient, but we're not talking about a gig of data here. */ + /* create and open a temp file */ + if ((tmp_fp = cvs_temp_file (&tmp_name)) == NULL) + error (1, errno, "unable to open temp file %s", tmp_name); - fp = CVS_FOPEN (passfile, "r"); - /* FIXME: should be printing a message if fp == NULL and not - existence_error (errno). */ - if (fp != NULL) - { - /* Check each line to see if we have this entry already. */ + line = 0; while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - if (strncmp (CVSroot_original, linebuf, root_len) == 0) - { - already_entered = 1; - break; - } - } + { + line++; + if (line < found_at + || (line != found_at + && !password_entry_parseline(cvsroot_canonical, 0, line, linebuf))) + { + if (fprintf (tmp_fp, "%s", linebuf) == EOF) + { + /* try and clean up anyhow */ + error (0, errno, "fatal error: cannot write %s", tmp_name); + if (fclose (tmp_fp) == EOF) + error (0, errno, "cannot close %s", tmp_name); + /* call CVS_UNLINK instead of unlink_file since the file + * got created in noexec mode + */ + if (CVS_UNLINK (tmp_name) < 0) + error (0, errno, "cannot remove %s", tmp_name); + /* but quit so we don't remove all the entries from a + * user's password file accidentally + */ + error (1, 0, "exiting"); + } + } + } + if (line_length < 0 && !feof (fp)) + { + error (0, errno, "cannot read %s", passfile); + goto error_exit; + } if (fclose (fp) < 0) + /* not fatal, unless it cascades */ error (0, errno, "cannot close %s", passfile); - } - else if (!existence_error (errno)) - error (0, errno, "cannot open %s", passfile); - - if (already_entered) - { - /* This user/host has a password in the file already. */ - - strtok (linebuf, " "); - found_password = strtok (NULL, "\n"); - if (strcmp (found_password, typed_password)) - { - /* typed_password and found_password don't match, so we'll - * have to update passfile. We replace the old password - * with the new one by writing a tmp file whose contents are - * exactly the same as passfile except that this one entry - * gets typed_password instead of found_password. Then we - * rename the tmp file on top of passfile. + if (fclose (tmp_fp) < 0) + /* not fatal, unless it cascades */ + /* FIXME - does copy_file return correct results if the file wasn't + * closed? should this be fatal? */ - char *tmp_name; - FILE *tmp_fp; - - tmp_name = cvs_temp_name (); - if ((tmp_fp = CVS_FOPEN (tmp_name, "w")) == NULL) - { - error (1, errno, "unable to open temp file %s", tmp_name); - return 1; - } - chmod (tmp_name, 0600); - - fp = CVS_FOPEN (passfile, "r"); - if (fp == NULL) - { - error (1, errno, "unable to open %s", passfile); - if (linebuf) - free (linebuf); - return 1; - } - /* I'm not paranoid, they really ARE out to get me: */ - chmod (passfile, 0600); - - while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - if (strncmp (CVSroot_original, linebuf, root_len)) - { - if (fprintf (tmp_fp, "%s", linebuf) == EOF) - error (0, errno, "cannot write %s", tmp_name); - } - else - { - if (fprintf (tmp_fp, "%s %s\n", CVSroot_original, - typed_password) == EOF) - error (0, errno, "cannot write %s", tmp_name); - } - } - if (line_length < 0 && !feof (fp)) - error (0, errno, "cannot read %s", passfile); - if (linebuf) - free (linebuf); - if (fclose (tmp_fp) < 0) - error (0, errno, "cannot close %s", tmp_name); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", passfile); - - /* FIXME: rename_file would make more sense (e.g. almost - always faster). */ - copy_file (tmp_name, passfile); - if (unlink_file (tmp_name) < 0) - error (0, errno, "cannot remove %s", tmp_name); - chmod (passfile, 0600); - - free (tmp_name); - } + error (0, errno, "cannot close %s", tmp_name); + + /* FIXME: rename_file would make more sense (e.g. almost + * always faster). + * + * I don't think so, unless we change the way rename_file works to + * attempt a cp/rm sequence when rename fails since rename doesn't + * work across file systems and it isn't uncommon to have /tmp + * on its own partition. + * + * For that matter, it's probably not uncommon to have a home + * directory on an NFS mount. + */ + copy_file (tmp_name, passfile); + if (CVS_UNLINK (tmp_name) < 0) + error (0, errno, "cannot remove %s", tmp_name); + free (tmp_name); } - else + + /* in add mode, if we didn't find an entry or found an entry with a + * different password, append the new line + */ + if (!noexec && operation == password_entry_add + && (password == NULL || strcmp (password, newpassword))) { - if (linebuf) - free (linebuf); if ((fp = CVS_FOPEN (passfile, "a")) == NULL) - { - error (1, errno, "could not open %s", passfile); - free (passfile); - return 1; - } - - if (fprintf (fp, "%s %s\n", CVSroot_original, typed_password) == EOF) - error (0, errno, "cannot write %s", passfile); + 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 (0, errno, "cannot close %s", passfile); } /* Utter, total, raving paranoia, I know. */ chmod (passfile, 0600); + + if (password) + { + free (password); + password = NULL; + } + if (linebuf) + free (linebuf); + +out: + free (cvsroot_canonical); + free (passfile); + return password; + +error_exit: + /* just exit when we're not in lookup mode */ + if (operation != password_entry_lookup) + error (1, 0, "fatal error: exiting"); + /* clean up and exit in lookup mode so we can try a login with a NULL + * password anyhow in case that's what we would have found + */ + save_errno = errno; + if (fp != NULL) + { + /* Utter, total, raving paranoia, I know. */ + 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; +} + + + +/* Prompt for a password, and store it in the file "CVS/.cvspass". + */ + +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: "); + typed_password = scramble (tmp); + memset (tmp, 0, strlen (tmp)); + } + + /* Force get_cvs_password() to use this one (when the client + * confirms the new password with the server), instead of + * consulting the file. We make a new copy because cvs_password + * will get zeroed by connect_to_server(). */ + cvs_password = xstrdup (typed_password); + + connect_to_pserver (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 (passfile); free (cvs_password); + free (cvsroot_canonical); cvs_password = NULL; + return 0; } /* Returns the _scrambled_ password. The server must descramble before hashing and comparing. If password file not found, or - password not found in the file, just return NULL. */ + password not found in the file, just return NULL. */ char * get_cvs_password () { - int found_it = 0; - int root_len; - char *password = NULL; - char *linebuf = NULL; - size_t linebuf_len; - FILE *fp; - char *passfile; - int line_length; - + if (current_parsed_root->password) + return (scramble(current_parsed_root->password)); + /* If someone (i.e., login()) is calling connect_to_pserver() out of context, then assume they have supplied the correct, scrambled password. */ @@ -320,76 +594,24 @@ get_cvs_password () if (getenv ("CVS_PASSWORD") != NULL) { /* In previous versions of CVS one could specify a password in - CVS_PASSWORD. This is a bad idea, because in BSD variants - of unix anyone can see the environment variable with 'ps'. - But for users who were using that feature we want to at - least let them know what is going on. After printing this - warning, we should fall through to the regular error where - we tell them to run "cvs login" (unless they already ran - it, of course). */ - error (0, 0, "CVS_PASSWORD is no longer supported; ignored"); + * CVS_PASSWORD. This is a bad idea, because in BSD variants + * of unix anyone can see the environment variable with 'ps'. + * But for users who were using that feature we want to at + * least let them know what is going on. After printing this + * warning, we should fall through to the regular error where + * we tell them to run "cvs login" (unless they already ran + * it, of course). + */ + error (0, 0, "CVS_PASSWORD is no longer supported; ignored"); } - /* Else get it from the file. First make sure that the CVSROOT - variable has the appropriate fields filled in. */ - - if (CVSroot_method != pserver_method) + if (current_parsed_root->method != pserver_method) { - error (0, 0, "can only call GET_CVS_PASSWORD with pserver method"); - error (1, 0, "CVSROOT: %s", CVSroot_original); + error (0, 0, "can only call get_cvs_password with pserver method"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); } - if (! CVSroot_username) - { - error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", - CVSroot_original); - error (1, 0, "Please make sure to specify \"user@host\"!"); - } - - passfile = construct_cvspass_filename (); - fp = CVS_FOPEN (passfile, "r"); - if (fp == NULL) - { - free (passfile); - return NULL; - } - - root_len = strlen (CVSroot_original); - - /* Check each line to see if we have this entry already. */ - while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - if (strncmp (CVSroot_original, linebuf, root_len) == 0) - { - /* This is it! So break out and deal with linebuf. */ - found_it = 1; - break; - } - } - if (line_length < 0 && !feof (fp)) - error (0, errno, "cannot read %s", passfile); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", passfile); - - if (found_it) - { - /* linebuf now contains the line with the password. */ - char *tmp; - - strtok (linebuf, " "); - tmp = strtok (NULL, "\n"); - if (tmp == NULL) - error (1, 0, "bad entry in %s for %s", passfile, CVSroot_original); - - /* Give it permanent storage. */ - password = xstrdup (tmp); - memset (tmp, 0, strlen (password)); - } - - if (linebuf) - free (linebuf); - free (passfile); - return password; + return password_entry_operation (password_entry_lookup, current_parsed_root, NULL); } static const char *const logout_usage[] = @@ -405,29 +627,15 @@ logout (argc, argv) int argc; char **argv; { - char *passfile; - FILE *fp; - char *tmp_name = NULL; - FILE *tmp_fp; - char *linebuf = (char *) NULL; - size_t linebuf_len; - int root_len, found = 0; - int line_length; + char *cvsroot_canonical; if (argc < 0) usage (logout_usage); - if (CVSroot_method != pserver_method) + if (current_parsed_root->method != pserver_method) { error (0, 0, "can only use pserver method with `logout' command"); - error (1, 0, "CVSROOT: %s", CVSroot_original); - } - - if (! CVSroot_username) - { - error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", - CVSroot_original); - error (1, 0, "Please make sure to specify \"user@host\"!"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); } /* Hmm. Do we want a variant of this command which deletes _all_ @@ -438,73 +646,15 @@ logout (argc, argv) of security, in that it wouldn't delete entries from any .cvspass files but the current one. */ - printf ("(Logging out of %s@%s)\n", CVSroot_username, CVSroot_hostname); - fflush (stdout); - - /* IF we have a password for this "[user@]host:/path" already - * THEN - * drop the entry - * ELSE - * do nothing - */ - - passfile = construct_cvspass_filename (); - /* FIXME: This should not be in /tmp; that is almost surely a security - hole. Probably should just keep it in memory. */ - tmp_name = cvs_temp_name (); - if ((tmp_fp = CVS_FOPEN (tmp_name, "w")) == NULL) + if (!quiet) { - error (1, errno, "unable to open temp file %s", tmp_name); - return 1; - } - chmod (tmp_name, 0600); - - root_len = strlen (CVSroot_original); - - fp = CVS_FOPEN (passfile, "r"); - if (fp == NULL) - error (1, errno, "Error opening %s", passfile); - - /* Check each line to see if we have this entry. */ - /* Copy only those lines that do not match this entry */ - while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - if (strncmp (CVSroot_original, linebuf, root_len)) - { - if (fprintf (tmp_fp, "%s", linebuf) == EOF) - error (0, errno, "cannot write %s", tmp_name); - } - else - found = 1; - } - if (line_length < 0 && !feof (fp)) - error (0, errno, "cannot read %s", passfile); - - if (linebuf) - free (linebuf); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", passfile); - if (fclose (tmp_fp) < 0) - error (0, errno, "cannot close %s", tmp_name); - - if (! found) - { - printf ("Entry not found for %s\n", CVSroot_original); - if (unlink_file (tmp_name) < 0) - error (0, errno, "cannot remove %s", tmp_name); - } - else - { - /* FIXME: rename_file would make more sense (e.g. almost - always faster). */ - copy_file (tmp_name, passfile); - if (unlink_file (tmp_name) < 0) - error (0, errno, "cannot remove %s", tmp_name); - chmod (passfile, 0600); + cvsroot_canonical = normalize_cvsroot(current_parsed_root); + printf ("Logging out of %s\n", cvsroot_canonical); + fflush (stdout); + free (cvsroot_canonical); } - if (tmp_name) - free (tmp_name); + password_entry_operation (password_entry_delete, current_parsed_root, NULL); return 0; } |