Logo Search packages:      
Sourcecode: ncpfs version File versions  Download package

nwcalls.c

/* 
    nwcalls.c - Connection oriented calls
    Copyright (C) 1999, 2000  Petr Vandrovec

    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:

      0.00  1998, December 1        Petr Vandrovec <vandrove@vc.cvut.cz>
            Initial release.

      1.00  1999, November 20       Petr Vandrovec <vandrove@vc.cvut.cz>
            Added copyright and license.

      1.01  2000, January 16        Petr Vandrovec <vandrove@vc.cvut.cz>
            Changes for 32bit uids.
            
      1.02  2000, May 1       Petr Vandrovec <vandrove@vc.cvut.cz>
            Added NWGetFileServerUTCTime.

      1.03  2000, May 7       Petr Vandrovec <vandrove@vc.cvut.cz>
            NWGetFileServerUTCTime moved to nwtime.c.

      1.04  2000, May 27            Petr Vandrovec <vandrove@vc.cvut.cz>
            Make NWParsePath return ERR_NULL_POINTER if path==NULL.

      1.05  2000, June 1            Petr Vandrovec <vandrove@vc.cvut.cz>
            Added NWGetBinderyAccessLevel.

        1.06  2001, January 7       Patrick Pollet <patrick.pollet@insa-lyon.fr>
            Make NWCCConnGetInfo ERR_NULL_POINTER if buffer==NULL.
            Added answer to NWCC_INFO_TREE_NAME in NWCCConnGetInfo.
            Added NWCCOpenConnByName (only IPX transport supported).

        1.07  2001, January 8       Patrick Pollet <patrick.pollet@insa-lyon.fr>
            Added disk retrictions API calls.

        1.08  2001, January 9       Patrick Pollet <patrick.pollet@insa-lyon.fr>
            Added semaphores API calls.

        1.09  2001, January 10            Patrick Pollet <patrick.pollet@insa-lyon.fr>
            Added NWCCGetConnAddress,NWCCGetConnAddressLength,
            Fixed NWGetVolumeNumber prototype in nwcalls.h and added it here.

      1.10  2001, February 11       Petr Vandrovec <vandrove@vc.cvut.cz>
            Added NWScanSemaphoresByConn.

      1.11  2001, February 25       Petr Vandrovec <vandrove@vc.cvut.cz>
            Added __NWExecuteGlobal code, implemented NW{Set,Get}BroadcastsMode for
            permanent connections.

      1.12  2001, February 25       Patrick Pollet <Patrick.Pollet@cipcinsa.insa-lyon.fr>
            Added NWIsObjectInSet.

      1.13  2001, March 7           Petr Vandrovec <vandrove@vc.cvut.cz>
            Fixed meaning of iterHandle parameter in
            NWScanVolDiskRestrictions/NWScanVolDiskRestrictions2.

      1.14  2001, May 31            Petr Vandrovec <vandrove@vc.cvut.cz>
            Include strings.h, do not use IPX in IPX-less config, hack
            around missing MSG_DONTWAIT.

      1.15  2001, June 26           Petr Vandrovec <vandrove@vc.cvut.cz>
            Added missing #include <stddef.h>. Discovered by Daniel Greenberg <daneli@umich.edu>

      1.16  2001, December 30       Petr Vandrovec <vandrove@vc.cvut.cz>
            Forget about NWCC_TRAN_TYPE_* crap and use NT_* everywhere.

      1.17  2002, January 20        Petr Vandrovec <vandrove@vc.cvut.cz>
            Move NWCC_INFO_MOUNT_POINT code from nwclient.c here.

      1.18  2002, February 4        Petr Vandrovec <vandrove@vc.cvut.cz>
            Handle correctly kernels reporting volume number > 255.

      1.19  2002, May 3       Petr Vandrovec <vandrove@vc.cvut.cz>
            Move NWCCOpenConnByName to resolve.c.

 */

#include "config.h"

#include <errno.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/time.h>

#include <ncp/nwcalls.h>
#include <ncp/nwnet.h>
#include "ncplib_i.h"
#include "ncpcode.h"
#include "ncpi.h"

NWCCODE NWCallsInit(void* dummy1, void* dummy2) {
      (void)dummy1;
      (void)dummy2;
      return 0;
}

NWCCODE NWRequest(NWCONN_HANDLE conn, nuint function, 
      nuint numRq, const NW_FRAGMENT* rq, 
      nuint numRp, NW_FRAGMENT* rp) {
      long result;
      nuint i;
      size_t rest;
      unsigned char* ptr;

      ncp_init_request(conn);
      if (function & NCPC_SUBFUNCTION) {
            ncp_add_word_hl(conn, 0);
            ncp_add_byte(conn, NCPC_SUBFN(function));
            conn->has_subfunction = 1;
      }
      for (i=numRq; i--; rq++) {
            ncp_add_mem(conn, rq->fragAddr.ro, rq->fragSize);
      }
      result = ncp_request(conn, NCPC_FN(function));
      if (result) {
            ncp_unlock_conn(conn);
            return result;
      }
      rest = conn->ncp_reply_size;
      ptr = ncp_reply_data(conn, 0);
      for (i=numRp; i--; rp++) {
            size_t spc = rp->fragSize;
            if (spc > rest) {
                  memcpy(rp->fragAddr.rw, ptr, rest);
                  /* Do we want to modify fragList ? */
                  /* I think so because of we want to propagate */
                  /* reply length back to client */
                  /* maybe we could do "return conn->ncp_reply_size" */
                  /* instead of "return 0". */
                  rp->fragSize = rest;
                  rest = 0;
            } else {
                  memcpy(rp->fragAddr.rw, ptr, spc);
                  rest -= spc;
            }
      }
      ncp_unlock_conn(conn);
      return 0;
}
      
/* NWRequestSimple: Simple request, 1 request fragment, max. 1 reply fragment
 * !!! check carefully whether you call this function with rp->fragAddress ?= NULL !!!
 * conn = connection handle
 * function = NCP function (0x00 - 0xFF), subfunction 0x00xx - 0xFFxx, sfn flag 0x1xxxx
 * rq = pointer to request, if rqlen == 0 then address can be NULL
 * rqlen = request length, if rqlen == 0, rq can be NULL
 * rp = pointer to reply fragment
 *    on error: return nonzero code, unlock conn
 *    on success: return 0;
 *                if rp == NULL: throw away reply
 *                if rp != NULL && rp->fragAddress == NULL: fill rp with pointer
 *                            to data and with size, do NOT unlock conn(!)
 *          if rp != NULL && rp->fragAddress != NULL: fill buffer pointed by
 *                            rp->fragAddress with reply up to rp->fragSize
 *                            rp->fragSize is reply length (can be greater than
 *                            fragSize on input), unlock conn
 */
NWCCODE NWRequestSimple(NWCONN_HANDLE conn, nuint function,
      const void* rq, size_t rqlen, NW_FRAGMENT* rp) {
      long result;
      
      if ((rp && rp->fragSize && !rp->fragAddr.rw) || (rqlen && !rq))
            return NWE_PARAM_INVALID;
      ncp_init_request(conn);
      if (function & NCPC_SUBFUNCTION) {
            ncp_add_word_hl(conn, rqlen+1);
            ncp_add_byte(conn, NCPC_SUBFN(function));
      }
      if (rqlen) 
            ncp_add_mem(conn, rq, rqlen);
      result = ncp_request(conn, NCPC_FN(function));
      if (result) {
            ncp_unlock_conn(conn);
            return result;
      }
      if (rp) {
            size_t rest;
            void* ptr;

            rest = conn->ncp_reply_size;
            ptr = ncp_reply_data(conn, 0);
            if (rp->fragAddr.rw) {
                  size_t maxsize = rp->fragSize;
                  if (maxsize > rest)
                        maxsize = rest;
                  rp->fragSize = rest;
                  memcpy(rp->fragAddr.rw, ptr, maxsize);
            } else {
                  rp->fragAddr.rw = ptr;
                  rp->fragSize = rest;
                  return 0;
            }
      }
      ncp_unlock_conn(conn);
      return 0;
}

/* FIXME: Mark socket O_NONBLOCK instead */
#ifndef MSG_DONTWAIT
#define MSG_DONTWAIT 0
#endif
static int timedRecv(int fd, void* b, size_t len, int timeout) {
      struct timeval tv;
      unsigned char* buff = b;

      tv.tv_sec = timeout / 1000000;
      tv.tv_usec = timeout % 1000000;
      do {
            fd_set rdset;
            int rd;

            do {
                  FD_ZERO(&rdset);
                  FD_SET(fd, &rdset);
                  rd = select(fd + 1, &rdset, NULL, NULL, &tv);
            } while (rd == -1 && errno == EINTR);
            if (rd == -1)
                  break;
            if (!FD_ISSET(fd, &rdset))
                  break;
            rd = recv(fd, buff, len, MSG_DONTWAIT);
            if (rd == 0)
                  break;
            if (rd > 0) {
                  size_t rd2 = rd;

                  if (rd2 > len)
                        break;
                  buff += rd2;
                  len -= rd2;
                  if (!len)
                        return 0;
            }
      } while (1);
      return -1;
}

static int timedRecvMsg(int fd, struct iovec* v, size_t vnum, int timeout) {
      struct timeval tv;

      tv.tv_sec = timeout / 1000000;
      tv.tv_usec = timeout % 1000000;
      do {
            fd_set rdset;
            int rd;
            struct msghdr m;

            do {
                  FD_ZERO(&rdset);
                  FD_SET(fd, &rdset);
                  rd = select(fd + 1, &rdset, NULL, NULL, &tv);
            } while (rd == -1 && errno == EINTR);
            if (rd == -1)
                  break;
            if (!FD_ISSET(fd, &rdset))
                  break;
            m.msg_name = NULL;
            m.msg_namelen = 0;
            m.msg_iov = v;
            m.msg_iovlen = vnum;
            m.msg_control = NULL;
            m.msg_controllen = 0;
            m.msg_flags = 0;
            rd = recvmsg(fd, &m, MSG_DONTWAIT);
            if (rd == 0)
                  break;
            if (rd > 0) {
                  size_t rd2 = rd;
                  while (rd2 >= v->iov_len) {
                        rd2 -= v++->iov_len;
                        if (!--vnum) {
                              if (rd2)
                                    return -1;
                              return 0;
                        }
                  }
                  v->iov_base = (unsigned char*)v->iov_base + rd2;
                  v->iov_len -= rd2;
            }
      } while (1);
      return -1;
}

static NWCCODE __NWExecuteGlobal(NWCONN_HANDLE conn, nuint function,
            nuint numRq, const NW_FRAGMENT* rq,
            nuint numRp, const NW_FRAGMENT* rp,
            size_t* rplen) {
      NWCCODE err = NCPLIB_NCPD_DEAD;
      int fd;
      struct msghdr msg;
      struct iovec iov[17];
      struct iovec* i;
      int sndlen;
      int rd;
      int retry = 0;
      size_t exprpl;
      size_t maxrpl;
      unsigned char rqb[20];
      nuint j;
      size_t rdu;

      if (rplen)
            *rplen = 0;

      if ((numRq > 16) || (numRp > 17)) {
            return NWE_REQ_TOO_MANY_REQ_FRAGS;
      }
      ncp_lock_conn(conn);
retryLoop:; 
      fd = conn->global_fd;
      if (fd == -1) {
            int fsfd;
            struct stat stb;
            struct sockaddr_un sun;
            socklen_t sunlen;

            fsfd = ncp_get_fid(conn);
            if (fsfd == -1) {
                  goto errquit;
            }
            if (fstat(fsfd, &stb)) {
                  goto errquit;
            }
            fd = socket(PF_UNIX, SOCK_STREAM, 0);
            if (fd < 0) {
                  goto errquit;
            }
            sunlen = offsetof(struct sockaddr_un, sun_path) + sprintf(sun.sun_path, "%cncpfs.permanent.mount.%lu", 0,
                        (unsigned long)stb.st_dev) + 1;
            sun.sun_family = AF_UNIX;
            if (connect(fd, (struct sockaddr*)&sun, sunlen)) {
                  close(fd);
                  goto errquit;
            }
            conn->global_fd = fd;
      }
      msg.msg_name = NULL;
      msg.msg_namelen = 0;
      msg.msg_iov = iov;
      msg.msg_iovlen = numRq + 1;
      msg.msg_control = NULL;
      msg.msg_controllen = 0;
      msg.msg_flags = 0;
      iov[0].iov_base = rqb;
      iov[0].iov_len = 20;
      sndlen = 20;
      i = iov + 1;
      {
            nuint numr = numRq;
            const NW_FRAGMENT* nwf = rq;
            
            while (numr--) {
                  i->iov_base = nwf->fragAddr.rw;
                  i->iov_len = nwf->fragSize;
                  sndlen += nwf->fragSize;
                  i++;
                  rq++;
            }
            numr = numRp;
            nwf = rp;
            maxrpl = 12;
            while (numr--) {
                  maxrpl += nwf++->fragSize;
            }
      }
      DSET_LH(rqb, 0, NCPI_REQUEST_SIGNATURE);
      DSET_LH(rqb, 4, sndlen);
      DSET_LH(rqb, 8, 1);
      DSET_LH(rqb, 12, maxrpl);
      DSET_LH(rqb, 16, function);
      rd = sendmsg(fd, &msg, MSG_NOSIGNAL);
      if (rd != sndlen) {
            close(fd);
            conn->global_fd = -1;
            if (retry) {
                  goto errquit;
            }
            retry = 1;
            goto retryLoop;
      }
      rd = timedRecv(fd, rqb, 12, 30000000);
      if (rd < 0) {
            goto abort;
      }
      if (DVAL_LH(rqb, 0) != NCPI_REPLY_SIGNATURE) {
            goto abort;
      }
      exprpl = DVAL_LH(rqb, 4);
      if (exprpl < 12) {
            goto abort;
      }
      exprpl -= 12;
      rdu = exprpl;
      i = iov;
      for (j = 0; j < numRp; j++) {
            size_t r = rp[j].fragSize;
            if (r > rdu)
                  r = rdu;
            if (r) {
                  i->iov_base = rp[j].fragAddr.rw;
                  i->iov_len = r;
                  rdu -= r;
                  i++;
            }
      }
      if (i != iov) {
            if (timedRecvMsg(fd, iov, i-iov, 10000000)) {
                  goto abort;
            }
      }
      if (rplen) {
            *rplen = exprpl - rdu;
      }
      if (rdu) {
            timedRecv(fd, conn->packet, rdu, 10000000);
      }
      err = DVAL_LH(rqb, 8);
quit:;      
      ncp_unlock_conn(conn);
      return err;
abort:;
      close(fd);
      conn->global_fd = -1;
errquit:;
      goto quit;
}

NWCCODE NWGetObjectName(NWCONN_HANDLE conn, NWObjectID ID, char* name, NWObjectType* type) {
      long result;
      struct ncp_bindery_object spc;

      result = ncp_get_bindery_object_name(conn, ID, &spc);
      if (result)
            return result;
      if (name) strncpy(name, spc.object_name, NCP_BINDERY_NAME_LEN);
      if (type) *type = spc.object_type;
      return 0;
}

NWCCODE NWGetObjectID(NWCONN_HANDLE conn, const char* name, NWObjectType type, NWObjectID* ID) {
      long result;
      struct ncp_bindery_object spc;
      
      result = ncp_get_bindery_object_id(conn, type, name, &spc);
      if (result)
            return result;
      if (ID)
            *ID = spc.object_id;
      return 0;
}

NWCCODE NWGetBinderyAccessLevel(NWCONN_HANDLE conn,
            nuint8* level, NWObjectID* id) {
      nuint8 reply[16];
      NW_FRAGMENT rp;
      NWCCODE err;
      
      rp.fragAddr.rw = reply;
      rp.fragSize = sizeof(reply);
      
      err = NWRequestSimple(conn, NCPC_SFN(0x17, 0x46), NULL, 0, &rp);
      if (err)
            return err;
      if (rp.fragSize < 5)
            return NWE_INVALID_NCP_PACKET_LENGTH;
      if (level)
            *level = BVAL(reply, 0);
      if (id)
            *id = DVAL_HL(reply, 1);
      return 0;
} 

NWCCODE NWGetNSEntryInfo(NWCONN_HANDLE conn, nuint dirHandle, const char* path, 
      nuint srcNS, nuint dstNS, nuint16 attr, nuint32 rim, NW_ENTRY_INFO* info) {
      unsigned char buff[1024];
      int rslt;

      rslt = ncp_path_to_NW_format(path, buff, sizeof(buff));
      if (rslt < 0)
            return -rslt;
      rslt = ncp_obtain_file_or_subdir_info2(conn, srcNS, dstNS, attr, rim, 
            dirHandle?0:0xFF, 0, dirHandle, buff, rslt, info);
      return rslt;
}

NWCCODE NWParsePath(const char* path, char* server, NWCONN_HANDLE* conn, 
      char* volume, char* volpath) {
      long result;
      NWCONN_HANDLE cn;
      struct NWCCRootEntry rent;
      char tmp[1000];
      char* ptr;

      if (!path)
            return ERR_NULL_POINTER;

      result = ncp_open_mount(path, &cn);
      if (result) {
            if (volume)
                  *volume = 0;
            if (volpath)
                  strcpy(volpath, path);
            if (conn)
                  *conn = NULL;
            if (server)
                  *server = 0;
            return 0;
      }
      result = NWCCGetConnInfo(cn, NWCC_INFO_ROOT_ENTRY, sizeof(rent), &rent);
      if (result) {
            ncp_close(cn);
            return NWE_REQUESTER_FAILURE;
      }
      if (rent.volume > 255) {
            *tmp = 0;
      } else {
            result = ncp_ns_get_full_name(cn, NW_NS_DOS, NW_NS_DOS, 1,
                  rent.volume, rent.dirEnt, NULL, 0, tmp, sizeof(tmp));
            if (result) {
                  ncp_close(cn);
                  return result;
            }
      }
      ptr = strchr(tmp, ':');
      if (ptr) {
            if (volume) {
                  memcpy(volume, tmp, ptr-tmp);
                  volume[ptr-tmp]=0;
            }
            if (volpath)
                  strcpy(volpath, ptr+1);
      } else {
            if (volume)
                  *volume = 0;
            if (volpath)
                  strcpy(volpath, tmp);
      }
      if (server) {
            if (NWGetFileServerName(cn, server)) {
                  *server = 0;
            }
      }
      if (conn)
            *conn = cn;
      else
            ncp_close(cn);
      return 0;
}

NWCCODE NWDownFileServer(NWCONN_HANDLE conn, nuint force) {
      nuint8 flag = force?0:0xFF;

      return NWRequestSimple(conn, NCPC_SFN(0x17, 0xD3), &flag, sizeof(flag), NULL);
}

NWCCODE NWCloseBindery(NWCONN_HANDLE conn) {
      return NWRequestSimple(conn, NCPC_SFN(0x17, 0x44), NULL, 0, NULL);
}

NWCCODE NWOpenBindery(NWCONN_HANDLE conn) {
      return NWRequestSimple(conn, NCPC_SFN(0x17, 0x45), NULL, 0, NULL);
}

NWCCODE NWDisableTTS(NWCONN_HANDLE conn) {
      return NWRequestSimple(conn, NCPC_SFN(0x17, 0xCF), NULL, 0, NULL);
}

NWCCODE NWEnableTTS(NWCONN_HANDLE conn) {
      return NWRequestSimple(conn, NCPC_SFN(0x17, 0xD0), NULL, 0, NULL);
}

NWCCODE NWDisableFileServerLogin(NWCONN_HANDLE conn) {
      return NWRequestSimple(conn, NCPC_SFN(0x17, 0xCB), NULL, 0, NULL);
}

NWCCODE NWEnableFileServerLogin(NWCONN_HANDLE conn) {
      return NWRequestSimple(conn, NCPC_SFN(0x17, 0xCC), NULL, 0, NULL);
}

static NWCCODE NWSMExec(NWCONN_HANDLE conn, int scode, 
            const nuint8* rqd, const char* txt, const char* txt2, 
      nuint32* rpd) {
      NW_FRAGMENT rq[4];
      NW_FRAGMENT rp;
      static const char here20[20] = { 0 };
      nuint8 rpl[24];
      nuint8 hdr[3];
      NWCCODE err;

      if (!rqd) rq[1].fragAddr.ro = here20; else rq[1].fragAddr.ro = rqd;
      rq[1].fragSize = 20;
      rq[0].fragAddr.ro = hdr;
      rq[0].fragSize = sizeof(hdr);
      rq[2].fragAddr.ro = txt;
      rq[2].fragSize = strlen(txt) + 1;
      rq[3].fragAddr.ro = txt2;
      rq[3].fragSize = txt2?(strlen(txt2) + 1):0;
      WSET_HL(hdr, 0, rq[3].fragSize + rq[2].fragSize + 1 + 20);
      BSET(hdr, 2, scode);
      rp.fragAddr.rw = rpl;
      rp.fragSize = sizeof(rpl);
      err = NWRequest(conn, 0x83, 4, rq, 1, &rp);
      if (err)
            return err;
      if (rp.fragSize < 4)
            return 0x897E;    /* buffer mismatch */
      err = DVAL_LH(rpl, 0);
      if (err)
            return err;
      if (rpd) {
            if (rp.fragSize < 24)
                  return 0x897E;
            *rpd = DVAL_LH(rpl, 20);
      }
      return err;
}

NWCCODE NWSMLoadNLM(NWCONN_HANDLE conn, const char* cmd) {
      return NWSMExec(conn, 0x01, NULL, cmd, NULL, NULL);
}

NWCCODE NWSMUnloadNLM(NWCONN_HANDLE conn, const char* cmd) {
      return NWSMExec(conn, 0x02, NULL, cmd, NULL, NULL);
}

NWCCODE NWSMMountVolume(NWCONN_HANDLE conn, const char* vol, nuint32* volnum) {
      return NWSMExec(conn, 0x03, NULL, vol, NULL, volnum);
}

NWCCODE NWSMDismountVolumeByName(NWCONN_HANDLE conn, const char* vol) {
      return NWSMExec(conn, 0x04, NULL, vol, NULL, NULL);
}

NWCCODE NWSMDismountVolumeByNumber(NWCONN_HANDLE conn, nuint32 vol) {
      char volname[NCP_VOLNAME_LEN + 1];
      NWCCODE err;

      err = ncp_get_volume_name(conn, vol, volname, sizeof(volname));
      if (err)
            return err;
      return NWSMDismountVolumeByName(conn, volname);
}

NWCCODE NWSMSetDynamicCmdIntValue(NWCONN_HANDLE conn, const char* set,
            nuint32 value) {
      unsigned char hdr[20];

      memset(hdr, 0, sizeof(hdr));
      DSET_LH(hdr, 0, 1);
      DSET_LH(hdr, 4, value);
      return NWSMExec(conn, 0x06, hdr, set, NULL, NULL);
}

NWCCODE NWSMSetDynamicCmdStrValue(NWCONN_HANDLE conn, const char* set,
            const char* value) {
      return NWSMExec(conn, 0x06, NULL, set, value, NULL);
}

NWCCODE NWSMExecuteNCFFile(NWCONN_HANDLE conn, const char* cmd) {
      return NWSMExec(conn, 0x07, NULL, cmd, NULL, NULL);
}

/* Pre 3.11 NetWare */
static NWCCODE NWClearConnectionNumberV0(NWCONN_HANDLE conn, NWCONN_NUM connNumber) {
      nuint8 connN;

      if (connNumber >= 256)
            return NWE_CONN_NUM_INVALID;
      connN = connNumber;
      return NWRequestSimple(conn, NCPC_SFN(0x17, 0xD2), &connN, 1, NULL);
}

/* Post (including) 3.11 NetWare... */
static NWCCODE NWClearConnectionNumberV1(NWCONN_HANDLE conn, NWCONN_NUM connNumber) {
      nuint8 connN[4];

      DSET_LH(connN, 0, connNumber);
      return NWRequestSimple(conn, NCPC_SFN(0x17, 0xFE), &connN, 4, NULL);
}

NWCCODE NWClearConnectionNumber(NWCONN_HANDLE conn, NWCONN_NUM connNumber) {
      NWCCODE err;

      err = NWClearConnectionNumberV1(conn, connNumber);
      if (err == NWE_NCP_NOT_SUPPORTED) {
            err = NWClearConnectionNumberV0(conn, connNumber);
      }
      return err;
}

/* -------------------------------- */
/* Get object connection list
 * name must not be NULL (otherwise core)
 * name must be shorter than 256 chars (otherwise NWE_SERVER_FAILURE)
 * conns can be NULL
 * numConns can be NULL
 * returned numConns can be greater than maxConns, but connections
   above maxConns are lost
 * that's all? */
static NWCCODE NCPGetObjectConnectionList215(NWCONN_HANDLE conn,
            const char* name, nuint16 type, size_t* numConns,
            NWCONN_NUM* conns, size_t maxConns) {
      nuint8 rq_array[2+1];
      nuint8 rp_array[1+256];
      NW_FRAGMENT rq[2];
      NW_FRAGMENT rp;
      size_t strl;
      NWCCODE err;
      size_t nconns;
            
      strl = strlen(name);
      if (strl > 255)
            return NWE_SERVER_FAILURE;
      WSET_HL(rq_array, 0, type);
      BSET(rq_array, 2, strl);
      rq[0].fragAddr.ro = rq_array;
      rq[0].fragSize = 3;
      rq[1].fragAddr.ro = name;
      rq[1].fragSize = strl;
      rp.fragAddr.rw = rp_array;
      rp.fragSize = sizeof(rp_array);
      err = NWRequest(conn, NCPC_SFN(0x17, 0x15), 2, rq, 1, &rp);
      if (err)
            return err;
      if (rp.fragSize < 1) {
            /* at least one byte must be returned, do not you think? */
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      nconns = BVAL(rp_array, 0);
      if (rp.fragSize <= nconns) {
            /* reply shorter than returned values... */
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      if (maxConns > nconns)
            maxConns = nconns;
      if (conns) {
            size_t i;
            
            for (i = 1; i <= maxConns; i++) {
                  *conns++ = BVAL(rp_array, i);
            }
      }
      if (numConns)
            *numConns = nconns;
      return 0;
}

static NWCCODE NCPGetObjectConnectionList(NWCONN_HANDLE conn, NWCONN_NUM first,
            const char* name, nuint16 type, size_t* numConns,
            NWCONN_NUM* conns, size_t maxConns) {
      nuint8 rq_array[4+2+1];
      nuint8 rp_array[1+1024];
      NW_FRAGMENT rq[2];
      NW_FRAGMENT rp;
      size_t strl;
      NWCCODE err;
      size_t nconns;
            
      strl = strlen(name);
      if (strl > 255)
            return NWE_SERVER_FAILURE;
      DSET_LH(rq_array, 0, first);
      WSET_HL(rq_array, 4, type);
      BSET(rq_array, 6, strl);
      rq[0].fragAddr.ro = rq_array;
      rq[0].fragSize = 7;
      rq[1].fragAddr.ro = name;
      rq[1].fragSize = strl;
      rp.fragAddr.rw = rp_array;
      rp.fragSize = sizeof(rp_array);
      err = NWRequest(conn, NCPC_SFN(0x17, 0x1B), 2, rq, 1, &rp);
      if (err)
            return err;
      if (rp.fragSize < 1) {
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      nconns = BVAL(rp_array, 0);
      if (rp.fragSize < nconns * 4 + 1) {
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      if (maxConns > nconns)
            maxConns = nconns;
      if (conns) {
            size_t i;
            
            for (i = 0; i < maxConns; i++) {
                  *conns++ = DVAL_LH(rp_array, 1 + i * 4);
            }
      }
      if (numConns)
            *numConns = nconns;
      return 0;
}

static NWCCODE NCPGetConnListFromObject(NWCONN_HANDLE conn, NWObjectID objID,
            NWCONN_NUM firstConn, size_t* conns, NWCONN_NUM* connlist) {
      nuint8 rq_array[4+4];
      nuint8 rp_array[2+4*125]; /* 125 is magic NWServer constant... */
                          /* ncphdr+2+4*125 is just below 512... maybe? */
      NW_FRAGMENT rq;
      NW_FRAGMENT rp;
      NWCCODE err;
      size_t nconns;
            
      DSET_HL(rq_array, 0, objID);
      DSET_LH(rq_array, 4, firstConn);
      rq.fragAddr.ro = rq_array;
      rq.fragSize = 8;
      rp.fragAddr.rw = rp_array;
      rp.fragSize = sizeof(rp_array);
      err = NWRequest(conn, NCPC_SFN(0x17, 0x1F), 1, &rq, 1, &rp);
      if (err)
            return err;
      if (rp.fragSize < 2) {
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      nconns = WVAL_LH(rp_array, 0);
      if (rp.fragSize < nconns * 4 + 2) {
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      if (nconns > 125)
            nconns = 125; /* ugh... Houston, we have a problem... */
      if (connlist) {
            size_t i;
            
            for (i = 0; i < nconns; i++) {
                  *connlist++ = DVAL_LH(rp_array, 2 + i * 4);
            }
      }
      if (conns)
            *conns = nconns;
      return 0;
}

NWCCODE NWGetObjectConnectionNumbers(NWCONN_HANDLE conn, const char* name,
            nuint16 type, size_t* numConns, NWCONN_NUM* conns,
            size_t maxConns) {
      size_t returned;
      size_t complete;
      NWCCODE err;
      
      err = NCPGetObjectConnectionList(conn, 0, name, type, &returned, 
                  conns, maxConns);
      if (err == NWE_NCP_NOT_SUPPORTED) {       
            return NCPGetObjectConnectionList215(conn, name, type, 
                  numConns, conns, maxConns);
      }
      if (err)
            return err;
      complete = 0;
      /* TODO: do it better... */
      while (!err && (returned == 255) && (maxConns > 255) && conns) {
            conns += returned;
            maxConns -= returned;
            complete += returned;
            returned = 0;
            err = NCPGetObjectConnectionList(conn, conns[-1], name,
                  type, &returned, conns, maxConns);
      }
      if (numConns)
            *numConns = complete + returned;
      return 0;   
}

NWCCODE NWGetConnListFromObject(NWCONN_HANDLE conn, NWObjectID objID,
            NWCONN_NUM firstConn, size_t* conns, NWCONN_NUM* connlist) {
      char name[NCP_BINDERY_NAME_LEN+1];
      nuint16 type;
      NWCCODE err;
      
      err = NCPGetConnListFromObject(conn, objID, firstConn, conns, connlist);
      if (err != NWE_NCP_NOT_SUPPORTED)
            return err;
      err = NWGetObjectName(conn, objID, name, &type);
      if (err)
            return err;
      if (firstConn) {
            if (conns) *conns = 0;
      } else
            err = NWGetObjectConnectionNumbers(conn, name, type, conns, connlist, 125);
      return err;
}

/* caller must not modify connArray during run ! */
static NWCCODE NCPSendBroadcastMessageOld(NWCONN_HANDLE conn, const char* message,
            size_t conns, NWCONN_NUM* connArray, nuint8* deliveryStatus) {
      nuint8 rq_array[1+255+1];
      nuint8 rp_array[1+255];
      size_t strl;
      NWCCODE err;
      size_t nconns;
      size_t rconns;
      size_t cnt;
      NWCONN_NUM* tmp;
      
      strl = strlen(message);
      if (strl > 58)
            strl = 58;
      if (conns > 255)
            return NWE_SERVER_FAILURE;
      if (!conns)
            return 0;
      nconns = 1;
      tmp = connArray;
      for (cnt = conns; cnt; cnt--) {
            NWCONN_NUM r = *tmp++;
            if (r < 256)
                  BSET(rq_array, nconns++, r);
      }
      BSET(rq_array, 0, nconns-1);
      BSET(rq_array, nconns, strl);

      if (nconns != 1) {
            NW_FRAGMENT rq[2];
            NW_FRAGMENT rp;
            
            /* something to do... */
            rq[0].fragAddr.ro = rq_array;
            rq[0].fragSize = nconns + 1;
            rq[1].fragAddr.ro = message;
            rq[1].fragSize = strl;
            rp.fragAddr.rw = rp_array;
            rp.fragSize = sizeof(rp_array);
            err = NWRequest(conn, NCPC_SFN(0x15, 0x00), 2, rq, 1, &rp);
            if (err)
                  return err;
            if (rp.fragSize < 1) {
                  return NWE_INVALID_NCP_PACKET_LENGTH;
            }
            rconns = BVAL(rp_array, 0);
            if (rp.fragSize < rconns + 1) {
                  return NWE_INVALID_NCP_PACKET_LENGTH;
            }
            if (rconns != nconns)
                  return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      if (deliveryStatus) {
            size_t r = 1;
            for (; conns--;) {
                  if (*connArray++ < 256)
                        *deliveryStatus++ = BVAL(rp_array, r++);
                  else
                        *deliveryStatus++ = 0x01;
            }
      }
      return 0;
}

static NWCCODE NCPSendBroadcastMessage(NWCONN_HANDLE conn, const char* message,
            size_t conns, NWCONN_NUM* connArray, nuint8* deliveryStatus) {
#define __MAX_PER_REQUEST 512
      nuint8 rq_array[2+4*__MAX_PER_REQUEST+1];
      nuint8 rp_array[2+4*__MAX_PER_REQUEST];
      NW_FRAGMENT rq[2];
      NW_FRAGMENT rp;
      size_t strl;
      NWCCODE err;
      size_t nconns;
      size_t cnt;
            
      strl = strlen(message);
      if (strl > 255)
            strl = 255;
      if (conns > __MAX_PER_REQUEST)
            return NWE_SERVER_FAILURE;
      if (!conns)
            return 0;
      WSET_LH(rq_array, 0, conns);
      nconns = 2;
      for (cnt = conns; cnt; cnt--) {
            DSET_LH(rq_array, nconns, *connArray++);
            nconns += 4;
      }
      BSET(rq_array, nconns, strl);
      rq[0].fragAddr.ro = rq_array;
      rq[0].fragSize = nconns + 1;
      rq[1].fragAddr.ro = message;
      rq[1].fragSize = strl;
      rp.fragAddr.rw = rp_array;
      rp.fragSize = sizeof(rp_array);
      err = NWRequest(conn, NCPC_SFN(0x15, 0x0A), 2, rq, 1, &rp);
      if (err)
            return err;
      if (rp.fragSize < 2) {
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      nconns = WVAL_LH(rp_array, 0);
      if (rp.fragSize < nconns * 4 + 2) {
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      if (nconns != conns)
            return NWE_INVALID_NCP_PACKET_LENGTH;
      if (deliveryStatus) {
            size_t i;
            
            for (i = 0; i < nconns; i++) {
                  *deliveryStatus++ = DVAL_LH(rp_array, 2 + i * 4);
            }
      }
      return 0;
#undef __MAX_PER_REQUEST      
}

NWCCODE NWSendBroadcastMessage(NWCONN_HANDLE conn, const char* message,
            size_t conns, NWCONN_NUM* connArray, nuint8* deliveryStatus) {
      NWCCODE err;
      
      err = NCPSendBroadcastMessage(conn, message, conns, connArray,
            deliveryStatus);
      if (err != NWE_NCP_NOT_SUPPORTED)
            return err;
      return NCPSendBroadcastMessageOld(conn, message, conns, connArray,
            deliveryStatus);
}

NWCCODE __NWReadFileServerInfo(NWCONN_HANDLE conn) {
      NWCCODE err;
      struct ncp_file_server_info_2 target;

      if (conn->serverInfo.valid)
            return 0;
      err = ncp_get_file_server_information_2(conn, &target, sizeof(target));
      if (err) {
            return err;
      }
      ncpt_mutex_lock(&conn->serverInfo.mutex);
      if (!conn->serverInfo.valid) {
            char* sn;
            conn->serverInfo.version.major = target.FileServiceVersion;
            conn->serverInfo.version.minor = target.FileServiceSubVersion;
            conn->serverInfo.version.revision = target.Revision;
            conn->serverInfo.ncp64bit = target._64BitOffsetsSupportedFlag != 0;
            sn = strdup(target.ServerName);
            if (sn) {
                  if (conn->serverInfo.serverName)
                        free(conn->serverInfo.serverName);
                  conn->serverInfo.serverName = sn;
            } else
                  err = ENOMEM;
            if (!err)
                  conn->serverInfo.valid = 1;
      }
      ncpt_mutex_unlock(&conn->serverInfo.mutex);
      return err;
}

static NWCCODE NWGetFileServerNameInt(NWCONN_HANDLE conn, size_t maxlen, char* name) {
      NWCCODE err;

      err = __NWReadFileServerInfo(conn);
      if (err)
            return err;
      if (name) {
            size_t len = strlen(conn->serverInfo.serverName) + 1;
            if (len > maxlen)
                  return NWE_BUFFER_OVERFLOW;
            memcpy(name, conn->serverInfo.serverName, len);
      }
      return 0;   
}

NWCCODE NWGetFileServerName(NWCONN_HANDLE conn, char* name) {
      return NWGetFileServerNameInt(conn, NCP_BINDERY_NAME_LEN+1, name);
}

NWCCODE NWGetFileServerVersion(NWCONN_HANDLE conn, u_int16_t* version) {
      NWCCODE err;

      err = __NWReadFileServerInfo(conn);
      if (err)
            return err;
      if (version) {
            *version = (conn->serverInfo.version.major << 8) 
                   | conn->serverInfo.version.minor;
      }
      return 0;
}

NWCCODE NWGetConnectionNumber(NWCONN_HANDLE conn, NWCONN_NUM* number) {
      int err = ncp_get_conn_number(conn);
      if (err < 0)
            return NWE_REQUESTER_FAILURE;
      *number = err;
      return 0;
}

NWCCODE ncp_put_req_size_unsigned(void* buffer, size_t reqlen, unsigned /* long */ long val) {
      switch (reqlen) {
            case 1:     if (val & ~0xFF)
                        return E2BIG;
                  *(u_int8_t*)buffer = val;
                  return 0;
            case 2:     if (val & ~0xFFFF)
                        return E2BIG;
                  *(u_int16_t*)buffer = val;
                  return 0;
            case 4:     /* if (val & ~0xFFFFFFFFUL)
                        return E2BIG;
                  */
                  *(u_int32_t*)buffer = val;
                  return 0;
      }
      return NWE_PARAM_INVALID;
}

/* should be in ndslib.h ? */
#define NWE_BIND_NO_SUCH_PROP NWE_NCP_NOT_SUPPORTED

NWCCODE NWCCGetConnInfo(NWCONN_HANDLE conn, nuint info, size_t len,
            void* buffer) {
      if (!buffer) /* just in case */
            return ERR_NULL_POINTER;

      switch (info) {
            case NWCC_INFO_CONN_NUMBER:
                  if (len == sizeof(NWCONN_NUM))
                        return NWGetConnectionNumber(conn, (NWCONN_NUM*)buffer);
                  {
                        NWCONN_NUM cn;
                        NWCCODE err;
                        
                        err = NWGetConnectionNumber(conn, &cn);
                        if (err)
                              return err;
                        return ncp_put_req_size_unsigned(buffer, len, cn);
                  }
            case NWCC_INFO_USER_ID:
                  {
                        if (!conn->user_id_valid) {
                              NWCCODE err;
                              struct ncp_bindery_object obj;
                              NWCONN_NUM cn;

                              err = NWGetConnectionNumber(conn, &cn);
                              if (err)
                                    return err;
                              err = ncp_get_stations_logged_info(conn, cn, &obj, NULL);
                              if (err)
                                    return err;
                              conn->user_id = obj.object_id;
                              conn->user_id_valid = 1;
                        }
                        if (len != sizeof(NWObjectID))
                              return NWE_BUFFER_OVERFLOW;
                        *(NWObjectID*)buffer = conn->user_id;
                        return 0;
                  }
            case NWCC_INFO_SERVER_NAME:
                  return NWGetFileServerNameInt(conn, len, buffer);
            case NWCC_INFO_MAX_PACKET_SIZE:
                  return ncp_put_req_size_unsigned(buffer, len, conn->i.buffer_size);
            case NWCC_INFO_SERVER_VERSION:
                  {
                        NWCCODE err;

                        err = __NWReadFileServerInfo(conn);
                        if (err)
                              return err;
                        if (len != sizeof(NWCCVersion))
                              return NWE_BUFFER_OVERFLOW;
                        ((NWCCVersion*)buffer)->major = conn->serverInfo.version.major;
                        ((NWCCVersion*)buffer)->minor = conn->serverInfo.version.minor;
                        ((NWCCVersion*)buffer)->revision = conn->serverInfo.version.revision;
#if 0
                        printf ("NWCC_INFO_SERVER_VERSION %d %d %d %d %s\n", conn->serverInfo.valid,
                              conn->serverInfo.version.major, conn->serverInfo.version.minor,
                              conn->serverInfo.version.revision, conn->serverInfo.serverName);
#endif

                  }
                  return 0;
            case NWCC_INFO_TRAN_ADDR:
                  {
                        NWCCTranAddr* p = buffer;
                        if (len < sizeof(NWCCTranAddr))
                              return NWE_BUFFER_OVERFLOW;
                        switch (conn->addr.any.sa_family) {
#ifdef CONFIG_NATIVE_IP
                              case AF_INET:
                                    if (p->len < 6)
                                          return NWE_BUFFER_OVERFLOW;
                                    p->len = 6;
                                    p->type = NT_UDP;
                                    memcpy(((char*)(p->buffer))+2, &conn->addr.inet.sin_addr.s_addr, 4);
                                    memcpy(p->buffer, &conn->addr.inet.sin_port, 2);
                                    break;
#endif
#ifdef CONFIG_NATIVE_IPX
                              case AF_IPX:
                                    if (p->len < 12)
                                          return NWE_BUFFER_OVERFLOW;
                                    p->len = 12;
                                    p->type = NT_IPX;
                                    memcpy(p->buffer, &conn->addr.ipx.sipx_network, 4);
                                    memcpy(((char*)(p->buffer))+4, conn->addr.ipx.sipx_node, 6);
                                    memcpy(((char*)(p->buffer))+10, &conn->addr.ipx.sipx_port, 2);
                                    break;
#endif
                              default:
                                    return NWE_REQUESTER_FAILURE;
                        }                       
                  }
                  return 0;
            case NWCC_INFO_USER_NAME:
                  {
                        const char* user = "";
                        size_t usrlen;
                        NWCCODE err;
                        struct ncp_bindery_object obj;

                        if (!conn->user) {
                              err = ncp_get_stations_logged_info(conn, conn->i.connection, &obj, NULL);
                              if (!err)
                                    conn->user = strdup(obj.object_name);
                        }
                        if (conn->user)
                              user = conn->user;
                        usrlen = strlen(user) + 1;
                        if (usrlen > len)
                              return NWE_BUFFER_OVERFLOW;
                        memcpy(buffer, user, usrlen);
                  }
                  return 0;
            case NWCC_INFO_MOUNT_UID:
                  {
                        uid_t tmpuid;
                        NWCCODE err;

                        if (ncp_get_conn_type(conn) != NCP_CONN_PERMANENT)
                              return NWE_REQUESTER_FAILURE;
                        if (len == sizeof(uid_t))
                              return ncp_get_mount_uid(conn->mount_fid,
                                    buffer);
                        err = ncp_get_mount_uid(conn->mount_fid,
                              &tmpuid);
                        if (err)
                              return err;
                        return ncp_put_req_size_unsigned(buffer, len, tmpuid);
                  }
            case NWCC_INFO_SECURITY:
                  {
                        unsigned int val = 0;
                        
                        if (ncp_get_sign_wanted(conn))
                              val |= NWCC_SECUR_LEVEL_SIGN_HEADERS;
                        if (ncp_get_sign_active(conn))
                              val |= NWCC_SECUR_SIGNING_IN_USE;
                        return ncp_put_req_size_unsigned(buffer, len, val);
                  }
            case NWCC_INFO_BCAST_STATE:
                  {
                        unsigned char rpb[4];
                        NW_FRAGMENT rp;
                        size_t tln;
                        NWCCODE err;

                        rp.fragAddr.rw = rpb;
                        rp.fragSize = sizeof(rpb);
                        
                        err = __NWExecuteGlobal(conn, NCPI_OP_GETBCASTSTATE, 0, NULL, 1, &rp, &tln);
                        if (err != NCPLIB_NCPD_DEAD) {
                              if (err)
                                    return err;
                              if (tln < 4)
                                    return NWE_INVALID_NCP_PACKET_LENGTH;
                              return ncp_put_req_size_unsigned(buffer, len, DVAL_LH(rpb, 0));
                        }
                        if (conn->bcast_state == NWCC_BCAST_PERMIT_UNKNOWN)
                              return NWE_REQUESTER_FAILURE;
                        return ncp_put_req_size_unsigned(buffer, len, conn->bcast_state);
                  }
            case NWCC_INFO_TREE_NAME:
#ifdef NDS_SUPPORT
                  {

                        char tn[MAX_TREE_NAME_CHARS+1];
                        size_t treelen;

                        if (!NWIsDSServer(conn, tn))
                              return NWE_BIND_NO_SUCH_PROP;
                        if (buffer) {
                              char* p;
                              p = strchr(tn, '\0') - 1;
                              while ((p >= tn) && (*p == '_'))
                                    p--;
                              treelen = p - tn + 1;
                              tn[treelen] = 0;
                              if (treelen >= len)
                                    return NWE_BUFFER_OVERFLOW;
                              strcpy(buffer,tn);
                        }
                  }
                  return 0;
#else
                  return NWE_BIND_NO_SUCH_PROP;
#endif
            case NWCC_INFO_ROOT_ENTRY:
                  {
                        if (ncp_get_conn_type(conn) != NCP_CONN_PERMANENT)
                              return NWE_REQUESTER_FAILURE;
                        if (len < sizeof(struct NWCCRootEntry))
                              return NWE_BUFFER_OVERFLOW;
                        ((struct NWCCRootEntry*)buffer)->volume = conn->i.volume_number;
                        ((struct NWCCRootEntry*)buffer)->dirEnt = conn->i.directory_id;
                  }
                  return 0;
            case NWCC_INFO_MOUNT_POINT:
                  {
                        size_t need_len;
                        
                        if (ncp_get_conn_type(conn) != NCP_CONN_PERMANENT)
                              return NWE_REQUESTER_FAILURE;
                        if (!conn->mount_point)
                              return NWE_REQUESTER_FAILURE;
                        need_len = strlen(conn->mount_point) + 1;
                        if (len < need_len)
                              return NWE_BUFFER_OVERFLOW;
                        memcpy(buffer, conn->mount_point, need_len);
                  }
                  return 0;
            default:
                  return NWE_INVALID_LEVEL;
      }
}

NWCCODE NWCCOpenConnByAddr(const NWCCTranAddr* addr, nuint openState,
            nuint reserved, NWCONN_HANDLE* conn) {
      nuint32 t;
      union ncp_sockaddr server;
      const nuint8* buffer;
      enum NET_ADDRESS_TYPE addrType;
      
      buffer = addr->buffer;
      if (!buffer)
            return NWE_PARAM_INVALID; /* ERR_NULL_POINTER */
      t = addr->type;
#ifdef CONFIG_NATIVE_IPX
      if (t == NWCC_TRAN_TYPE_IPX_old || t == NT_IPX) {
            if (addr->len < 12)
                  return NWE_BUFFER_OVERFLOW;
            server.ipx.sipx_family = AF_IPX;
            memcpy(&server.ipx.sipx_network, buffer, 4);
            memcpy( server.ipx.sipx_node, buffer+4, 6);
            memcpy(&server.ipx.sipx_port, buffer+4+6, 2);
            server.ipx.sipx_type = 0x11;  /* NCP/IPX */
            addrType = NT_IPX;
      } else 
#endif
#ifdef CONFIG_NATIVE_IP
      if (t == NT_UDP || t == NT_TCP) {
            if (addr->len < 6)
                  return NWE_BUFFER_OVERFLOW;
            server.inet.sin_family = AF_INET;
            memcpy(&server.inet.sin_addr.s_addr, buffer+2, 4);
            memcpy(&server.inet.sin_port, buffer, 2);
            addrType = t;
      } else
#endif
            return NWE_UNSUPPORTED_TRAN_TYPE;
      return NWCCOpenConnBySockAddr(&server.any, addrType, openState,
            reserved, conn);
}

NWCCODE NWCCCloseConn(NWCONN_HANDLE conn) {
      return ncp_close(conn);
}


/******************************************************** PP*/

NWCCODE NWScanVolDiskRestrictions(
            NWCONN_HANDLE     conn,
            NWVOL_NUM   volNum,
            nuint32*    sequence,
            NWVolumeRestrictions * volInfo) {
/* old API ??? by blocks of 12
   picke them up by blocks of 16 by return them by 12 */
      NWVOL_RESTRICTIONS myBuffer16;
      NWCCODE err;

      if (!volInfo)
            return ERR_NULL_POINTER;
      err = NWScanVolDiskRestrictions2(conn, volNum, sequence, &myBuffer16);
      if (err)
            return err;
      if (myBuffer16.numberOfEntries > 12)
            myBuffer16.numberOfEntries = 12;
      volInfo->numberOfEntries = myBuffer16.numberOfEntries;
      if (myBuffer16.numberOfEntries) 
            memcpy(volInfo->resInfo, myBuffer16.resInfo, myBuffer16.numberOfEntries * sizeof(NWOBJ_REST));
      return 0;
}

NWCCODE NWScanVolDiskRestrictions2(
            NWCONN_HANDLE     conn,
            NWVOL_NUM   volNum,
            nuint32*    sequence,
            NWVOL_RESTRICTIONS * volInfo) {
/* by blocks of 16 */
      nuint8 rq[1+4];
      nuint8 rp_b[sizeof (NWVOL_RESTRICTIONS)];
      NW_FRAGMENT rp;
      NWCCODE err;
      unsigned int tmp;
      unsigned int i;

      if (!sequence || !volInfo)
            return ERR_NULL_POINTER;

      rp.fragAddr.rw = rp_b;
      rp.fragSize = sizeof(rp_b);

      BSET(rq,0,volNum);
      DSET_LH(rq,1,*sequence);

      err = NWRequestSimple(conn, NCPC_SFN(22,32), &rq, 5, &rp);
      if (err)
            return err;
      if (rp.fragSize < 1)
            return NWE_INVALID_NCP_PACKET_LENGTH;
      tmp = BVAL(rp_b,0);
      /* tmp =0 means no more entries, done */
      if (tmp > 16)
            return NWE_INVALID_NCP_PACKET_LENGTH; /* or a better code ??? */
      if (rp.fragSize < 1+tmp*8)
            return NWE_INVALID_NCP_PACKET_LENGTH;

      volInfo->numberOfEntries = tmp;
      for (i = 0; i < tmp; i++) {
            volInfo->resInfo[i].objectID = DVAL_HL(rp_b, 1+i*8);
            volInfo->resInfo[i].restriction = DVAL_LH(rp_b, 1+i*8+4);
      }
      for (; i < 16; i++) {
            volInfo->resInfo[i].objectID = 0;
            volInfo->resInfo[i].restriction = 0;
      }
      return 0;
}


NWCCODE NWRemoveObjectDiskRestrictions(
            NWCONN_HANDLE     conn,
            NWVOL_NUM   volNum,
            NWObjectID  objectID) {
      nuint8 rq[5];

      BSET(rq, 0, volNum);
      DSET_HL(rq, 1, objectID);
      return NWRequestSimple(conn, NCPC_SFN(22,34), &rq, 5, NULL);
}

NWCCODE NWSetObjectVolSpaceLimit(
            NWCONN_HANDLE     conn,
            NWVOL_NUM   volNum,
            NWObjectID  objectID,
            nuint32           restriction) {
      nuint8 rq[1+4+4];

      BSET(rq,0,volNum);
      DSET_HL(rq,1,objectID);
      DSET_LH(rq,5,restriction);
      return NWRequestSimple(conn, NCPC_SFN(22,33), &rq, 1+4+4, NULL);
}

NWCCODE NWGetObjDiskRestrictions(
            NWCONN_HANDLE     conn,
            NWVOL_NUM   volNum,
            NWObjectID  objectID,
            nuint32*    restriction,
            nuint32*    inUse) {
      nuint8 rq[1+4];
      nuint8 rp_b[4+4];
      NW_FRAGMENT rp;
      NWCCODE err;

      rp.fragAddr.rw = rp_b;
      rp.fragSize = sizeof(rp_b);

      BSET(rq, 0, volNum);
      DSET_HL(rq, 1, objectID);

      err = NWRequestSimple(conn, NCPC_SFN(22,41), &rq, 5, &rp);
      if (err)
            return err;
      if (rp.fragSize < 8)
            return NWE_INVALID_NCP_PACKET_LENGTH;
      if (restriction)
            *restriction = DVAL_LH(rp_b, 0);
      if (inUse)
            *inUse = DVAL_LH(rp_b, 4);
      return 0;
}

/*****************************************************************************/
NWCCODE __NWEnableBroadcasts(NWCONN_HANDLE conn) {
      return NWRequestSimple(conn, NCPC_SFN(21,3), NULL, 0, NULL);
}

NWCCODE __NWDisableBroadcasts(NWCONN_HANDLE conn) {
      return NWRequestSimple(conn, NCPC_SFN(21,2), NULL, 0, NULL);
}

NWCCODE NWGetBroadcastMode(NWCONN_HANDLE conn, nuint16* mode) {
      return NWCCGetConnInfo(conn, NWCC_INFO_BCAST_STATE, sizeof(*mode), mode);
}

NWCCODE NWSetBroadcastMode(NWCONN_HANDLE conn, nuint16 mode) {
      NWCCODE err;
      nuint8 rqb[4];
      NW_FRAGMENT rq;

      switch (mode) {
            case NWCC_BCAST_PERMIT_ALL:
            case NWCC_BCAST_PERMIT_SYSTEM:
            case NWCC_BCAST_PERMIT_NONE:
            case NWCC_BCAST_PERMIT_POLL:
                  break;
            default:
                  return NWE_PARAM_INVALID;
      }
      DSET_LH(rqb, 0, mode);
      rq.fragAddr.ro = rqb;
      rq.fragSize = sizeof(rqb);
      
      err = __NWExecuteGlobal(conn, NCPI_OP_SETBCASTSTATE, 1, &rq, 0, NULL, NULL);
      if (err != NCPLIB_NCPD_DEAD) {
            return err;
      }
      switch (mode) {
            case NWCC_BCAST_PERMIT_ALL:
                  err = __NWEnableBroadcasts(conn); 
                  break;
            case NWCC_BCAST_PERMIT_SYSTEM:
            case NWCC_BCAST_PERMIT_NONE:
                  err = __NWDisableBroadcasts(conn);
                  break;
            case NWCC_BCAST_PERMIT_POLL:
                  err = __NWDisableBroadcasts(conn);
                  break;
            default:
                  err = NWE_PARAM_INVALID;
      }
      if (err)
            return err;
      conn->bcast_state = mode;
      return 0;
}

NWCCODE NWEnableBroadcasts(NWCONN_HANDLE conn) {
      return NWSetBroadcastMode(conn, NWCC_BCAST_PERMIT_ALL);
}

NWCCODE NWDisableBroadcasts(NWCONN_HANDLE conn) {
      return NWSetBroadcastMode(conn, NWCC_BCAST_PERMIT_NONE);
}

/********************************************************************************/


NWCCODE NWSignalSemaphore(
            NWCONN_HANDLE     conn,
            nuint32           semHandle) {
      NWCCODE err;

      ncp_init_request(conn);
      ncp_add_byte(conn, 3);  /* sub_func */
      ncp_add_dword_lh(conn, semHandle);
      err = ncp_request(conn, 111);
      ncp_unlock_conn(conn);
      return err;
}

NWCCODE NWCloseSemaphore(
            NWCONN_HANDLE     conn,
            nuint32           semHandle) {
      NWCCODE err;

      ncp_init_request(conn);
      ncp_add_byte(conn, 4);  /* sub_func */
      ncp_add_dword_lh(conn, semHandle);
      err = ncp_request(conn, 111);
      /* this NCP returns openCount and shareCount , but no way to return them */
      ncp_unlock_conn(conn);
      return err;
}

NWCCODE NWOpenSemaphore(
            NWCONN_HANDLE     conn,
            const char* semName,
            nint16            initValue,
            nuint32*    semHandle,
            nuint16*    semOpenCount){
      NWCCODE err;
      size_t len;
      nuint8 nameBuf[512];

      if (!semName)
            return ERR_NULL_POINTER;
      if (!semHandle)
            return ERR_NULL_POINTER;

      len = strlen(semName);
      if (len > 255)
            len = 255;

      memset(nameBuf, 0, sizeof(nameBuf));
      memcpy(nameBuf, semName, len);
      ncp_init_request(conn);
      ncp_add_byte(conn, 0);  /* sub func 0 */
      ncp_add_byte(conn, initValue);
      ncp_add_byte(conn, len);
      ncp_add_mem(conn, nameBuf, sizeof(nameBuf));
      /* the string must be exactly 512 bytes
         else server complains about a NCP packet incomplete */
      err = ncp_request(conn, 111);
      if (err) {
            ncp_unlock_conn(conn);
            return err;
      }

      *semHandle = ncp_reply_dword_lh(conn, 0);
      if (semOpenCount)
            *semOpenCount = ncp_reply_byte(conn, 4); /* NCP returns byte not word? */
      ncp_unlock_conn(conn);
      return 0;
}

NWCCODE NWExamineSemaphore(
            NWCONN_HANDLE     conn,
            nuint32           semHandle,
            nint16*           semValue,
            nuint16*    semOpenCount) {
      NWCCODE err;

      ncp_init_request(conn);
      ncp_add_byte(conn, 1);  /* sub_func */
      ncp_add_dword_lh(conn, semHandle);
      err = ncp_request(conn, 111);
      if (!err) {
            if (semValue) {
                  *semValue = ncp_reply_byte(conn, 0); /* NCP returns byte not word? */
            }
            if (semOpenCount){
                  *semOpenCount = ncp_reply_byte(conn, 1); /* NCP returns byte not word? */
            }
      }
      ncp_unlock_conn(conn);
      return err;
}

NWCCODE NWWaitOnSemaphore(
            NWCONN_HANDLE     conn,
            nuint32           semHandle,
            nuint16           timeOutValue) {
      NWCCODE err;

      ncp_init_request(conn);
      ncp_add_byte(conn, 2);  /* sub_func */
      ncp_add_dword_lh(conn, semHandle);
      ncp_add_word_lh(conn, timeOutValue);
      err = ncp_request(conn, 111);
      ncp_unlock_conn(conn);
      return err;
}

NWCCODE NWCancelWait(
            NWCONN_HANDLE     conn) {
      return NWRequestSimple(conn, NCPC_FN(112), NULL, 0, NULL);
}

NWCCODE NWGetVolumeNumber (
            NWCONN_HANDLE     conn,
            const char* volumeName,
            NWVOL_NUM*  volumeNumber) {
      NWCCODE err;
      int v;

      /* nothing fancy but make code portable from MSWIN_PLAT to UNIX_PLAT ;-) */
      if (!volumeName)
            return ERR_NULL_POINTER;
      if (!volumeNumber)
            return ERR_NULL_POINTER;
      err = ncp_get_volume_number(conn, volumeName, &v);
      if (err)
            return err;
      *volumeNumber = v;
      return 0;
}



static NWCCODE __NWCCGetConnAddressInt(   /* internal */
            NWCONN_HANDLE     conn,
            nuint32*    bufLen,     /* return both */
            NWCCTranAddr*     tranAddr) {
      NWCCODE err;
      nuint8 myBuffer[32];
      NWCCTranAddr myTranAddr;

      myTranAddr.buffer = myBuffer;
      myTranAddr.len = sizeof(myBuffer);

      err = NWCCGetConnInfo(conn, NWCC_INFO_TRAN_ADDR, sizeof(myTranAddr), &myTranAddr);
      if (err)
            return err;
      if (bufLen)
            *bufLen = myTranAddr.len;
      if (tranAddr) {
            tranAddr->type = myTranAddr.type;
            tranAddr->len = myTranAddr.len;
            if (tranAddr->buffer)
                  memcpy(tranAddr->buffer, myTranAddr.buffer, myTranAddr.len);
      }
      return 0;
}

NWCCODE NWCCGetConnAddress(
            NWCONN_HANDLE     conn,
            nuint32           bufLen,
            NWCCTranAddr*     tranAddr) {

      NWCCODE err;
      nuint32 aux;

      err = __NWCCGetConnAddressInt(conn, &aux, NULL);
      if (err)
            return err;
      if (aux > bufLen)
            return NWE_BUFFER_OVERFLOW;
      return __NWCCGetConnAddressInt(conn, NULL, tranAddr);
}

NWCCODE NWCCGetConnAddressLength(
            NWCONN_HANDLE     conn,
            nuint32*    bufLen) {
      return __NWCCGetConnAddressInt(conn, bufLen, NULL);
}

static NWCCODE __ncp_semaphore_fetch(CONN_SEMAPHORE *semaphore, CONN_SEMAPHORES *semaphores) {
      size_t nm;
      u_int8_t* base = semaphores->records + semaphores->curOffset;
      u_int8_t* end = semaphores->records + sizeof(semaphores->records);
      

      if (base + 7 > end) {
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      nm = BVAL(base, 7);
      if (base + 7 + nm > end) {
            return NWE_INVALID_NCP_PACKET_LENGTH;
      }
      if (nm >= sizeof(semaphore->semaphoreName)) {
            return NWE_BUFFER_OVERFLOW;
      }
      semaphore->openCount = WVAL_LH(base, 0);
      semaphore->semaphoreValue = WVAL_LH(base, 2);
      semaphore->taskNumber = WVAL_LH(base, 4);
      memcpy(semaphore->semaphoreName, base + 7, nm);
      semaphore->semaphoreName[nm] = 0;
      semaphores->curOffset += 7 + nm;
      semaphores->curRecord++;
      return 0;
}

NWCCODE
NWScanSemaphoresByConn(
            NWCONN_HANDLE     conn,
            NWCONN_NUM  connNum,
            u_int16_t*  iterHandle,
            CONN_SEMAPHORE*   semaphore,
            CONN_SEMAPHORES* semaphores) {
      NWCCODE result;
      u_int16_t ih;
      
      if (!iterHandle || !semaphores) {
            return NWE_PARAM_INVALID;
      }
      ih = *iterHandle;
      if (!ih) {
            semaphores->nextRequest = 0;
            semaphores->numRecords = 0;
            semaphores->curRecord = 0;
      } else if (ih < semaphores->numRecords) {
            if (!semaphore)
                  return NWE_PARAM_INVALID;
            if (semaphores->curRecord != ih)
                  return NWE_PARAM_INVALID;
            result = __ncp_semaphore_fetch(semaphore, semaphores);
            if (result)
                  goto abrt;
            ih = semaphores->curRecord;
            goto finish;
      } else if (semaphores->nextRequest == 0) {
            return NWE_REQUESTER_FAILURE;
      }
      ncp_init_request_s(conn, 241);
      ncp_add_word_lh(conn, connNum);
      ncp_add_word_lh(conn, semaphores->nextRequest);
      result = ncp_request(conn, 23);
      if (result) {
            ncp_unlock_conn(conn);
            goto abrt;
      }
      if (conn->ncp_reply_size < 4) {
            ncp_unlock_conn(conn);
            result = NWE_INVALID_NCP_PACKET_LENGTH;
            goto abrt;
      }
      semaphores->nextRequest = ncp_reply_word_lh(conn, 0);
      semaphores->numRecords = ncp_reply_word_lh(conn, 2);
      if (semaphores->numRecords == 0) {
            ncp_unlock_conn(conn);
            result = NWE_REQUESTER_FAILURE;
            goto abrt;
      }
      {
            size_t tocopy;
            
            tocopy = conn->ncp_reply_size - 4;
            if (tocopy > sizeof(semaphores->records)) {
                  tocopy = sizeof(semaphores->records);
            }
            memcpy(semaphores->records, ncp_reply_data(conn, 4), tocopy);
      }
      ncp_unlock_conn(conn);
      semaphores->curRecord = 0;
      semaphores->curOffset = 0;
      if (semaphores->numRecords && semaphore) {
            result = __ncp_semaphore_fetch(semaphore, semaphores);
            if (result)
                  goto abrt;
            ih = 1;
      } else if (semaphores->numRecords) {
            ih = semaphores->numRecords;
      } else {
            semaphores->nextRequest = 0;
            ih = 0xFFFF;
      }
finish:;
      if (ih >= semaphores->numRecords && semaphores->nextRequest == 0) {
            ih = 0xFFFF;
      }
      *iterHandle = ih;
      return 0;
abrt:;
      semaphores->nextRequest = 0;
      semaphores->numRecords = 0;
      *iterHandle = 0xFFFF;
      return result;
}

NWCCODE
NWIsObjectInSet(NWCONN_HANDLE conn,
            const char* object_name,
            NWObjectType object_type,
            const char* property_name,
            const char* member_name,
            NWObjectType member_type) {
      NWCCODE result;

      ncp_init_request_s(conn, 67);
      ncp_add_word_hl(conn, object_type);
      ncp_add_pstring(conn, object_name);
      ncp_add_pstring(conn, property_name);
      ncp_add_word_hl(conn, member_type);
      ncp_add_pstring(conn, member_name);
      result = ncp_request(conn, 23);
      ncp_unlock_conn(conn);
      return result;
}

Generated by  Doxygen 1.6.0   Back to index