/* 
   Unix SMB/Netbios implementation.
   Version 2.0
   SMB client library implementation
   Copyright (C) Andrew Tridgell 1998
   Copyright (C) Richard Sharpe 2000
   Copyright (C) John Terpstra 2000
   
   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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"
#include "libsmbclient.h"

/* Structure for servers ... Held here so we don't need an include ...
 * May be better to put in an include file
 */

struct smbc_server {
  struct smbc_server *next, *prev;
  struct cli_state cli;
  dev_t dev;
  char *server_name;
  char *share_name;
  char *workgroup;
  char *username;
  BOOL no_pathinfo2;
};

/* Keep directory entries in a list */
struct smbc_dir_list {
  struct smbc_dir_list *next;
  struct smbc_dirent *dirent;
};

struct smbc_file {
  int cli_fd; 
  int smbc_fd;
  char *fname;
  off_t offset;
  struct smbc_server *srv;
  BOOL file;
  struct smbc_dir_list *dir_list, *dir_end, *dir_next;
  int dir_type, dir_error;
};

int smbc_fstatdir(int fd, struct stat *st); /* Forward decl */
BOOL smbc_getatr(struct smbc_server *srv, char *path, 
		 uint16 *mode, size_t *size, 
		 time_t *c_time, time_t *a_time, time_t *m_time,
		 SMB_INO_T *ino);

extern BOOL in_client;
static int smbc_initialized = 0;
static smbc_get_auth_data_fn smbc_auth_fn = NULL;
static int smbc_debug;
static int smbc_start_fd;
static int smbc_max_fd = 10000;
static struct smbc_file **smbc_file_table;
static struct smbc_server *smbc_srvs;
static pstring  my_netbios_name;
static pstring smbc_user;

/*
 * Function to parse a path and turn it into components
 *
 * We accept smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]]
 * 
 * smb://       means show all the workgroups
 * smb://name/  means, if name<1D> exists, list servers in workgroup,
 *              else, if name<20> exists, list all shares for server ...
 */

static const char *smbc_prefix = "smb:";

static int
smbc_parse_path(const char *fname, char *server, char *share, char *path,
		char *user, char *password) /* FIXME, lengths of strings */
{
  static pstring s;
  pstring userinfo;
  char *p;
  int len;

  server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
  pstrcpy(s, fname);

  /*  clean_fname(s);  causing problems ... */

  /* see if it has the right prefix */
  len = strlen(smbc_prefix);
  if (strncmp(s,smbc_prefix,len) || 
      (s[len] != '/' && s[len] != 0)) return -1; /* What about no smb: ? */

  p = s + len;

  /* Watch the test below, we are testing to see if we should exit */

  if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {

    return -1;

  }

  p += 2;  /* Skip the // or \\  */

  if (*p == (char)0)
    return 0;

  if (*p == '/') {

    strncpy(server, lp_workgroup(), 16); /* FIXME: Danger here */
    return 0;

  }

  /*
   * ok, its for us. Now parse out the server, share etc. 
   *
   * However, we want to parse out [[domain;]user[:password]@] if it
   * exists ...
   */

  if (strchr(p, '@')) { 
    pstring username, passwd, domain;
    char *u = userinfo;

    next_token(&p, userinfo, "@", sizeof(fstring));

    username[0] = passwd[0] = domain[0] = 0;

    if (strchr(u, ';')) {
      
      next_token(&u, domain, ";", sizeof(fstring));

    }

    if (strchr(u, ':')) {

      next_token(&u, username, ":", sizeof(fstring));

      pstrcpy(passwd, u);

    }
    else {

      pstrcpy(username, u);

    }

    if (username[0])
      strncpy(user, username, sizeof(fstring));  /* FIXME, size and domain */

    if (passwd[0])
      strncpy(password, passwd, sizeof(fstring)); /* FIXME, size */

  }

  if (!next_token(&p, server, "/", sizeof(fstring))) {

    return -1;

  }

  if (*p == (char)0) return 0;  /* That's it ... */
  
  if (!next_token(&p, share, "/", sizeof(fstring))) {

    return -1;

  }

  pstrcpy(path, p);
  
  all_string_sub(path, "/", "\\", 0);

  return 0;
}

/*
 * Convert an SMB error into a UNIX error ...
 */

int smbc_errno(struct cli_state *c)
{
	uint8 eclass;
	uint32 ecode;
	int ret;

	ret = cli_error(c, &eclass, &ecode, NULL);

	if (ret) {
		DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", 
			 (int)eclass, (int)ecode, (int)ecode, ret));
	}
	return ret;
}

/*
 * Connect to a server, possibly on an existing connection
 *
 * Here, what we want to do is: If the server and username
 * match an existing connection, reuse that, otherwise, establish a 
 * new connection.
 *
 * If we have to create a new connection, call the auth_fn to get the
 * info we need, unless the username and password were passed in.
 */

struct smbc_server *smbc_server(char *server, char *share, 
				char *workgroup, char *username, 
				char *password)
{
  struct smbc_server *srv=NULL;
  struct cli_state c;
  struct nmb_name called, calling;
  char *p, *server_n = server;
  fstring group;
  pstring ipenv;
  struct in_addr ip;
  extern struct in_addr ipzero;
  
  ip = ipzero;
  ZERO_STRUCT(c);

  /* try to use an existing connection */
  for (srv=smbc_srvs;srv;srv=srv->next) {
    if (strcmp(server,srv->server_name)==0 &&
	strcmp(share,srv->share_name)==0 &&
	strcmp(workgroup,srv->workgroup)==0 &&
	strcmp(username, srv->username) == 0) 
      return srv;
  }

  if (server[0] == 0) {
    errno = EPERM;
    return NULL;
  }

  /* 
   * Pick up the auth info here, once we know we need to connect
   * But only if we do not have a username and password ...
   */

  if (!username[0] || !password[0])
    smbc_auth_fn(server, share, workgroup, sizeof(fstring),
		 username, sizeof(fstring), password, sizeof(fstring));

  /* 
   * However, smbc_auth_fn may have picked up info relating to an 
   * existing connection, so try for an existing connection again ...
   */

  for (srv=smbc_srvs;srv;srv=srv->next) {
    if (strcmp(server,srv->server_name)==0 &&
	strcmp(share,srv->share_name)==0 &&
	strcmp(workgroup,srv->workgroup)==0 &&
	strcmp(username, srv->username) == 0) 
      return srv;
  }

  make_nmb_name(&calling, my_netbios_name, 0x0);
  make_nmb_name(&called , server, 0x20);

  DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
  
  if ((p=strchr(server_n,'#')) && 
      (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
    
    fstrcpy(group, server_n);
    p = strchr(group,'#');
    *p = 0;
		
  }

  DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));

 again:
  slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);

  ip = ipzero;

  /* have to open a new connection */
  if (!cli_initialise(&c) || !cli_connect(&c, server_n, &ip)) {
    errno = ENOENT;
    return NULL;
  }

  if (!cli_session_request(&c, &calling, &called)) {
    cli_shutdown(&c);
    if (strcmp(called.name, "*SMBSERVER")) {
      make_nmb_name(&called , "*SMBSERVER", 0x20);
      goto again;
    }
    errno = ENOENT;
    return NULL;
  }
  
  DEBUG(4,(" session request ok\n"));
  
  if (!cli_negprot(&c)) {
    cli_shutdown(&c);
    errno = ENOENT;
    return NULL;
  }

  if (!cli_session_setup(&c, username, 
			 password, strlen(password),
			 password, strlen(password),
			 workgroup) &&
      /* try an anonymous login if it failed */
      !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
    cli_shutdown(&c);
    errno = EPERM;
    return NULL;
  }

  DEBUG(4,(" session setup ok\n"));

  if (!cli_send_tconX(&c, share, "?????",
		      password, strlen(password)+1)) {
    errno = smbc_errno(&c);
    cli_shutdown(&c);
    return NULL;
  }
  
  DEBUG(4,(" tconx ok\n"));
  
  srv = (struct smbc_server *)malloc(sizeof(*srv));
  if (!srv) {
    errno = ENOMEM;
    goto failed;
  }

  ZERO_STRUCTP(srv);

  srv->cli = c;

  srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));

  srv->server_name = strdup(server);
  if (!srv->server_name) {
    errno = ENOMEM;
    goto failed;
  }

  srv->share_name = strdup(share);
  if (!srv->share_name) {
    errno = ENOMEM;
    goto failed;
  }

  srv->workgroup = strdup(workgroup);
  if (!srv->workgroup) {
    errno = ENOMEM;
    goto failed;
  }

  srv->username = strdup(username);
  if (!srv->username) {
    errno = ENOMEM;
    goto failed;
  }

  DLIST_ADD(smbc_srvs, srv);

  return srv;

 failed:
  cli_shutdown(&c);
  if (!srv) return NULL;
  
  if (srv->server_name) free(srv->server_name);
  if (srv->share_name) free(srv->share_name);
  free(srv);
  return NULL;
}

/*
 *Initialise the library etc 
 *
 * We accept valiv values for debug from 0 to 100,
 * and insist that fn must be non-null.
 */

int smbc_init(smbc_get_auth_data_fn fn, int debug)
{
  pstring conf;
  int p, pid;
  char *user = NULL, *home = NULL, *pname="libsmbclient";

  /*
   * Next lot ifdef'd out until test suite fixed ...
   */

  if (!fn || debug < 0 || debug > 100) {

    errno = EINVAL;
    return -1;

  }

  smbc_initialized = 1;
  smbc_auth_fn = fn;
  smbc_debug = debug;

  DEBUGLEVEL = -1;

  setup_logging(pname, False);

  /*
   * We try to construct our netbios name from our hostname etc
   */

  user = getenv("USER");
  if (!user) user = "";  /* FIXME: What to do about this? */

  /*
   * FIXME: Is this the best way to get the user info? */

  pstrcpy(smbc_user, user); /* Save for use elsewhere */

  pid = getpid();

  /*
   * Hmmm, I want to get hostname as well, but I am too lazy for the moment
   */

  slprintf(my_netbios_name, 16, "smbc%s%d", user, pid);

  charset_initialise();

  /* Here we would open the smb.conf file if needed ... */

  home = getenv("HOME");

  slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);

  load_interfaces();  /* Load the list of interfaces ... */

  in_client = True; /* FIXME, make a param */

  if (!lp_load(conf, True, False, False)) {

    /*
     * Hmmm, what the hell do we do here ... we could not parse the
     * config file ... We must return an error ... and keep info around
     * about why we failed
     */
    
    errno = ENOENT; /* FIXME: Figure out the correct error response */
    return -1;

  }

  codepage_initialise(lp_client_code_page()); /* Get a codepage */

  reopen_logs();  /* Get logging working ... */

  name_register_wins(my_netbios_name, 0);

  /* 
   * Now initialize the file descriptor array and figure out what the
   * max open files is, so we can return FD's that are above the max
   * open file, and separated by a guard band
   */

#if (defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE))
  do {
    struct rlimit rlp;

    if (getrlimit(RLIMIT_NOFILE, &rlp)) {

      DEBUG(0, ("smbc_init: getrlimit(1) for RLIMIT_NOFILE failed with error %s\n", strerror(errno)));

      smbc_start_fd = 1000000;
      smbc_max_fd = 10000;     /* FIXME, should be a define ... */

    }
    else {
      
      smbc_start_fd = rlp.rlim_max + 10000; /* Leave a guard space of 10,000 */
      smbc_max_fd = 10000;

    }
  } while ( 0 );
#else /* !defined(HAVE_GETRLIMIT) || !defined(RLIMIT_NOFILE) */

  smbc_start_fd = 1000000;
  smbc_max_fd = 10000;     /* FIXME, should be a define ... */

#endif

  smbc_file_table = malloc(smbc_max_fd * sizeof(struct smbc_file *));

  for (p = 0; p < smbc_max_fd; p++)
    smbc_file_table[p] = NULL;

  if (!smbc_file_table)
    return ENOMEM;

  return 0;  /* Success */

}

/*
 * Routine to open() a file ...
 */

int smbc_open(const char *fname, int flags, mode_t mode)
{
  fstring server, share, user, password;
  pstring path;
  struct smbc_server *srv = NULL;
  int fd;

  if (!smbc_initialized) {

    errno = EUCLEAN;  /* Best I can think of ... */
    return -1;

  }

  if (!fname) {

    errno = EINVAL;
    return -1;

  }

  smbc_parse_path(fname, server, share, path, user, password); /* FIXME, check errors */

  if (user[0] == (char)0) pstrcpy(user, smbc_user);

  srv = smbc_server(server, share, lp_workgroup(), user, password);

  if (!srv) {

    if (errno == EPERM) errno = EACCES;
    return -1;  /* smbc_server sets errno */

  }

  if (path[strlen(path) - 1] == '\\') {
    
    fd = -1;

  }
  else {

    int slot = 0;

    /* Find a free slot first */

    while (smbc_file_table[slot])
      slot++;

    if (slot > smbc_max_fd) {

      errno = ENOMEM; /* FIXME, is this best? */
      return -1;

    }

    smbc_file_table[slot] = malloc(sizeof(struct smbc_file));

    if (!smbc_file_table[slot]) {

      errno = ENOMEM;
      return -1;

    }

    if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) {

      /* Handle the error ... */

      free(smbc_file_table[slot]);
      smbc_file_table[slot] = NULL;
      errno = smbc_errno(&srv->cli);
      return -1;

    }

    /* Fill in file struct */

    smbc_file_table[slot]->cli_fd  = fd;
    smbc_file_table[slot]->smbc_fd = slot + smbc_start_fd;
    smbc_file_table[slot]->fname   = strdup(fname);
    smbc_file_table[slot]->srv     = srv;
    smbc_file_table[slot]->offset  = 0;
    smbc_file_table[slot]->file    = True;

    return smbc_file_table[slot]->smbc_fd;

  }

  /* Check if opendir needed ... */

  if (fd == -1) {
    int eno = 0;

    eno = smbc_errno(&srv->cli);
    fd = smbc_opendir(fname);
    if (fd < 0) errno = eno;
    return fd;

  }

  return 1;  /* Success, with fd ... */

}

/*
 * Routine to create a file 
 */

static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */

int smbc_creat(const char *path, mode_t mode)
{

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  return smbc_open(path, creat_bits, mode);
}

/*
 * Routine to read() a file ...
 */

ssize_t smbc_read(int fd, void *buf, size_t count)
{
  struct smbc_file *fe;
  int ret;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  DEBUG(4, ("smbc_read(%d, %d)\n", fd, (int)count));

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return -1;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  if (!fe->file) {

    errno = EBADF;
    return -1;

  }

  ret = cli_read(&fe->srv->cli, fe->cli_fd, buf, fe->offset, count);

  if (ret < 0) {

    errno = smbc_errno(&fe->srv->cli);
    return -1;

  }

  fe->offset += ret;

  DEBUG(4, ("  --> %d\n", ret));

  return ret;  /* Success, ret bytes of data ... */

}

/*
 * Routine to write() a file ...
 */

ssize_t smbc_write(int fd, void *buf, size_t count)
{
  int ret;
  struct smbc_file *fe;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return -1;
    
  }

  /* Check that the buffer exists ... */

  if (buf == NULL) {

    errno = EINVAL;
    return -1;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  ret = cli_write(&fe->srv->cli, fe->cli_fd, 0, buf, fe->offset, count);

  if (ret <= 0) {

    errno = smbc_errno(&fe->srv->cli);
    return -1;

  }

  fe->offset += ret;

  return ret;  /* Success, 0 bytes of data ... */
}
 
/*
 * Routine to close() a file ...
 */

int smbc_close(int fd)
{
  struct smbc_file *fe;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
   
    errno = EBADF;
    return -1;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  if (!fe->file) {

    return smbc_closedir(fd);

  }

  if (!cli_close(&fe->srv->cli, fe->cli_fd)) {

    errno = smbc_errno(&fe->srv->cli);  /* FIXME, should we deallocate slot? */
    return -1;

  }

  free(fe);
  smbc_file_table[fd - smbc_start_fd] = NULL;

  return 0;
}

/*
 * Routine to unlink() a file
 */

int smbc_unlink(const char *fname)
{
  fstring server, share, user, password;
  pstring path;
  struct smbc_server *srv = NULL;

  if (!smbc_initialized) {

    errno = EUCLEAN;  /* Best I can think of ... */
    return -1;

  }

  if (!fname) {

    errno = EINVAL;
    return -1;

  }

  smbc_parse_path(fname, server, share, path, user, password); /* FIXME, check errors */

  if (user[0] == (char)0) pstrcpy(user, smbc_user);

  srv = smbc_server(server, share, lp_workgroup(), user, password);

  if (!srv) {

    return -1;  /* smbc_server sets errno */

  }

  /*  if (strncmp(srv->cli.dev, "LPT", 3) == 0) {

    int job = smbc_stat_printjob(srv, path, NULL, NULL);
    if (job == -1) {

      return -1;

    }
    if (cli_printjob_del(&srv->cli, job) != 0) {

      return -1;

    }
    } else */

  if (!cli_unlink(&srv->cli, path)) {

    errno = smbc_errno(&srv->cli);

    if (errno == EACCES) { /* Check if the file is a directory */

      int saverr = errno;
      size_t size = 0;
      uint16 mode = 0;
      time_t m_time = 0, a_time = 0, c_time = 0;
      SMB_INO_T ino = 0;

      if (!smbc_getatr(srv, path, &mode, &size,
		       &c_time, &a_time, &m_time, &ino)) {

	/* Hmmm, bad error ... What? */

	errno = smbc_errno(&srv->cli);
	return -1;

      }
      else {

	if (IS_DOS_DIR(mode))
	  errno = EISDIR;
	else
	  errno = saverr;  /* Restore this */

      }
    }

    return -1;

  }

  return 0;  /* Success ... */

}

/*
 * Routine to rename() a file
 */

int smbc_rename(const char *oname, const char *nname)
{
  fstring server1, share1, server2, share2, user1, user2, password1, password2;
  pstring path1, path2;
  struct smbc_server *srv = NULL;

  if (!smbc_initialized) {

    errno = EUCLEAN;  /* Best I can think of ... */
    return -1;

  }

  if (!oname || !nname) {

    errno = EINVAL;
    return -1;

  }
  
  DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));

  smbc_parse_path(oname, server1, share1, path1, user1, password1);

  if (user1[0] == (char)0) pstrcpy(user1, smbc_user);

  smbc_parse_path(nname, server2, share2, path2, user2, password2);

  if (user2[0] == (char)0) pstrcpy(user2, smbc_user);

  if (strcmp(server1, server2) || strcmp(share1, share2) ||
      strcmp(user1, user2)) {

    /* Can't rename across file systems, or users?? */

    errno = EXDEV;
    return -1;

  }

  srv = smbc_server(server1, share1, lp_workgroup(), user1, password1);
  if (!srv) {

    return -1;

  }

  if (!cli_rename(&srv->cli, path1, path2)) {
    int eno = smbc_errno(&srv->cli);

    if (eno != EEXIST ||
	!cli_unlink(&srv->cli, path2) ||
	!cli_rename(&srv->cli, path1, path2)) {

      errno = eno;
      return -1;

    }
  }

  return 0; /* Success */

}

/*
 * A routine to lseek() a file
 */

off_t smbc_lseek(int fd, off_t offset, int whence)
{
  struct smbc_file *fe;
  size_t size;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return -1;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  if (!fe->file) {

    return smbc_lseekdir(fd, offset, whence);

  }

  switch (whence) {
  case SEEK_SET:
    fe->offset = offset;
    break;

  case SEEK_CUR:
    fe->offset += offset;
    break;

  case SEEK_END:
    if (!cli_qfileinfo(&fe->srv->cli, fe->cli_fd, NULL, &size, NULL, NULL,
		       NULL, NULL, NULL) &&
	!cli_getattrE(&fe->srv->cli, fe->cli_fd, NULL, &size, NULL, NULL,
		      NULL)) {

      errno = EINVAL;
      return -1;
    }
    fe->offset = size + offset;
    break;

  default:
    errno = EINVAL;
    break;

  }

  return fe->offset;

}

/* 
 * Generate an inode number from file name for those things that need it
 */

static
ino_t smbc_inode(const char *name)
{

  if (!*name) return 2; /* FIXME, why 2 ??? */
  return (ino_t)str_checksum(name);

}

/*
 * Routine to put basic stat info into a stat structure ... Used by stat and
 * fstat below.
 */

static
int smbc_setup_stat(struct stat *st, char *fname, size_t size, int mode)
{

  st->st_mode = 0;

  if (IS_DOS_DIR(mode)) {
    st->st_mode = SMBC_DIR_MODE;
  } else {
    st->st_mode = SMBC_FILE_MODE;
  }

  if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
  if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
  if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
  if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;

  st->st_size = size;
  st->st_blksize = 512;
  st->st_blocks = (size+511)/512;
  st->st_uid = getuid();
  st->st_gid = getgid();

  if (IS_DOS_DIR(mode)) {
    st->st_nlink = 2;
  } else {
    st->st_nlink = 1;
  }

  if (st->st_ino == 0) {
    st->st_ino = smbc_inode(fname);
  }

  return True;  /* FIXME: Is this needed ? */

}

/*
 * Get info from an SMB server on a file. Use a qpathinfo call first
 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
 */

BOOL smbc_getatr(struct smbc_server *srv, char *path, 
		 uint16 *mode, size_t *size, 
		 time_t *c_time, time_t *a_time, time_t *m_time,
		 SMB_INO_T *ino)
{

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
  
  if (!srv->no_pathinfo2 &&
      cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
		     size, mode, ino)) return True;

  /* if this is NT then don't bother with the getatr */
  if (srv->cli.capabilities & CAP_NT_SMBS) return False;

  if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
    a_time = c_time = m_time;
    srv->no_pathinfo2 = True;
    return True;
  }
  return False;
}

/*
 * Routine to stat a file given a name
 */

int smbc_stat(const char *fname, struct stat *st)
{
  struct smbc_server *srv;
  fstring server, share, user, password;
  pstring path;
  time_t m_time = 0, a_time = 0, c_time = 0;
  size_t size = 0;
  uint16 mode = 0;
  SMB_INO_T ino = 0;

  if (!smbc_initialized) {

    errno = EUCLEAN;  /* Best I can think of ... */
    return -1;

  }

  if (!fname) {

    errno = EINVAL;
    return -1;

  }
  
  DEBUG(4, ("smbc_stat(%s)\n", fname));

  smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/

  if (user[0] == (char)0) pstrcpy(user, smbc_user);

  srv = smbc_server(server, share, lp_workgroup(), user, password);

  if (!srv) {

    return -1;  /* errno set by smbc_server */

  }

  /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {

     mode = aDIR | aRONLY;

     }
     else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {

       if (strcmp(path, "\\") == 0) {

          mode = aDIR | aRONLY;

       }
       else {

         mode = aRONLY;
	 smbc_stat_printjob(srv, path, &size, &m_time);
	 c_time = a_time = m_time;

       }
       else { */

  if (!smbc_getatr(srv, path, &mode, &size, 
		   &c_time, &a_time, &m_time, &ino)) {

    errno = smbc_errno(&srv->cli);
    return -1;

  }

  /* } */

  st->st_ino = ino;

  smbc_setup_stat(st, path, size, mode);

  st->st_atime = a_time;
  st->st_ctime = c_time;
  st->st_mtime = m_time;
  st->st_dev   = srv->dev;

  return 0;

}

/*
 * Routine to stat a file given an fd
 */

int smbc_fstat(int fd, struct stat *st)
{
  struct smbc_file *fe;
  time_t c_time, a_time, m_time;
  size_t size;
  uint16 mode;
  SMB_INO_T ino = 0;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return -1;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  if (!fe->file) {

    return smbc_fstatdir(fd, st);

  }

  if (!cli_qfileinfo(&fe->srv->cli, fe->cli_fd,
		     &mode, &size, &c_time, &a_time, &m_time, NULL, &ino) &&
      !cli_getattrE(&fe->srv->cli, fe->cli_fd,
		    &mode, &size, &c_time, &a_time, &m_time)) {

    errno = EINVAL;
    return -1;

  }

  st->st_ino = ino;

  smbc_setup_stat(st, fe->fname, size, mode);

  st->st_atime = a_time;
  st->st_ctime = c_time;
  st->st_mtime = m_time;
  st->st_dev = fe->srv->dev;

  return 0;

}

/*
 * Routine to open a directory
 *
 * We want to allow:
 *
 * smb: which should list all the workgroups available
 * smb:workgroup
 * smb:workgroup//server
 * smb://server
 * smb://server/share
 */

static void smbc_remove_dir(struct smbc_file *dir)
{
  struct smbc_dir_list *d,*f;

  d = dir->dir_list;
  while (d) {

    f = d; d = d->next;

    if (f->dirent) free(f->dirent);
    free(f);

  }

  dir->dir_list = dir->dir_end = dir->dir_next = NULL;

}

static int add_dirent(struct smbc_file *dir, const char *name, const char *comment, uint32 type)
{
  struct smbc_dirent *dirent;
  int size;

  /*
   * Allocate space for the dirent, which must be increased by the 
   * size of the name and the comment and 1 for the null on the comment.
   * The null on the name is already accounted for.
   */

  size = sizeof(struct smbc_dirent) + (name?strlen(name):0) +
    (comment?strlen(comment):0) + 1; 
    
  dirent = malloc(size);

  if (!dirent) {

    dir->dir_error = ENOMEM;
    return -1;

  }

  if (dir->dir_list == NULL) {

    dir->dir_list = malloc(sizeof(struct smbc_dir_list));
    if (!dir->dir_list) {

      free(dirent);
      dir->dir_error = ENOMEM;
      return -1;

    }

    dir->dir_end = dir->dir_next = dir->dir_list;
  
  }
  else {

    dir->dir_end->next = malloc(sizeof(struct smbc_dir_list));

    if (!dir->dir_end) {

      free(dirent);
      dir->dir_error = ENOMEM;
      return -1;

    }

    dir->dir_end = dir->dir_end->next;

  }

  dir->dir_end->next = NULL;
  dir->dir_end->dirent = dirent;

  dirent->smbc_type = type;
  dirent->namelen = (name?strlen(name):0);
  dirent->commentlen = (comment?strlen(comment):0);
  dirent->dirlen = size;
  
  strncpy(dirent->name, (name?name:""), dirent->namelen + 1);

  dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
  strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);

  return 0;

}

static void
list_fn(const char *name, uint32 type, const char *comment, void *state)
{
  struct smbc_file *dir = (struct smbc_file *)state;
  int dirent_type;

  /* We need to process the type a little ... */

  if (dir->dir_type == SMBC_FILE_SHARE) {

    switch (type) {
    case 0: /* Directory tree */
      dirent_type = SMBC_FILE_SHARE;
      break;

    case 1:
      dirent_type = SMBC_PRINTER_SHARE;
      break;

    case 2:
      dirent_type = SMBC_COMMS_SHARE;
      break;

    case 3:
      dirent_type = SMBC_IPC_SHARE;
      break;

    default:
      dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
      break;
    }

  }
  else dirent_type = dir->dir_type;

  if (add_dirent(dir, name, comment, dirent_type) < 0) {

    /* An error occurred, what do we do? */

  }

}

static void
dir_list_fn(file_info *finfo, const char *mask, void *state)
{

  /*  fprintf(stderr, "Finfo->name=%s, mask=%s, mode=%0X\n", finfo->name, mask, finfo->mode);*/
  if (add_dirent((struct smbc_file *)state, finfo->name, "", 
		 (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {

    /* Handle an error ... */

  } 

}

int smbc_opendir(const char *fname)
{
  fstring server, share, user, password;
  pstring path;
  struct smbc_server *srv = NULL;
  struct in_addr rem_ip;
  int slot = 0;
  uint8 eclass;
  uint32 ecode;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (!fname) {
    
    errno = EINVAL;
    return -1;

  }

  if (smbc_parse_path(fname, server, share, path, user, password)) {

    errno = EINVAL;
    return -1;

  }

  if (user[0] == (char)0) pstrcpy(user, smbc_user);

  /* Get a file entry ... */

  slot = 0;

  while (smbc_file_table[slot])
    slot++;

  if (slot > smbc_max_fd) {

    errno = ENOMEM;
    return -1; /* FIXME, ... move into a func */
      
  }

  smbc_file_table[slot] = malloc(sizeof(struct smbc_file));

  if (!smbc_file_table[slot]) {

    errno = ENOMEM;
    return -1;

  }

  smbc_file_table[slot]->cli_fd   = 0;
  smbc_file_table[slot]->smbc_fd  = slot + smbc_start_fd;
  smbc_file_table[slot]->fname    = strdup(fname);
  smbc_file_table[slot]->srv      = NULL;
  smbc_file_table[slot]->offset   = 0;
  smbc_file_table[slot]->file     = False;
  smbc_file_table[slot]->dir_list = 
    smbc_file_table[slot]->dir_next =
    smbc_file_table[slot]->dir_end = NULL;

  if (server[0] == (char)0) {

    if (share[0] != (char)0 || path[0] != (char)0) {
    
      errno = EINVAL;
      if (smbc_file_table[slot]) free(smbc_file_table[slot]);
      smbc_file_table[slot] = NULL;
      return -1;

    }

    /* We have server and share and path empty ... so list the workgroups */

    /*cli_get_backup_server(my_netbios_name, lp_workgroup(), server, sizeof(server));*/

    if (!resolve_name(lp_workgroup(), &rem_ip, 0x1d)) {
      
      errno = EINVAL;  /* Something wrong with smb.conf? */
      return -1;

    }

    smbc_file_table[slot]->dir_type = SMBC_WORKGROUP;

    /* find the name of the server ... */

    if (!name_status_find(0, rem_ip, server)) {

      fprintf(stderr, "Could not get the name of local master browser ...\n");
      errno = EINVAL;
      return -1;

    }

    /*
     * Get a connection to IPC$ on the server if we do not already have one
     */

    srv = smbc_server(server, "IPC$", lp_workgroup(), user, password);

    if (!srv) {

      if (smbc_file_table[slot]) free(smbc_file_table[slot]);
      smbc_file_table[slot] = NULL;
      return -1;

    }

    smbc_file_table[slot]->srv = srv;

    /* Now, list the stuff ... */

    if (!cli_NetServerEnum(&srv->cli, lp_workgroup(), 0x80000000, list_fn,
			   (void *)smbc_file_table[slot])) {

      if (smbc_file_table[slot]) free(smbc_file_table[slot]);
      smbc_file_table[slot] = NULL;
      errno = cli_error(&srv->cli, &eclass, &ecode, NULL);
      return -1;

    }
  }
  else { /* Server not an empty string ... Check the rest and see what gives */

    if (share[0] == (char)0) {

      if (path[0] != (char)0) { /* Should not have empty share with path */

	errno = EINVAL;
	if (smbc_file_table[slot]) free(smbc_file_table[slot]);
	smbc_file_table[slot] = NULL;
	return -1;
	
      }

      /* Check to see if <server><1D> translates, or <server><20> translates */

      if (resolve_name(server, &rem_ip, 0x1d)) { /* Found LMB */
	pstring buserver;

	smbc_file_table[slot]->dir_type = SMBC_SERVER;

	/*
	 * Get the backup list ...
	 */

	/*cli_get_backup_server(my_netbios_name, server, buserver, sizeof(buserver)); */

	if (!name_status_find(0, rem_ip, buserver)) {

	  fprintf(stderr, "Could not get name of local master browser ...\n");
	  errno = EPERM;  /* FIXME, is this correct */
	  return -1;

	}

	/*
	 * Get a connection to IPC$ on the server if we do not already have one
	 */

	srv = smbc_server(buserver, "IPC$", lp_workgroup(), user, password);

	if (!srv) {

	  if (smbc_file_table[slot]) free(smbc_file_table[slot]);
	  smbc_file_table[slot] = NULL;  /* FIXME: Memory leaks ... */
	  return -1;

	}

	smbc_file_table[slot]->srv = srv;

	/* Now, list the servers ... */

	if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn,
			       (void *)smbc_file_table[slot])) {

	  if (smbc_file_table[slot]) free(smbc_file_table[slot]);
	  smbc_file_table[slot] = NULL;
	  errno = cli_error(&srv->cli, &eclass, &ecode, NULL);
	  return -1;

	}

      }
      else {

	if (resolve_name(server, &rem_ip, 0x20)) {

	  /* Now, list the shares ... */

	  smbc_file_table[slot]->dir_type = SMBC_FILE_SHARE;

	  srv = smbc_server(server, "IPC$", lp_workgroup(), user, password);

	  if (!srv) {

	    if (smbc_file_table[slot]) free(smbc_file_table[slot]);
	    smbc_file_table[slot] = NULL;
	    return -1;

	  }

	  smbc_file_table[slot]->srv = srv;

	  /* Now, list the servers ... */

	  if (cli_RNetShareEnum(&srv->cli, list_fn, 
				(void *)smbc_file_table[slot]) < 0) {

	    errno = cli_error(&srv->cli, &eclass, &ecode, NULL);
	    if (smbc_file_table[slot]) free(smbc_file_table[slot]);
	    smbc_file_table[slot] = NULL;
	    return -1;

	  }

	}
	else {

	  errno = EINVAL;
	  if (smbc_file_table[slot]) free(smbc_file_table[slot]);
	  smbc_file_table[slot] = NULL;
	  return -1;

	}

      }

    }
    else { /* The server and share are specified ... work from there ... */

      /* Well, we connect to the server and list the directory */

      smbc_file_table[slot]->dir_type = SMBC_FILE_SHARE;

      srv = smbc_server(server, share, lp_workgroup(), user, password);

      if (!srv) {

	if (smbc_file_table[slot]) free(smbc_file_table[slot]);
	smbc_file_table[slot] = NULL;
	return -1;

      }

      smbc_file_table[slot]->srv = srv;

      /* Now, list the files ... */

      pstrcat(path, "\\*");

      if (cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn, 
		   (void *)smbc_file_table[slot]) < 0) {

	if (smbc_file_table[slot]) free(smbc_file_table[slot]);
	smbc_file_table[slot] = NULL;
	errno = smbc_errno(&srv->cli);
	return -1;

      }
    }

  }

  return smbc_file_table[slot]->smbc_fd;

}

/*
 * Routine to close a directory
 */

int smbc_closedir(int fd)
{
  struct smbc_file *fe;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return -1;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  if (!fe) {

    errno = ENOENT;  /* FIXME: Is this correct */
    return -1;

  }

  smbc_remove_dir(fe); /* Clean it up */

  if (fe) free(fe);    /* Free the space too */

  smbc_file_table[fd - smbc_start_fd] = NULL;

  return 0;

}

/*
 * Routine to get a directory entry
 */

static char smbc_local_dirent[512];  /* Make big enough */

struct smbc_dirent *smbc_readdir(unsigned int fd)
{
  struct smbc_file *fe;
  struct smbc_dirent *dirp, *dirent;

  /* Check that all is ok first ... */

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return NULL;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return NULL;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  if (fe->file != False) { /* FIXME, should be dir, perhaps */

    errno = ENOTDIR;
    return NULL;

  }

  if (!fe->dir_next)
    return NULL;
  else {

    dirent = fe->dir_next->dirent;

    if (!dirent) {

      errno = ENOENT;
      return NULL;

    }

    /* Hmmm, do I even need to copy it? */

    bcopy(dirent, smbc_local_dirent, dirent->dirlen); /* Copy the dirent */

    dirp = (struct smbc_dirent *)smbc_local_dirent;

    dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);
    
    fe->dir_next = fe->dir_next->next;

    return (struct smbc_dirent *)smbc_local_dirent;
  }

}

/*
 * Routine to get directory entries
 */

int smbc_getdents(unsigned int fd, struct smbc_dirent *dirp, int count)
{
  struct smbc_file *fe;
  struct smbc_dir_list *dir;
  int rem = count, reqd;

  /* Check that all is ok first ... */

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return -1;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  if (fe->file != False) { /* FIXME, should be dir, perhaps */

    errno = ENOTDIR;
    return -1;

  }

  /* 
   * Now, retrieve the number of entries that will fit in what was passed
   * We have to figure out if the info is in the list, or we need to 
   * send a request to the server to get the info.
   */

  while ((dir = fe->dir_next)) {
    struct smbc_dirent *dirent;

    if (!dir->dirent) {

      errno = ENOENT;  /* Bad error */
      return -1;

    }

    if (rem < (reqd = (sizeof(struct smbc_dirent) + dir->dirent->namelen + 
			 dir->dirent->commentlen + 1))) {

      if (rem < count) { /* We managed to copy something */

	errno = 0;
	return count - rem;

      }
      else { /* Nothing copied ... */

	errno = EINVAL;  /* Not enough space ... */
	return -1;

      }

    }

    dirent = dir->dirent;

    bcopy(dirent, dirp, reqd); /* Copy the data in ... */
    
    dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);

    (char *)dirp += reqd;

    rem -= reqd;

    fe->dir_next = dir = dir -> next;
  }

  if (rem == count)
    return 0;
  else 
    return count - rem;

}

/*
 * Routine to create a directory ...
 */

int smbc_mkdir(const char *fname, mode_t mode)
{
  struct smbc_server *srv;
  fstring server, share, user, password;
  pstring path;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (!fname) {

    errno = EINVAL;
    return -1;

  }
  
  DEBUG(4, ("smbc_mkdir(%s)\n", fname));

  smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/

  if (user[0] == (char)0) pstrcpy(user, smbc_user);

  srv = smbc_server(server, share, lp_workgroup(), user, password);

  if (!srv) {

    return -1;  /* errno set by smbc_server */

  }

  /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {

     mode = aDIR | aRONLY;

     }
     else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {

       if (strcmp(path, "\\") == 0) {

          mode = aDIR | aRONLY;

       }
       else {

         mode = aRONLY;
	 smbc_stat_printjob(srv, path, &size, &m_time);
	 c_time = a_time = m_time;

       }
       else { */

  if (!cli_mkdir(&srv->cli, path)) {

    errno = smbc_errno(&srv->cli);
    return -1;

  } 

  return 0;

}

/*
 * Routine to remove a directory
 */

int smbc_rmdir(const char *fname)
{
  struct smbc_server *srv;
  fstring server, share, user, password;
  pstring path;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (!fname) {

    errno = EINVAL;
    return -1;

  }
  
  DEBUG(4, ("smbc_rmdir(%s)\n", fname));

  smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/

  if (user[0] == (char)0) pstrcpy(user, smbc_user);

  srv = smbc_server(server, share, lp_workgroup(), user, password);

  if (!srv) {

    return -1;  /* errno set by smbc_server */

  }

  /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {

     mode = aDIR | aRONLY;

     }
     else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {

       if (strcmp(path, "\\") == 0) {

          mode = aDIR | aRONLY;

       }
       else {

         mode = aRONLY;
	 smbc_stat_printjob(srv, path, &size, &m_time);
	 c_time = a_time = m_time;

       }
       else { */

  if (!cli_rmdir(&srv->cli, path)) {

    errno = smbc_errno(&srv->cli);
    return -1;

  } 

  return 0;

}

/*
 * Routine to return the current directory position
 */

off_t smbc_telldir(int fd)
{
  struct smbc_file *fe;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return -1;

  }

  fe = smbc_file_table[fd - smbc_start_fd];

  if (fe->file != False) { /* FIXME, should be dir, perhaps */

    errno = ENOTDIR;
    return -1;

  }

  return (off_t) fe->dir_next;

}

/*
 * Routine to seek on a directory
 */

int smbc_lseekdir(int fd, off_t offset, int whence)
{

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {

    errno = EBADF;
    return -1;

  }

  return ENOSYS;  /* Not implemented so far ... */

}

/*
 * Routine to fstat a dir
 */

int smbc_fstatdir(int fd, struct stat *st)
{

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  /* No code yet ... */

  return 0;

}

/*
 * Routine to print a file on a remote server ...
 *
 * We open the file, which we assume to be on a remote server, and then
 * copy it to a print file on the share specified by printq.
 */

int smbc_print_file(const char *fname, const char *printq)
{
  int fid1, fid2, bytes, saverr, tot_bytes = 0;
  char buf[4096];

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (!fname && !printq) {

    errno = EINVAL;
    return -1;

  }

  /* Try to open the file for reading ... */

  if ((fid1 = smbc_open(fname, O_RDONLY, 0666)) < 0) {

    fprintf(stderr, "Error, fname=%s, errno=%i\n", fname, errno);
    return -1;  /* smbc_open sets errno */

  }

  /* Now, try to open the printer file for writing */

  if ((fid2 = smbc_open_print_job(printq)) < 0) {

    saverr = errno;  /* Save errno */
    smbc_close(fid1);
    errno = saverr;
    return -1;

  }

  while ((bytes = smbc_read(fid1, buf, sizeof(buf))) > 0) {

    tot_bytes += bytes;

    if ((smbc_write(fid2, buf, bytes)) < 0) {

      saverr = errno;
      smbc_close(fid1);
      smbc_close(fid2);
      errno = saverr;

    }

  }

  saverr = errno;

  smbc_close(fid1);  /* We have to close these anyway */
  smbc_close(fid2);

  if (bytes < 0) {

    errno = saverr;
    return -1;

  }

  return tot_bytes;

}

/*
 * Open a print file to be written to by other calls
 */

int smbc_open_print_job(const char *fname)
{
  fstring server, share, user, password;
  pstring path;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (!fname) {

    errno = EINVAL;
    return -1;

  }
  
  DEBUG(4, ("smbc_open_print_job(%s)\n", fname));

  smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/

  /* What if the path is empty, or the file exists? */

  return smbc_open(fname, O_WRONLY, 666);

}

/*
 * Routine to list print jobs on a printer share ...
 */

int smbc_list_print_jobs(const char *fname, void (*fn)(struct print_job_info *))
{
  struct smbc_server *srv;
  fstring server, share, user, password;
  pstring path;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (!fname) {

    errno = EINVAL;
    return -1;

  }
  
  DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));

  smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/

  if (user[0] == (char)0) pstrcpy(user, smbc_user);

  srv = smbc_server(server, share, lp_workgroup(), user, password);

  if (!srv) {

    return -1;  /* errno set by smbc_server */

  }

  if (cli_print_queue(&srv->cli, fn) < 0) {

    errno = smbc_errno(&srv->cli);
    return -1;

  }

  return 0;

}

/*
 * Delete a print job from a remote printer share
 */

int smbc_unlink_print_job(const char *fname, int id)
{
  struct smbc_server *srv;
  fstring server, share, user, password;
  pstring path;

  if (!smbc_initialized) {

    errno = EUCLEAN;
    return -1;

  }

  if (!fname) {

    errno = EINVAL;
    return -1;

  }
  
  DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));

  smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/

  if (user[0] == (char)0) pstrcpy(user, smbc_user);

  srv = smbc_server(server, share, lp_workgroup(), user, password);

  if (!srv) {

    return -1;  /* errno set by smbc_server */

  }

  if (cli_printjob_del(&srv->cli, id) < 0) {

    errno = smbc_errno(&srv->cli);
    return -1;

  }

  return 0;

}