root/usr/src/cmd/bnu/expfile.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * Copyright (c) 2001 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#ident  "%Z%%M% %I%     %E% SMI"        /* from SVR4 bnu:expfile.c 2.10 */

#include "uucp.h"

/*
 * expand file name expansion is based on first characters
 *      /       -> fully qualified pathname. no
 *                 processing necessary
 *      ~       -> prepended with login directory
 *      ~/      -> prepended with Pubdir
 *      default -> prepended with current directory
 *      file    -> filename to expand
 * returns:
 *      0       -> ok
 *      FAIL    -> no Wrkdir name available
 */
int
expfile(file)
register char *file;
{
        register char *fpart, *up;
        uid_t uid;
        char user[NAMESIZE], save[MAXFULLNAME];
        extern int gninfo(), canPath();

        if (strlcpy(save, file, sizeof (save)) >= sizeof (save))
                return(FAIL);
        if (*file != '/')
            if (*file ==  '~') {
                /* find / and copy user part */
                for (fpart = save + 1, up = user; *fpart != '\0'
                        && *fpart != '/'; fpart++)
                                *up++ = *fpart;
                *up = '\0';
                if ((user[0]=='\0') || (gninfo(user, &uid, file) != 0)){
                        (void) strcpy(file, Pubdir);
                }
                if (strlen(file) + strlen(fpart) + 1 > (unsigned)MAXFULLNAME)
                        return(FAIL);
                (void) strcat(file, fpart);
            } else {
                if (strlen(Wrkdir) + strlen(save) + 2 > (unsigned)MAXFULLNAME)
                        return(FAIL);
                (void) sprintf(file, "%s/%s", Wrkdir, save);
                if (Wrkdir[0] == '\0')
                        return(FAIL);
            }

        if (canPath(file) != 0) { /* I don't think this will ever fail */
            (void) strcpy(file, CORRUPTDIR);
            return(FAIL);
        } else
            return(0);
}


/*
 * make all necessary directories
 *      name    -> directory to make
 *      mask    -> mask to use during directory creation
 * return:
 *      0       -> success
 *      FAIL    -> failure
 */
int
mkdirs(name, mask)
mode_t mask;
register char *name;
{
        register char *p;
        mode_t omask;
        char dir[MAXFULLNAME];

        strcpy(dir, name);
        if (*LASTCHAR(dir) != '/')
                (void) strcat(dir, "/");
        p = dir + 1;
        for (;;) {
            if ((p = strchr(p, '/')) == NULL)
                return(0);
            *p = '\0';
            if (DIRECTORY(dir)) {
                /* if directory exists and is owned by uucp, child's
                    permissions should be no more open than parent */
                if (__s_.st_uid == UUCPUID)
                    mask |= ((~__s_.st_mode) & PUB_DIRMODE);
            } else {
                DEBUG(4, "mkdir - %s\n", dir);
                omask = umask(mask);
                if (mkdir(dir, PUB_DIRMODE) == FAIL) {
                    umask(omask);
                    return (FAIL);
                }
                umask(omask);
            }
            *p++ = '/';
        }
        /* NOTREACHED */
}

/*
 * expand file name and check return
 * print error if it failed.
 *      file    -> file name to check
 * returns:
 *      0       -> ok
 *      FAIL    -> if expfile failed
 */
int
ckexpf(file)
char *file;
{
        if (expfile(file) == 0)
                return(0);

        fprintf(stderr, gettext("Illegal filename (%s).\n"), file);
        return(FAIL);
}


/*
 * make canonical path out of path passed as argument.
 *
 * Eliminate redundant self-references like // or /./
 * (A single terminal / will be preserved, however.)
 * Dispose of references to .. in the path names.
 * In relative path names, this means that .. or a/../..
 * will be treated as an illegal reference.
 * In full paths, .. is always allowed, with /.. treated as /
 *
 * returns:
 *      0       -> path is now in canonical form
 *      FAIL    -> relative path contained illegal .. reference
 */

int
canPath(path)
register char *path;    /* path is modified in place */
{
    register char *to, *fr;

    to = fr = path;
    if (*fr == '/') *to++ = *fr++;
    for (;;) {
        /* skip past references to self and validate references to .. */
        for (;;) {
            if (*fr == '/') {
                fr++;
                continue;
            }
            if ((strncmp(fr, "./", 2) == SAME) || EQUALS(fr, ".")) {
                fr++;
                continue;
            }
            if ((strncmp(fr, "../", 3) == SAME) || EQUALS(fr, "..")) {
                fr += 2;
                /*      /.. is /        */
                if (((to - 1) == path) && (*path == '/')) continue;
                /* error if no previous component */
                if (to <= path) return (FAIL);
                /* back past previous component */
                while ((--to > path) && (to[-1] != '/'));
                continue;
            }
            break;
        }
        /*
         * What follows is a legitimate component,
         * terminated by a null or a /
         */
        if (*fr == '\0') break;
        while (((*to++ = *fr) != '\0') && (*fr++ != '/'));
    }
    /* null path is . */
    if (to == path) *to++ = '.';
    *to = '\0';
    return (0);
}