/*
    ncplogin.c ncpmap.c and possibly ncpmount.c
    Copyright (C) 1995, 1997 by Volker Lendecke


    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Revision history:
        1.00 2001 February 26           Patrick Pollet <patrick.pollet@insa-lyon.fr>
            Initial release
            This program is based on ncpmount.c release 1.07 2001, February 25	by Petr Vandrovec <vandrove@vc.cvut.cz>
            Basically it substitute the -S server parameter with -T tree -X context options
            Source code of ncplogin AND ncpmap (AND eventually ncpmount) should stays the same.
            See the Makefile.in for compile flags.
                CFLAGS_ncplogin.o = -DNWMSG=\"$(sbindir)/nwmsg\"
                CFLAGS_ncpmap.o = -DNWMSG=\"$(sbindir)/nwmsg\" -DNCPMAP=1
                CFLAGS_ncpmount.o = -DNWMSG=\"$(sbindir)/nwmsg\" -DNCPMOUNT=1

      1.01 2001 March 10                Patrick Pollet <patrick.pollet@insa-lyon.fr>
             Added failed in all error messages for easier TCL/tk parsing
             Added -'E' option to echo mount_point in case of success (for TCL/Tk)
             Shuffled some blocks of code to mimize #ifdef #ifndef
             Corrected:#1135 (make_unique_dir= forgot to autocreate ~/ncp!
             Corrected:#1397 wrong list of possible arguments to getopt()
             Corrected:#1754 else was missing.
             Corrected:#2147 ncplogin cannot use NWDSWhoami since we are not yet authenticated
             Corrected:forgot to free context at various errors points
             Corrected:incomplete help ncplogin,ncpmap (-X missing)

     1.02 2001 March 12                 Patrick Pollet <patrick.pollet@insa-lyon.fr>
             Corrected #1668  info.resourcename was not set to SYS with -S option.
             Note that with ncplogin/ncpmap: if volume is already mounted
              the message already mounted:name of mount point will be printed and exitcode=0 (#2201)

     1.03 2001 March 16                 Patrick Pollet <patrick.pollet@insa-lyon.fr>
             Added Default user logic if missing from command line (ncplogin only in TREE mode)

     1.04 2001 August 11                Petr Vandrovec
              Modified ncpmount part to match ncpmount v 1.08

     1.05 2001 October 25               Patrick Pollet <patrick.pollet@insa-lyon.fr>
                Added a signal(SIGPIPE, SIG_IGN) in process_connexion to match ncpmount v 1.08
                In progress: ncpmap does not complains in case of multiple connexions and mount it twice
                (2 entries in /etc/mtab)???
		not a problem with ncplogin since it check that user is not currently authenticated ...

     1.06 2001 December 8	        Patrick Pollet <patrick.pollet@insa-lyon.fr>
		ncplogin with -S option did not searched for missing -U user argument in environnment/nwclient
		file if not supplied on the command line. Code moved from the -T case to all cases.

     1.07 2002 January 20		Petr Vandrovec <vandrove@vc.cvut.cz>
		Apply checks on uid/gid/owner value, so normal user can no longer mount filesystem
		with someone else's uid.
		Convert fprintf(stderr, "failed:yyy"...) to eprintf("yyy"...). Now ncpmount and ncplogin
		use same .po entries.
		Convert eprintf() + exit() => errexit()

	1.08  2002, February 27		Petr Vandrovec <vandrove@vc.cvut.cz>
		Fix core dump when no username is specified to ncplogin.

	1.09  2002, February 27		Petr Vandrovec <vandrove@vc.cvut.cz>
		Erase password quickly from command line.
		Clear command line completely.
		Simpler command line handling.

	1.10  2002, April 19		Peter Schwindt <schwindt@ba-loerrach.de>
		Add option -A to the help text.

	1.11  2002, May 12		Patrick Pollet <patrick.pollet@insa-lyon.fr>
		On NFS mounted homes, local root user has normally no rights to peek in user's home, not speaking
		about autocreating there a mount point directory (unless NFS directories are exported with the no_root_squash
		option, which is quite unsafe !). So we autocreate mount point in a local /mnt/ncp/$USER directory.
		As usual this directory is autocreated and chmoded to 0700 ... To activate this, we added a -l command line option
		(for local mount).

 */

#include "ncpm_common.h"

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <pwd.h>
#include <ncp/ext/socket.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/mount.h>
#include <mntent.h>
#include <ncp/kernel/ipx.h>
#include <sys/ioctl.h>
#ifdef CONFIG_NATIVE_UNIX
#include <linux/un.h>
#endif

#ifndef NDS_SUPPORT
#	error "NDS_SUPPORT must be enabled for ncplogin/ncpmap"
#endif
#include <ncp/nwclient.h>

#include "private/libintl.h"
#define _(X) gettext(X)

#include "../lib/ncpi.h"
#include "mount_login.h"

static void usage(void);
static void help(void);

void veprintf(const char* message, va_list ap) {
	fprintf(stderr, "failed:");
	vfprintf(stderr, message, ap);
}

/*begin of "alternating code between ncplogin/ncpmap/ncpmount */
/*basically ncpmap has no user nor passord required
   thus all options regarding "user identity" (uid,gid, mount id ...) are marked off */
/*options of ncpmount irrelevant to both are marked off with #ifdef NCPMOUNT #endif */

/*ncplogin/ncpmap */
/*actually ncplogin does not care about this (it use -V SYS and -a internally) */
#ifdef NCPMAP
/* we will parse volume name later since in can be in NDS format */
static void opt_set_volume(struct ncp_mount_info* info, const char* param) {
	info->remote_path = param;
}

static void opt_set_auto_create(struct ncp_mount_info* info, unsigned int param) {
	info->autocreate = param;
}

struct optinfo extopts[] = {
	{'a', "autocreate",	FN_NOPARAM,	opt_set_auto_create,	1},/*not in ncplogin*/
	{'V', "volume",		FN_STRING,	opt_set_volume,		0},/*not in ncplogin*/
	{0,   NULL,		0,		NULL,			0}
};
#endif

        static int opt_set_volume_after_parsing_all_options(struct ncp_mount_info* info) {
                char tmpNWPath[1024];
		int e;

                /* we DID check in main that -V has been specified !*/
                strcpy(tmpNWPath,info->remote_path);
                if (info->root_path) {
                        strcat(tmpNWPath,"/");
                        strcat(tmpNWPath,info->root_path);
                }
                /* I keep forgeting typing it in uppercase so let's do it here */
                str_upper(tmpNWPath);
                info->pathlen = e = ncp_path_to_NW_format(tmpNWPath, info->NWpath, sizeof(info->NWpath));
	        if (e < 0) {
		        errexit(18, _("Volume path `%s' is invalid: `%s'\n"), tmpNWPath, strerror(-e));
	        };
	        if (info->pathlen == 1) {
		        info->mdata.mounted_vol = "";
		        info->remote_path = "/";
	        } else if (info->NWpath[0] != 1) {
		        info->mdata.mounted_vol = "dummy";
	        } else if (strlen(tmpNWPath) > NCP_VOLNAME_LEN) {
		        errexit(19, _("Volume name `%s' is too long\n"), tmpNWPath);
	        } else {
		        info->mdata.mounted_vol=info->remote_path;
	        }
                return 0;
        }


        /* new options ncplogin/ncpmap */
        static void opt_set_tree(struct ncp_mount_info* info, const char* param) {
		info->tree = param;
        }

        static void opt_set_root_path(struct ncp_mount_info* info, const char* param) {
	        info->root_path = param;
        }

        static void opt_set_name_context(struct ncp_mount_info* info, const char* param) {
                if (strlen(param)< sizeof(info->context))
	                strcpy(info->context,param);
                else{
	                errexit(19, _("Context name `%s' is too long\n"), param);
                }
        }

        static void opt_set_bcast_mode(struct ncp_mount_info* info, unsigned int param) {
	        info->bcastmode = param & 3;
        }

        static void opt_set_echo(struct ncp_mount_info* info, unsigned int param) {
	        info->echo_mnt_pnt = param;
        }

	static void opt_set_use_local_mnt_dir(struct ncp_mount_info* info, const char* param) {
	        info->use_local_mnt_dir = param ? param : "/mnt/ncp";
		info->autocreate = 1;  /* if -l present, force -a */
	}

static struct optinfo opts[] = {
	{'X', "namecontext",	FN_STRING,	opt_set_name_context,	1},/*unfortunate that C,c and N reserved*/
	{'T', "tree",		FN_STRING,	opt_set_tree,		0},
	{'R', "root",		FN_STRING,	opt_set_root_path,	0},
	{'B', "broadcast",	FN_INT,		opt_set_bcast_mode,	1},
	{'E', "echo",		FN_NOPARAM,	opt_set_echo,		1},
	{'l', "localmount",	FN_NOPARAM | FN_STRING,
						opt_set_use_local_mnt_dir, 0},
	{0,   NULL,		0,		NULL,			0}
};

static int make_unique_dir(struct ncp_mount_info* info, char * mpnt, size_t maxl,
		const char* p1, const char* p2) {
	struct stat st;
	size_t ln;
	char* curr;
	const struct passwd* pwd;

	if (!info->server || !info->NWpath || !info->NWpath[0]) return -1;

	pwd = getpwuid(info->mdata.uid);
	if (!pwd) return -1;

	if (!p2) {
		p2 = pwd->pw_name;
	}
	if (!p1) {
		p1 = pwd->pw_dir;
	} else {
		if (stat(p1, &st)) {
			if (mkdir(p1, 0711)) {
				return -1;
			}
		}
	}

	curr = mpnt;

	ln = snprintf(curr, maxl, "%s/%s", p1, p2);
	if (ln >= maxl) {
		return E2BIG;
	}
	if (stat(mpnt, &st)) {
		if (mkdir(mpnt, 0700) || chown(mpnt, pwd->pw_uid, pwd->pw_gid)) {
			return -1;
		}
	}
	curr[ln] = '/';
	curr += ln + 1;
	maxl -= ln + 1;

	ln = strlen(info->server);
	if (ln >= maxl) {
		return E2BIG;
	}
	memcpy(curr, info->server, ln);
	curr[ln] = 0;
	str_upper(curr);
	curr += ln;
	maxl -= ln;
	if (stat(mpnt, &st)){
		if (mkdir(mpnt,0700) || chown(mpnt, pwd->pw_uid, pwd->pw_gid)) {
			return -1;
		}
	}
	ln = info->NWpath[1];	/*length of NW fragments*/
	if (ln + 1 >= maxl) {
		return E2BIG;
	}
	*curr++ = '/';
	memcpy(curr, info->NWpath + 2, ln);
	curr[ln] = 0;
	str_upper(curr);
	if (stat(mpnt, &st)) {
		if (mkdir(mpnt, 0700) || chown(mpnt, pwd->pw_uid, pwd->pw_gid)) {
			return -1;
		}
	}
	return 0;
}

int
main(int argc, char *argv[])
{
	struct ncp_mount_info info;
	struct stat st;
	char mount_name[256];

	int result;

	long err;

	char ntree[MAX_TREE_NAME_CHARS + 1];

#ifndef NCPMAP
	struct ncp_conn_spec spec;
#endif
	NWDSContextHandle ctx=NULL;
	struct ncp_conn *conn;

	int opt;

#ifdef CONFIG_NATIVE_UNIX
	struct sockaddr_un server_un;
#endif
	int mount_ncp = 0;
#ifndef NCPMAP
	int opt_n = 0;
#endif
	int opt_v = 0;

	verify_argv(argc, argv);

	setlocale(LC_ALL, "");
	bindtextdomain(NCPFS_PACKAGE, LOCALEDIR);
	textdomain(NCPFS_PACKAGE);

	init_mount_info(&info);

	if (geteuid() != 0)
	{
		errexit(26, _("%s must be installed suid root\n"), progname);
	}

/*ncplogin,ncpmap */
#ifdef NCPMAP
	/*same as ncpmount :minus all users,passwd , uids, guids
			   :plus  -R -a -T -X*/

	while ((opt = getopt(argc, argv, "S:f:d:h?vV:t:r:o:"
					 "sN:y:p:i:A:B:T:X:R:aEl"
#else
	/*same as ncpmount :plus -T, -X
			   :minus -b -m
	 ncplogin does not want -R -a */
	while ((opt = getopt(argc, argv, "CS:U:c:u:g:f:d:P:nh?vV:t:r:o:"
					 "sN:y:p:i:A:B:T:X:El"

#endif
#ifdef MOUNT2
		"2"
#endif
#ifdef MOUNT3
		"345"
#endif
		)) != EOF)
	{
		char tmpopt[2];

		switch (opt)
		{
#ifndef NCPMAP
		case 'C':
#else
		case 'a':
#endif
		case 's':
		case 'E':
		case 'l':

			optarg = NULL;
#ifndef NCPMAP
		case 'U':
		case 'c':
		case 'u':
		case 'g':
#else
		case 'V':
		case 'R':
#endif
		case 'S':
		case 'T':
		case 'X':
		case 'f':
		case 'd':
		case 't':
		case 'r':
		case 'i':
		case 'A':
		case 'y':
		case 'p':
		case 'B':
			tmpopt[0] = opt;
			tmpopt[1] = 0;
			proc_option(opts, &info, tmpopt, optarg);
			break;
#ifndef NCPMAP
		case 'P':
			if (!mount_ncp) opt_n = 0;	/* clear -n (nopasswd) */
			tmpopt[0] = opt;
			tmpopt[1] = 0;
			proc_option(opts, &info, tmpopt, optarg);
			break;
		case 'n':
			opt_n = 1;	/* no passwd or no update /etc/fstab */
			break;
#endif
		case 'h':
		case '?':
			help();
			exit(27);
		case 'v':
			opt_v = 1;	/* version or verbose */
			break;
#ifdef MOUNT2
		case '2':
			info.version = 2;
			break;
#endif /* MOUNT2 */
#ifdef MOUNT3
		case '3':
			info.version = 3;
			break;
		case '4':
			info.version = 4;
			break;
		case '5':
			info.version = 5;
			break;
#endif /* MOUNT3 */
		case 'N':
			{
				char *inp;
				char *out;

				inp = optarg;
				while ((out = strtok(inp, ",;:"))!=NULL) {
					inp=NULL;
					if (!strcasecmp(out, "OS2"))
						proc_option(opts, &info, "noos2", NULL);
					else if (!strcasecmp(out, "LONG"))
						proc_option(opts, &info, "nolong", NULL);
					else if (!strcasecmp(out, "NFS"))
						proc_option(opts, &info, "nonfs", NULL);
					else {
						errexit(128, _("Unknown namespace \"%s\"\n"), out);
					}
				}
			};
			break;
		case 'o':
			{
				char* arg;

				arg = optarg;
				mount_ncp = 1;
				while (arg) {
					char* end;
					char* parm;

					arg += strspn(arg, " \t");
					if (!*arg) break;
					end = strchr(arg, ',');
					if (end) *end++ = 0;

					parm = strchr(arg, '=');
					if (parm) *parm++ = 0;

					proc_option(opts, &info, arg, parm);
					arg = end;
				};
			}
			break;
		default:
			eprintf(_("invalid option: %c\n"),opt);
			usage();
			return -1;
		}
	}

#ifndef NCPMAP
	if (opt_n && !mount_ncp) {
		opt_n = 0;
		proc_option(opts, &info, "passwd", "");
	}
#endif
	if (opt_v && !mount_ncp) {
		fprintf(stderr, _("ncpfs version %s\n"), NCPFS_VERSION);
		exit(30);
	}
	/*ncplogin -a not allowed.
	ncpmap only :added autocreate feature as LinuxNeighborood GUI utility for samba client*/
#ifdef NCPMAP
	if (optind != argc - 1){
		if (!info.autocreate) {
			usage();
			return -1;
		}
		/* we will autocreate later since NCPMAP may have to resolve NDS volume name!*/
	} else {
		realpath(argv[optind], mount_point);
		info.autocreate = 0; /*turn it off user has provided a mount point*/
	}
#else
	if (optind != argc) {
		usage();
		return -1;
	}
	info.autocreate = 1; /*forced in ncplogin */
#endif

	if (myuid != info.mdata.mounted_uid) {
		if (setreuid(info.mdata.mounted_uid, -1)) {
			errexit(76, _("Cannot impersonate as requested: %s\n"), strerror(errno));
		}
	}


	/* processing new arguments */
	/* FIX ME: so far I do not consider info.server_name (IP name) nor bindery only */
#ifdef NCPMAP
	if (!info.remote_path) {
		errexit(100, _("You must specify a volume to mount using -V option.\n"));
	}
#endif
	if (info.server && info.tree) {
		errexit(101, _("Cannot have both -T tree and -S server options\n"));
	}

	/* if a server has been specified, get its NDS status and tree */
	if (info.server) {
		err = NWCCOpenConnByName (NULL,info.server,NWCC_NAME_FORMAT_BIND,0,NWCC_RESERVED,&conn);
		if (err) {
			errexit(102, _("Unable to open connection to %s.\n"),info.server);
		}
		if (!NWCXIsDSServer(conn, ntree)) { /* we have removed trailing ---*/
			NWCCCloseConn(conn);
#ifdef NCPMAP
			errexit(103, _("%s is not a NDS server, so background authentication will fail.\n"), info.server);
#else
			errexit(103, _("%s is not a NDS server, so NDS authentication will fail.\n"), info.server);
#endif
		}
		NWCCCloseConn(conn);
		err = NWDSCreateContextHandleMnt(&ctx, ntree);
		if (err) {
			errexit(104, _("Cannot create NDS context handle: %s\n"), strerror(err));
		}
#ifdef NCPMAP
		if (!NWCXIsDSAuthenticated(ctx)) {
			NWDSFreeContext(ctx);
			errexit(105, _("Server %s belong to tree %s and you are not authenticated to it.\n"), info.server, ntree);
		}
#else
		if (NWCXIsDSAuthenticated(ctx)) {
			NWDSFreeContext(ctx);
			errexit(105, _("Server %s belong to tree %s and you are already authenticated to it.\n"),info.server, ntree);
		}
		/* with ncplogin use SYS as volume name */
		strcpy(info.resourceName, "SYS");
		info.remote_path=info.resourceName;
#endif
		info.tree = ntree;
		/*in that case the volume name must be in bindery format */
	} else {
		/* try to get a default tree from env variable */
		/* we are using the same env names as the Caldera client (hope they won't mind)
		#define PREFER_TREE_ENV "NWCLIENT_PREFERRED_TREE"
		#define PREFER_CTX_ENV "NWCLIENT_DEFAULT_NAME_CONTEXT"
		*/
		int flags = 0;

		if (!info.tree) {
			NWCXGetPreferredDSTree(ntree, sizeof(ntree));
			info.tree = ntree;
		}

		err = NWDSCreateContextHandleMnt (&ctx,info.tree);
		if (err) {
			errexit(104, _("Cannot create NDS context handle: %s\n"),strnwerror(err));
		}
#ifdef NCPMAP
		if (!NWCXIsDSAuthenticated(ctx)) {
			NWDSFreeContext(ctx);
			errexit(105, _("You are not authenticated to tree %s.\n"),info.tree);
		}
#else
		if (NWCXIsDSAuthenticated(ctx)) {
			NWDSFreeContext(ctx);
			errexit(105, _("You are already authenticated to tree %s.\n"),info.tree);
		}
#endif

		err = NWDSGetContext(ctx, DCK_FLAGS, &flags);
		if (!err) {
			flags |= DCV_XLATE_STRINGS | DCV_TYPELESS_NAMES | DCV_DEREF_ALIASES;
			err = NWDSSetContext(ctx, DCK_FLAGS, &flags);
		}

		if (err) {
			NWDSFreeContext(ctx);
			errexit(110, _("NWDSGetContext/NWDSSetContext (DCK_FLAGS) failed: %s.\n"), strnwerror(err));
		}
		/* no -X argument */
		if (!info.context[0])
			NWCXGetDefaultNameContext(info.tree,info.context,sizeof(info.context));
		/* got one */
		if (info.context[0]) {
			err = NWDSSetContext(ctx, DCK_NAME_CONTEXT, info.context);
			if (err) {
				errexit(106, _("NWDSSetContext(DCK_NAME_CTX) failed: %s\n"), strnwerror(err));
			}
		}

#ifdef NCPMAP
		/*resolve NDS volume name */

		if ((err=NWCXGetNDSVolumeServerAndResourceName(ctx,info.remote_path,
				info.serverName,sizeof(info.serverName),
				info.resourceName,sizeof(info.resourceName)))) {
			NWDSFreeContext(ctx);
			errexit(105, _("Cannot resolve volume name %s on tree %s (using context %s). Err:%s\n"),
				info.remote_path,info.tree,info.context[0]?info.context:"[Root]",strnwerror(err));
		} else {
			err = NWCXSplitNameAndContext(ctx,info.serverName,info.serverName,NULL);
			info.server=info.serverName;
			info.remote_path=info.resourceName;
		}
#else
		/* get a server name from that tree
		   NWCXAttchToTreeByName do search for a default server in env
		 */

		if ((err=NWCXAttachToTreeByName (&conn,info.tree))) {
			NWDSFreeContext(ctx);
			errexit(108, _("Cannot attach to tree %s. Err:%s\n"),
				info.tree,strnwerror(err));
		}
		if ((err=NWCCGetConnInfo(conn,NWCC_INFO_SERVER_NAME,sizeof(info.serverName),info.serverName))){
			NWCCCloseConn(conn);
			NWDSFreeContext(ctx);
			errexit(109, _("Cannot get server name from connection to tree %s. Err:%s\n"),
				info.tree,strnwerror(err));
		}

		/* use SYS volume that should always exist */
		strcpy(info.resourceName, "SYS");
		info.server = info.serverName;
		info.remote_path = info.resourceName;
		NWCCCloseConn(conn);
#endif
	}
/* v 1.06 make search of  DefaultUserName (ncplogin/ncpmount  only) moved here so it is called with both -T or -S options*/
#ifndef NCPMAP
	if (!info.user) {
		if (!NWCXGetDefaultUserName(info.tree,info.defaultUser,sizeof(info.defaultUser))) {
			info.user = info.defaultUser;
		} else {
			NWDSFreeContext(ctx);
			errexit(110, _("No user name found in cmd line nor in env\n"));
		}
	}
#endif
	/* just now do what ncpmount.c used to do where parsing -V option */
	err=opt_set_volume_after_parsing_all_options(&info);
	if (err) {
		NWDSFreeContext(ctx);
		exit(err);
	}

	/*printf ("Ok we will mount %s:%s:%s in %s\n",info.server,info.remote_path,info.root_path,mount_point);*/

	/* try autocreation of mount_point must be done AFTER NCPMAP specific part cause it is
	   only now that server and volume are correct*/
	if (info.autocreate) {
		/* printf ("try autocreate %s\n","");*/

		if (info.use_local_mnt_dir) {
			err = make_unique_dir(&info, mount_point, sizeof(mount_point), info.use_local_mnt_dir, NULL);
		} else {
			err = make_unique_dir(&info, mount_point, sizeof(mount_point), NULL, "ncp");
		}
		if (err) {
			NWDSFreeContext(ctx);
			errexit(-1, _("Could not autocreate mount point %s: %s\n"),mount_point, strerror(err));
		}
	}

	/* final checking it's time to do it for all ! */
	if (stat(mount_point, &st) == -1)
	{
		err = errno;
		NWDSFreeContext(ctx);
		errexit(31, _("Could not find mount point %s: %s\n"),
			mount_point, strerror(err));
	}
	if (mount_ok(&st) != 0)
	{
		err = errno;
		NWDSFreeContext(ctx);
		errexit(32, _("Cannot mount on %s: %s\n"), mount_point, strerror(err));
	}
	/* ncplogin: now that we have the server NAME in bindery format we can do it */
#ifndef NCPMAP

	/* no user/pwd in ncpmap */
	get_passwd(&info);

	if ((err = ncp_find_conn_spec3(info.server, info.user, info.password, 1, info.mdata.uid, info.allow_multiple_connections, &spec))
	    != 0)
	{
		mycom_err(err, _("in find_conn_spec"));
		exit(33);
	}
	/* I am afraid it's too late. ncp_find_conn_spec3 did it */
	if (info.upcase_password != 0)
	{
		str_upper(spec.password);
	}

#endif

#ifndef NCPMAP
	info.mdata.server_name = spec.server;
#endif
	if (info.mdata.dir_mode == 0)
	{
		info.mdata.dir_mode = info.mdata.file_mode;
		if ((info.mdata.dir_mode & S_IRUSR) != 0)
			info.mdata.dir_mode |= S_IXUSR;
		if ((info.mdata.dir_mode & S_IRGRP) != 0)
			info.mdata.dir_mode |= S_IXGRP;
		if ((info.mdata.dir_mode & S_IROTH) != 0)
			info.mdata.dir_mode |= S_IXOTH;
	}
#if defined(CONFIG_NATIVE_IP) || defined(CONFIG_NATIVE_UNIX)
	if (info.server_name) {
#if defined(CONFIG_NATIVE_UNIX)
		if (info.server_name[0] == '/') {
			server_un.sun_family = AF_UNIX;
			memcpy(server_un.sun_path, "\000ncpfs", 6);
			memcpy(&info.mdata.serv_addr, &server_un, 16);
		} else
#endif
#if defined(CONFIG_NATIVE_IP)
		{
			nuint tran;
			tran = NT_UDP;
			if (info.nt.valid && info.nt.value == NT_TCP) {
				tran = info.nt.value;
			}
			err = ncp_find_server_addr(&info.server_name, OT_FILE_SERVER, (struct sockaddr*)&info.mdata.serv_addr,
					sizeof(info.mdata.serv_addr), tran);
			if (err) {
				eprintf(_("Get host address `%s': %s\n"), info.server_name, strnwerror(err));
				NWDSFreeContext(ctx);
				exit(1);
			}
		}
#else
		{
			goto ncpipx;
		}
#endif
	} else
#endif	/* CONFIG_NATIVE_IP */
	{
ncpipx:;
#ifndef NCPMAP	/*ncpmount & ncplogin */
		if ((err = ncp_find_fileserver(spec.server, (struct sockaddr*)&info.mdata.serv_addr, sizeof(info.mdata.serv_addr))) != 0)
		{
			mycom_err(err, _("when trying to find %s"), spec.server);
			NWDSFreeContext(ctx);
			exit(36);
		}
#else

		if ((err = ncp_find_fileserver(info.server, (struct sockaddr*)&info.mdata.serv_addr, sizeof(info.mdata.serv_addr))) != 0)
		{
			mycom_err(err, _("when trying to find %s"), info.server);
			NWDSFreeContext(ctx);
			exit(36);
		}
#endif
	}

	err = proc_buildconn(&info);
	if (err) {
		NWDSFreeContext(ctx);
		exit(err);
	}

	info.mdata.mount_point = mount_point;
#ifndef NCPMAP
	strcpy(mount_name, spec.server);
	strcat(mount_name, "/");
	strcat(mount_name, spec.user);
#else
	{
/* v 1.05 test for no multiple mount */
		char nwuser[256]="";
/*		struct ncp_conn_spec tmpSpec; */

		strcpy(mount_name, info.server);
		strcat(mount_name, "/");
		if ((err = NWDSWhoAmI(ctx,nwuser))==0) {
		/* v 1.05 remove context in /etc/mtab entry */
			NWCXSplitNameAndContext(ctx,nwuser,nwuser,NULL);
			str_upper(nwuser);
			strcat(mount_name,nwuser);
/* does not work
    if current user has another connexion to that server, even to another volume:path
    ncp_find_permanent does not care about volume name nor "root path"
                        memset(&tmpSpec, 0, sizeof(tmpSpec));
                        strcpy(tmpSpec.user,nwuser);
                        strcpy(tmpSpec.server,info.serverName);
                        tmpSpec.uid=info.mdata.uid;
                        if ((!info.allow_multiple_connections)&&
		    ((tmp_mount = ncp_find_permanent(&tmpSpec)) != NULL))
		{
			fprintf(stderr,
				_("You already have mounted server %s\nas user "
				"%s\non mount point %s\n"), tmpSpec.server, tmpSpec.user,
				tmp_mount);
			exit(35);
		}
***************/
		} else {
			NWDSFreeContext(ctx);
			errexit(51, _("NWDSWhoAmi returned %s\n"), strnwerror(err));
		}
	}
#endif
	result = ncp_mount(mount_name, &info);
	if (result)
	{
		if (result == EBUSY) {
			/* for TCL/tk to catch it*/
			fprintf(stderr, _("already mounted:%s\n"), mount_point);
		} else {
			mycom_err(result, _("failed in mount(2)"));
		}
		NWDSFreeContext(ctx);
		/*exit code stays 0 for TCL/tk to be fixed...*/
		exit(0);
	}
	err = proc_aftermount(&info, &conn);
	if (err) {
		NWDSFreeContext(ctx);
		umount(mount_point);
		exit(err);
	}
#ifndef NCPMAP
#ifdef NDS_SUPPORT
	if (!info.force_bindery_login && NWIsDSServer(conn, NULL))
	{
		if ((err = nds_login_auth(conn, spec.user, spec.password)))
		{
			if (err != NWE_PASSWORD_EXPIRED) {
				mycom_err(err, _("failed in nds login"));
				fprintf(stderr, _("Login denied.\n"));
				ncp_close(conn);
				umount(mount_point);
				NWDSFreeContext(ctx);
				exit(55);
			}
			fprintf(stderr, _("Your password has expired\n"));
		}
	}
	else
#endif
	{
		if ((err = ncp_login_user(conn, spec.user, spec.password)) != 0) {
			struct nw_property p;
			struct ncp_prop_login_control *l = (struct ncp_prop_login_control *) &p;

			if (err != NWE_PASSWORD_EXPIRED) {
				mycom_err(err, _("in login"));
				fprintf(stderr, _("Login denied\n"));
				ncp_close(conn);
				umount(mount_point);
				NWDSFreeContext(ctx);
				exit(56);
			}
			fprintf(stderr, _("Your password has expired\n"));

			if ((err = ncp_read_property_value(conn, NCP_BINDERY_USER,
							   spec.user, 1,
							   "LOGIN_CONTROL", &p)) == 0) {
				fprintf(stderr, _("You have %d login attempts left\n"),
					l->GraceLogins);
			}
		}
	}
#else	/*ncpmap only*/
	err = NWDSAddConnection(ctx, conn);
	if (err) {
		ncp_close(conn);
		umount(mount_point);
		NWDSFreeContext(ctx);
		errexit(110, _("Cannot attach connection to context: %s\n"), strnwerror(err));
	}
	err = NWDSAuthenticateConn(ctx, conn);
	if (err) {
		ncp_close(conn);
		umount(mount_point);
/*one day I will be in trouble here, when NWDSFreeContext() will free all added connections */
		NWDSFreeContext(ctx);
		errexit(112, _("Cannot authenticate connection: %s\n"), strnwerror(err));
	}
#endif

	err = NWSetBroadcastMode(conn,info.bcastmode); /*ignore error for now */
	NWDSFreeContext(ctx); /*free at last no more ifndef NCPMOUNT below */

	if ((err = ncp_mount_specific(conn, -1, info.NWpath, info.pathlen)) != 0)
	{
		ncp_close(conn);
		umount(mount_point);
		errexit(57, _("Cannot access path \"%s\": %s\n"), info.remote_path, strerror(-err));
	}
	NWCCCloseConn(conn);
	/*ncpmap, ncplogin must write in /etc/mtab*/
	{
		add_mnt_entry(mount_name, mount_point, info.flags);
	}
	if (info.echo_mnt_pnt) {
		printf(_("mounted on:%s\n"),mount_point);
	}
	return 0;
}

static void usage(void)
{
#ifdef NCPMAP
	printf(_("usage: %s [options] mount-point\n"), progname);
#else
	printf(_("usage: %s [options]\n"), progname);
#endif
	printf(_("Try `%s -h' for more information\n"), progname);
}

static void
help(void)
{
	const char* str;
	const char* str2;

#ifdef NCPMAP
	printf(_("\n"
		 "usage: %s [options] mount-point\n"), progname);
#else
	printf(_("\n"
		 "usage: %s [options]\n"), progname);
#endif
#ifdef NCPMAP
	str = _(" mount_point is optional if -a option specified\n");
#else
	str = "";
#endif
	printf(_("\n"
		 "%s"
		 "-T tree        Tree name to be used\n"
		 "-S server      Server name to be used\n"
		 "                 these two options are exclusive\n"
		 "-X name_ctx    Default name context to be used\n"
		 "-E             Echo value of final mount_point\n")
		 , str);

#ifndef NCPMAP
	printf(_("\n"
		 "-U username    Username sent to server\n"
		 "-u uid         uid the mounted files get\n"
		 "-g gid         gid the mounted files get\n"
		 "-c uid         uid to identify the connection to mount on\n"
		 "               Only makes sense for root\n"
		 "-C             Don't convert password to uppercase\n"
		 "-P password    Use this password\n"
		 "-n             Do not use any password\n"
		 "               If neither -P nor -n are given, you are\n"
		 "               asked for a password.\n")
	);
#else
	printf(_("\n"
		 "-V volume      Volume to mount\n"
		 "                 must be in bindery format if -S server\n"
		 "                 or in NDS format if -T tree\n"
		 "-R path        Path in volume to 'map root'\n")
	);
#endif
#ifdef SIGNATURES
	str = _("-i level       Signature level, 0=never, 1=supported, 2=preferred, 3=required\n");
#else
	str = "";
#endif
#ifdef NCPMAP
	str2 = _("-a             Autocreate mounting point if needed in ~/ncp/SERVER/VOLUME\n");
#else
	str2 = "";
#endif
	printf(_("\n"
               "-f mode        permission the files get (octal notation)\n"
	       "-d mode        permission the dirs get (octal notation)\n"

	       "-t time_out    Waiting time (in 1/100s) to wait for\n"
	       "               an answer from the server. Default: 60\n"
	       "-r retry_count Number of retry attempts. Default: 5\n"

	       "-s             Enable renaming/deletion of read-only files\n"
	       "-h             print this help text\n"
	       "-v             print ncpfs version number\n"
	       "%s"
	       "-N os2,nfs     Do not use specified namespaces on mounted volume\n"
	       "-y charset     character set used for input and display\n"
	       "-p codepage    codepage used on volume, including letters `cp'\n"
               "-B bcast       Broadcast mode =0 all 1= console 2= none (default=all)\n"
               "%s"
	       "-l             Autocreate mounting point if needed in /mnt/ncp/SERVER/VOLUME\n"
	       "\n"),
                str, str2);
}

